开发指南概述第三方开发者可以通过开发 DIY 组件来扩展系统的页面装修功能。DIY 组件是指可以添加到自定义页面上的独立功能模块这些组件既可以供开发者自己使用也可以开放给其他用户包括其他开发者和最终用户使用极大地增强了系统的扩展性和灵活性功能介绍框架支持自定义页面装修用户可以通过「自由可视化拖拽组件配置实时预览」的方式无需代码开发即可自定义生成页面如商城首页、活动页、个人中心页等同时满足开发者扩展自定义组件的需求兼顾易用性于灵活性适配多终端H5/微信小程序展示场景装修界面采用三栏式布局左侧展示组件库供用户选择中间区域提供实时预览功能右侧面板用于编辑和配置所选组件的各项属性。框架开发的自定义页面装修自定义页面装修核心代码如需了解自定义页面装修的核心原理开发者可以自行阅读文件位置admin\src\app\views\diy\edit.vue该文件实现了整个装修界面的交互逻辑、组件管理和实时预览功能页面装修 store 状态管理代码装修过程中的状态管理通过 Store 模式实现文件位置admin\src\stores\modules\diy.ts装修数据最终存储到数据表中的结构主要包含两大部分globalglobal 对象为整体页面的数据结构后续还会持续完善实现更加灵活的自定义装修valuevalue 为组件集合在装修时添加自定义组件会变化uni-app 手机端渲染自定义组件 diy-group 组件在移动端uni-app中自定义页面通过 diy-group 组件进行渲染。该组件负责解析和展示装修数据中的所有组件。文件位置uni-app/src/addon/components/diy/group/自定义组件渲染 store 状态管理代码该文件负责处理移动端组件的数据获取、解析和渲染逻辑文件位置uni-app/src/app/stores/diy.ts配置本地开发环境概述由于自定义页面装修的效果是实时预览的在本地开发环境时需要同时运行 admin 后台管理端和 uni-app 手机端niucloud-web-app/src/main/resources/application.yml 文件中默认设置了手机端的访问地址配置本地开发环境如果出现了下面的界面就要检测 uni-app 手机算有无运行以及网址是否正确解决办法打开 uni-app 下的 .env.development 配置文件填写配置文件中的 api 请求地址和图片服务器地址填写好配置文件后开始执行编译命令。先在指定目录下执行命令编译h5编译完成后将地址填写到页面装修域名配置中不需要写后面的 wap例如http://localhost5174刷新后装修页面就可以正常显示了自定义组件概述自定义组件是 NIUCLOUD 框架提供的页面装修功能模块分为两类框架开发的自定义组件系统内置的组件如标题、图片广告、图文导航、魔方、文章、热区、公告等基础组件插件开发的自定义组件第三方开发者通过插件机制开发的组件如商城组件、会员卡组件等业务组件所有组件均支持拖拽操作和属性配置实现可视化页面装修框架定义的组件框架开发的自定义组件配置框架开发的自定义组件配置在 niucloud-core/src/main/resources/core/loader/diy/components.json如图所示配置文件是一个 json其中第一级对应组件分组如 BASIC然后对应分组名称title然后 list 列表其中每一项介绍一个组件图文导航组件针对图文导航组件进行说明具体一个组件的字段如下前端定义组件的编辑属性组件path 是 edit-graphic-nav这里指后台 diy 管理端按配置的前端代码位置这里的图文导航有很多配置选项比如横排竖排每行显示几个等等都有一个配置这些配置成功之后会组装成 json 传到后台同时系统配置也有初始化这些都在后台的配置文件具体配置字段多少与开发的组件有关uni-app 手机端渲染自定义组件uniapp 作用有两方面一方面后台配置实时展示另一方面针对配置后的结果展示在用户端比如上述图文导航组件比如上面图文导航组件名称 GraphicNav对应 uniapp 的自定义组件名称就是 graphinc-nav下面就是针对图文导航的文件定义以及页面文件位置uni-app/src/app/components/diy/graphic-nav开发自定义组件步骤新建 components.json 文件在 niucloud-addon/shop/src/main/resources/shop/loader/diy/ 目录下新建 components.json 文件文件名称必须对应框架会寻找所有插件下的这个文件进行加载前端定义编辑属性组件代码位置admin/src/addon/shop/views/diy/components根据 components.json 定义的 path 路径需要在前端定义编辑属性组件例如path 为 edit-goods-list编辑属性组件文件名称就是edit-goods-list.vueedit 编辑属性组件增加这段代码即可调用框架封装好的组件公共样式!-- 组件样式 -- slot namestyle/slot组件忽略属性每个组件可以根据自身业务情况设置忽略组件样式防止出现不可控效果关键代码为空时表示不忽略const diyStore useDiyStore() diyStore.editComponent.ignore [pageBgColor, marginTop, marginBottom, marginBoth, componentBgUrl] // 忽略公共属性目前可忽略的属性如下属性说明pageBgColor底部背景颜色componentBgUrl组件背景图componentBgColor组件背景颜色marginTop上边距marginBottom下边距marginBoth左右边距topRounded上圆角bottomRounded下圆角组件验证每个组件可以自定义验证规则点击保存时会触发关键代码截图{ code: true, // 验证状态true通过false未通过 message: // 提示信息 }// 组件验证 diyStore.editComponent.verify (index: number) { const res { code: true, message: } if (diyStore.value[index].source category) { if (diyStore.value[index].goods_category ) { res.code false res.message t(goodsCategoryPlaceholder) } } else if (diyStore.value[index].source custom) { if (diyStore.value[index].goods_ids.length 0) { res.code false res.message t(goodsPlaceholder) } } return res }uni-app 手机端定义渲染组件目录位置uni-app/src/addon/shop/components/diy根据 components.json 定义的组件关键字 key需要在 uni-app 手机端定义渲染组件例如组件关键字 key 为 GoodsList。渲染组件名称就是goods-list将驼峰命名改成横杠 - 分割即可。注意组件文件名称是小写关键代码template view :stylewarpCss !-- todo 背景图加遮罩层【可选若不需要可以移除】 -- view :stylemaskLayer/view !-- todo 根据业务自行编写渲染组件代码 -- !-- diyComponent 可以获取当前组件的数据结构可以打印查看 -- view classdiy-comp{{ diyComponent }}/view /view /template script setup langts import { ref, computed, watch, onMounted, nextTick, getCurrentInstance } from vue; import useDiyStore from /app/stores/diy; import { img } from /utils/common; 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(() { let style ; style position:relative;; if (diyComponent.value.componentStartBgColor) { if (diyComponent.value.componentStartBgColor diyComponent.value.componentEndBgColor) style background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });; else style background-color: diyComponent.value.componentStartBgColor ;; } if (diyComponent.value.componentBgUrl) { style background-image:url(${ img(diyComponent.value.componentBgUrl) });; style background-size: cover;background-repeat: no-repeat;; } if (diyComponent.value.topRounded) style border-top-left-radius: diyComponent.value.topRounded * 2 rpx;; if (diyComponent.value.topRounded) style border-top-right-radius: diyComponent.value.topRounded * 2 rpx;; if (diyComponent.value.bottomRounded) style border-bottom-left-radius: diyComponent.value.bottomRounded * 2 rpx;; if (diyComponent.value.bottomRounded) style border-bottom-right-radius: diyComponent.value.bottomRounded * 2 rpx;; return style; }) // 背景图加遮罩层【可选】 const maskLayer computed(() { let style ; if (diyComponent.value.componentBgUrl) { style position:absolute;top:0;width:100%;; style background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });; style height:${ height.value }px;; if (diyComponent.value.topRounded) style border-top-left-radius: diyComponent.value.topRounded * 2 rpx;; if (diyComponent.value.topRounded) style border-top-right-radius: diyComponent.value.topRounded * 2 rpx;; if (diyComponent.value.bottomRounded) style border-bottom-left-radius: diyComponent.value.bottomRounded * 2 rpx;; if (diyComponent.value.bottomRounded) style border-bottom-right-radius: diyComponent.value.bottomRounded * 2 rpx;; } return style; }); onMounted(() { refresh(); // 装修模式下刷新 if (diyStore.mode decorate) { watch( () diyComponent.value, (newValue, oldValue) { // todo 注意这里要替换成组件关键字key if (newValue newValue.componentName GoodsList) { refresh(); } } ) } }); const instance getCurrentInstance(); const height ref(0) // 页面onShow调用时会触发该方法 const refresh () { nextTick(() { const query uni.createSelectorQuery().in(instance); query.select(.diy-comp).boundingClientRect((data: any) { }).exec(); }) } /script style langscss scoped /style开发环境下建议手动修改 diy-group 组件代码引入自己开发的自定义组件文件位置uni-app/src/addon/components/diy/group/index.vue关键代码template v-ifcomponent.componentName GoodsList diy-goods-list :componentcomponent :globaldata.global :indexindex / /template import diyGoodsList from /addon/shop/components/diy/goods-list/index.vue;完成上面的操作后点击添加组件即可看到渲染后的组件效果