# DIY组件/页面开发
第三方开发者可以开发DIY组件扩展装修功能。
什么是DIY组件?我们把可以添加到自定义页面上的每个小功能叫做组件
这些DIY小组件可以自己使用,也可以开放给其他用户(开发者,最终用户)使用。DIY组件的扩展一般是通过在第三方插件中自定义来实现。一个插件,可以是一个功能完备的业务系统,也可以是小到仅仅包括一个DIY小组件,或者几个DIY页面组件的小插件。

<br>
**开发DIY页面组件步骤**
1. 建立一个自定义插件的目录,位置在niucloud\addon\ 比如叫hello_world, 则最终组件根目录就是niucloud\addon\hello_world
2. 在addon\hello_world\app\dict\diy目录下,建立components.php文件(如果以上目录不存在,则手工创建)




这里有几个相关文件要说明一下
```
components.php         DIY页面组件的字典定义
links.php              超级链接的字典定义
pages.php              DIY页面描述定义,也叫页面数据定义
template.php           DIY模版定义
```
打开components.php文件,进行编辑
```
return [
    'BASIC' => [
        'title' => get_lang('dict_diy.component_type_basic'),
        'list' => [
            'HelloText' => [
                'title' => 'hello文本',
                'icon' => 'iconfont-iconhuiyuanzhongxin',
                'path' => 'edit-hello-text',
                'support_page' => [],
                'uses' => 0,
                'sort' => 10007,
                'value' => [
                    'height' => 20
                ],
            ],
        ],
    ],
];
```
结构说明:
BASIC为该组件需要放置的分类。BASIC默认为系统标准分类。用户可以自定义分类,这个分类会在DIY页面时,自动出现分类自己定义的分类名称。
title节表示自己定义的组件希望放置的分类的名称,下面的代码是通过语言包来加载的(参考语言包的使用方法),开发者也可以直接书写为'title' => '我写的DIY组件'
~~~
'title' => get_lang('dict_diy.component_type_basics') 
~~~
list节是一个数组结构。可以同时包括多个DIY组件。
这里HelloText就是我写的组件的名字。
title 组件标题
icon  组件图标
path  组件路径(第5步骤文件的位置)
support_page 表示该组件可以在哪些DIY页面模板出现并使用,如果是空表示任何自定义页面都可以加载并使用该组件
uses 组件在页面中添加的个数。当=0,页面可以添加任意个该组件。当=1,页面只能添加一个组件
sort 组件的排序号
**value** 定义组件的字段和参数
3. 您可能在组件开发时,希望添加一些超链接到系统中,供别的地方调用该组件的相关功能,则需要在addon\hello_world\app\dict\diy目录下,建立links.php文件(如果以上目录不存在,则手工创建)。如果您开发组件时,不需要这一项,则这个步骤可以省略。
编辑文件内容
```
return [
    'SYSTEM_LINK' => [
        'title' => get_lang('dict_diy.system_link'),
        'child_list' => [
            [
                'name' => 'HELLO_WORLD_INFO',    //页面名称
                'title' => get_lang('dict_diy.hello_world_info'),   //页面标题
                'url' => '/pages/hello_world/info',         //页面的view地址
                'is_share' => 1   //是否可以分享
            ],
        ]
    ],
    'HELLO_WORLD_LINK' => [
        'title' => get_lang('dict_diy.hello_world_link'),
        'child_list' => [
            [
                'name' => 'HELLO_WORLD_INDEX',
                'title' => get_lang('dict_diy.hello_world_index'),
                'url' => '/pages/hello_world/index',
                'is_share' => 1
            ],
        ]
    ],
];
```
这样,niucloud-admin框架安装本插件时,会自动加载您定义好的超级链接。别的组件有需要引用超级链接的地方,会自动出现您写入的超级链接。
4.  您可能在组件开发时,希望添加一些自定义页面模板到系统中,则需要在addon\hello_world\app\dict\diy目录下,建立template.php文件(如果以上目录不存在,则手工创建)。如果您开发组件时,不需要这一项,则这个步骤可以省略。对于实际的一个功能,正常来说,大部分页面是固定页面的开发,个别页面做成自定义开放给使用者配置。也可以一个系统完全是固定页面组成。或者同一个页面可以同时支持自定义配置,也包括固定页面,供使用者选择使用。niucloud-admin框架全部支持。
编辑文件内容
```
return [
    'DIY_HELLO_WORLD_INDEX' => [
        'title' => get_lang('dict_diy.page_hello_world_index'),
        'page' => 'pages/hello_world/index',
    ],
    'DIY_HELLO_WORLD_INFO' => [
        'title' => get_lang('dict_diy.page_hello_world_info'),
        'page' => 'pages/hello_world/info',
    ],
];
```
这样,niucloud-admin框架安装本插件时,会自动加载您定义好的自定义页面模板。
5.  您可能在组件开发时,希望添加一些自定义页面数据到系统中,则需要在addon\myctl\app\dict\diy目录下,建立pages.php文件(如果以上目录不存在,则手工创建)。如果您开发组件时,不需要这一项,则这个步骤可以省略。
编辑文件内容
```
return [
    'DIY_INDEX' => [
        'hello_world_index' => [ // 页面标识
            "title" => "hello world 首页", // 页面名称
            'cover' => '', // 页面封面图
            'preview' => '', // 页面预览图
            'desc' => '', // 页面描述
            'mode' => 'diy', // 页面模式:diy:自定义,fixed:固定
            // 页面数据源
            "data" => [
                "global" => [
                    "title" => "hello world首页页面",
                    "pageBgColor" => "#F8F8F8",
                    'bgUrl' => '',
                    'imgWidth' => '',
                    'imgHeight' => '',
                    "bottomTabBarSwitch" => true,
                    "template" => [
                        'textColor' => "#303133",
                        "pageBgColor" => "",
                        "componentBgColor" => "",
                        "topRounded" => 0,
                        "bottomRounded" => 0,
                        "elementBgColor" => "",
                        "topElementRounded" => 0,
                        "bottomElementRounded" => 0,
                        "margin" => [
                            "top" => 0,
                            "bottom" => 0,
                            "both" => 0
                        ]
                    ],
                    'topStatusBar' => [
                        'bgColor' => "#ffffff",
                        'isTransparent' => false,
                        'isShow' => true,
                        'style' => 'style-1',
                        'textColor' => "#333333",
                        'textAlign' => 'center',
                    ],
                    'popWindow' => [
                        'imgUrl' => "",
                        'imgWidth' => '',
                        'imgHeight' => '',
                        'count' => -1,
                        'show' => 0,
                        'link' => [
                            'name' => ""
                        ],
                    ]
                ],
                "value" => [
                    [
                        "path" => "edit-image-ads",
                        "id" => "4640ld4k1pu0",
                        "componentName" => "ImageAds",
                        "componentTitle" => "图片广告",
                        "uses" => 0,
                        "list" => [
                            [
                                "link" => [
                                    "name" => ""
                                ],
                                "imageUrl" => "static/resource/images/diy/banner.png",
                                "imgWidth" => 750,
                                "imgHeight" => 320,
                                "id" => "2xuytp7622w0"
                            ]
                        ],
                        "ignore" => [],
                        "pageBgColor" => "",
                        "componentBgColor" => "",
                        "topRounded" => 0,
                        "bottomRounded" => 0,
                        "elementBgColor" => "",
                        "topElementRounded" => 0,
                        "bottomElementRounded" => 0,
                        "margin" => [
                            "top" => 0,
                            "bottom" => 0,
                            "both" => 0
                        ]
                    ]
                ]
            ]
        ]
    ]
];
```
这样,niucloud-admin框架安装本插件时,会自动加载您定义好的自定义页面数据。
通过上面的操作,这个后端的组件开发配置就都准备好了。如果这个时候把插件安装,就会发现在编辑页面时,您开发的组件已经显示在组件列表中了。只是没有任何功能。
6.开发组件后台设置功能
在插件的目录addon\hello_world\admin\src\views\diy\components\,建立edit-hello-text.vue文件。
编辑该文件
```
<template>
   <!-- 内容 -->
   <div class="content-wrap" v-show="diyStore.editTab == 'content'">
      <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">文本设置</h3>
         <el-form label-width="80px" class="px-[10px]">
            <el-form-item :label="t('blankHeight')">
               <el-slider v-model="diyStore.editComponent.height" show-input size="small" class="ml-[10px] horz-blank-slider"/>
            </el-form-item>
         </el-form>
      </div>
   </div>
   <!-- 样式 -->
   <div class="style-wrap" v-show="diyStore.editTab == 'style'">
      <slot name="style"></slot>
   </div>
</template>
<script lang="ts" setup>
    import {t} from '@/lang'
    import useDiyStore from '@/stores/modules/diy'
    const diyStore = useDiyStore()
    diyStore.editComponent.ignore = []; // 忽略公共属性
    defineExpose({})
</script>
<style lang="scss">
   .horz-blank-slider {
      .el-slider__input {
         width: 100px;
      }
   }
</style>
<style lang="scss" scoped></style>
```
 
7. 接下来就是开发具体的组件的前台功能 。组件的代码文件放置在addon\uni-app\components\diy\
下面的代码为具体的组件的功能代码
```
<template>
   <view :style="warpCss">
      演示插件文本——自定义组件
   </view>
</template>
<script setup lang="ts">
   import { computed } from 'vue';
   import useDiyStore from '@/stores/diy';
   const props = defineProps(['component', 'index']);
   const diyStore = useDiyStore();
   const diyComponent = computed(() => {
      if (diyStore.mode == 'decorate') {
         return diyStore.value[props.index];
      } else {
         return props.component;
      }
   })
    const warpCss = computed(() => {
        var style = '';
        style += 'background-color:' + diyComponent.value.componentBgColor + ';';
        style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
        style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
        style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
        style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
        return style;
    })
</script>
<style></style>
```
到此,一个自定义的组件就开发完了。
<br>
*****
<br>
接下来讲解一下package目录下面几个文件的功能和作用 
<br>
 <br>
**`admin-package.json`**   作用是插件配置的需要,在niucloud-admin框架基础上增加的扩展,插件安装时会合并到系统admin下面的package.json,然后系统执行npm install自动安装
**`composer.json`**  作用是在系统框架composer基础上增加的依赖,插件安装时会合并到系统的服务端niucloud\composer.json,然后系统执行composer update命令自动安装
**`uni-app-package.json`** 作用和uni-app端的 uni-app\package.json一样。表示插件开发中前端uni-app增加的扩展,插件安装时会合并到系统uni-app前端,然后系统执行npm install自动安装
**`uni-app-pages.json`**  插件开发中,uni-app前端定义的所有的页面模版。插件安装时,会自动复制到uni-app\pages路径下。niucloud-admin框架的设计,没有自动扫描文件,自动复制,这样设计的意义是,有些页面,需要条件编译,有些页面,需要编译后做一些特殊的处理,所以没有设计为自动扫描复制的机制。切记!没有写入这个配置的页面,会在安装时导致文件缺失,一定要把页面全部写入这个文件中
**`web-package.json`** 作用和web端的 web\package.json一样。表示插件开发中前端web增加的扩展,插件安装时会合并到系统web前端,然后系统执行npm install自动安装
                    
        - 源码下载
 - 安装部署
 - 环境要求
 - 安装视频教程
 - 宝塔部署
 - 授权绑定
 - 二次开发须知
 - 技术栈
 - 目录结构
 - 命名规范
 - 二次开发指导
 - 二次开发环境搭建步骤
 - 二次开发注意事项
 - 单站和Saas模式开发
 - 插件结构描述
 - MENU开发
 - DIY组件/页面开发
 - 自定义手机端DIY装修页面
 - Route 路由处理
 - Services中的core文件夹
 - 自定义站点管理端控制台页面样式
 - 调用素材资源
 - Resource资源文件
 - 引入图标ICON
 - SMS发送短信开发
 - Job 消息队列和计划任务
 - 消息队列
 - 计划任务开发
 - 生产环境编译打包处理步骤
 - 生产环境搭建步骤
 - 插件安装时npm,composer检测不可用问题处理
 - 上传图片大小限制修改
 - 插件uniapp开发
 - 菜单语言包
 - 插件打包
 - 插件内测在线升级
 - 自定义端口开发
 - 如何开发自定义布局后台布局
 - 二次开发应用插件视频教程
 - 二次开发安装视频教程
 - 准备工作与创建插件
 - 插件目录整体说明
 - 插件admin目录
 - 插件app目录说明(adminapi、api、验证器)
 - 插件app目录(dict、job)
 - 插件app目录说明(lang、listener)
 - 插件app目录说明(model、service)
 - 插件uniapp目录说明
 - 插件开发之后台功能开发(代码生成器)
 - 插件开发之uniapp功能开发(api)第一节
 - 插件开发之uniapp功能开发(api)第二节
 - 插件开发之uniapp功能开发(api)第三节
 - 插件安装与打包原理
 - 消息队列开发
 - 计划任务开发
 - DIY组件和自定义页面装修开发
 - 支付接口开发
 - 插件升级包打包流程以及云编译功能
 - 常见问题
 - 怎么添加菜单,添加了菜单不出现怎么回事
 - 站点site端(租户端、商家端)和saas管理端(平台端)究竟啥意思,有啥区别
 - 框架中是有订单表order,假如开发一个商城插件,请问商城的订单数据是不是重新搞一个订单表shop_order
 - 有些支付平台是绑定回调唯一网址或目录,如果有几个开发者开发插件都有支付那这块怎么解决?
 - 站点过期,可以登录,这样对吗?
 - 计划任务怎么启动啊
 - Git多分支开发,切换分支
 - 安装应用出现is_dir():报错处理方案
 - 绑定授权时出现“请求来源产品与授权产品不一致”解决方案
 - “未找到admin源码所在目录”的解决方案
 - 页面装修本地开发环境配置
 - 接口请求错误处理方案
 - 未获取到授权信息问题处理方案
 - 腾讯地图配置
 - 请求超时问题处理方案
 - 下载应用时提示找不到zip解决方案
 - 怎么关闭开发调试模式
 - 获取数据失败问题处理方案
 - 框架安装后,访问域名无法进入admin端(多数发生在本地)
 - 底部导航失效问题
 - 开放平台小程序审核通过发布失败问题
 - 先升级插件后升级框架,导致云编译报debounce的错误
 - 微信公众号自动回复不通
 - 修改访问域名默认跳转端口
 - 插件与框架的兼容问题处理
 - 升级提示mkdir()处理方案
 - 云编译时node.js内存不足导致内存溢出处理方案
 - 报错Allowed memory size of ** bytes exhausted (tried to allocate ** bytes)处理方法
 - 安装插件提示不适配框架版本的处理
 - 代码生成器
 - 设计数据表
 - 导入数据表
 - 添加字段
 - 代码生成
 - 效果预览
 - niucloud (服务端)
 - 服务端目录结构
 - 整体运行流程
 - 入口文件
 - 模块
 - 中间件
 - 控制器
 - 路由
 - 模型类
 - 服务类
 - 验证类
 - 消息队列
 - 开发消息队列
 - 多语言
 - 装载器
 - 短信发送
 - 上传文件
 - 第三方支付
 - 第三方登录
 - 数据字典装载器
 - 打印
 - 事件
 - 消息提醒(模版消息)
 - 数据字典
 - admin (后台管理端)
 - 准备工作
 - 新手入门
 - 目录结构
 - 系统配置
 - 路由
 - 接口
 - 管理端编译上传
 - uni-app(手机端前端)
 - 准备工作
 - 前端目录结构
 - Hbuilder开发
 - 发行h5
 - 运行uniapp
 - 发行uniapp
 - Visual Studio Code开发
 - 发行 uniapp
 - 运行 uniapp
 - 配置项
 - 路由
 - 接口
 - 分包建议
 - 手机端编译
 - 微信小程序编译上传
 - web端(PC前端)
 - 准备工作
 - 目录结构
 - 代码运行
 - 路由
 - 接口
 - 黑暗主题
 - web端打包上线
 - api接口
 - 配置手册
 - 阿里云oss云存储配置
 - 三方开放平台小程序托管
 - 计划任务配置
 - 站点域名配置
 - 小票打印配置
 - 电子面单配置
 - 微信小程序客服配置
 - 微信打款配置
 
