Skip to content

自定义组件

功能介绍

自定义组件是NIUCLOUD框架提供的页面装修功能模块,分为两类:

  1. 框架开发的自定义组件:系统内置的组件,如标题、图片广告、图文导航、魔方、文章、热区、公告等基础组件

  2. 插件开发的自定义组件:第三方开发者通过插件机制开发的组件,如商城组件、会员卡组件等业务组件

所有组件均支持拖拽操作和属性配置,实现可视化页面装修

框架定义的组件

系统框架已经定义好了一些基础的通用组件(不包含业务), 若无业务需求,开发者可以正常使用,无需重复开发

文件位置:niucloud/app/dict/diy/ComponentDict.php

自定义组件核心原理

如需了解自定义组件的核心原理,开发者可以自行阅读

文件路径:niucloud/core/dict/UniappComponent.php

根据当前站点下的应用/插件,找到 diy/components.php 文件,合并自定义组件列表

前端定义组件的编辑属性组件

框架定义的自定义组件目录为:admin/src/app/views/diy/components

uni-app手机端渲染自定义组件

框架定义的自定义组件目录为:uni-app/src/app/components/diy

更新diy-group组件代码核心原理

在使用云编译或者微信小程序云端发布时,会根据当前站点所安装的应用/插件,更新diy-group组件代码

如需了解核心原理,开发者可以自行阅读

文件位置:niucloud/app/service/core/addon/WapTrait.php

开发自定义组件步骤

新建components.php文件

在niucloud/addon/shop/app/dict/diy/目录下,新建components.php文件

文件名称必须对应,框架会寻找所有插件下的这个文件,进行加载 组件关键字建议增加自己插件的前缀,防止冲突。例如:o2oGoodsList、ShopMemberInfo等

关键代码解析

php
<?php

return [
    'SHOP_COMPONENT' => [ // 组件分类关键字key,建议大写
        'title' => get_lang('dict_diy.shop_component_type_basic'), // 组件分类名称
        'list' => [ // 组件列表
            'Text' => [ // 组件关键字key
                'title' => '标题', // 组件名称
                'icon' => 'iconfont iconbiaotipc', // 组件iconfont图标
                'path' => 'edit-text', // 编辑组件属性名称
                'support_page' => [], // 支持页面,为空表示任何自定义页面都可以加载并使用该组件
                'uses' => 0, // 组件的最大添加数量,0为不限制
                'sort' => 10001, // 排序号,从小到大
                'position' => '', // 组件置顶标识,设置后将不能拖拽,可选值:fixed、top_fixed、right_fixed、bottom_fixed、left_fixed
                // 组件属性,设置后将覆盖默认值
                'template' => [
                    "textColor" => "#303133", // 文字颜色
                    'pageStartBgColor' => '', // 底部背景颜色(开始)
                    'pageEndBgColor' => '', // 底部背景颜色(结束)
                    'pageGradientAngle' => 'to bottom', // 渐变角度,从上到下(to bottom)、从左到右(to right)
                    'componentBgUrl' => '', // 组件背景图片
                    'componentBgAlpha' => 2, // 组件背景图片的透明度,0~10
                    "componentStartBgColor" => '', // 组件背景颜色(开始)
                    "componentEndBgColor" => '', // 组件背景颜色(结束)
                    "componentGradientAngle" => 'to bottom', // 渐变角度,上下(to bottom)、左右(to right)
                    "topRounded" => 0, // 组件上圆角
                    "bottomRounded" => 0, // 组件下圆角
                    "elementBgColor" => '', // 元素背景颜色
                    "topElementRounded" => 0,// 元素上圆角
                    "bottomElementRounded" => 0, // 元素下圆角
                    "margin" => [
                        "top" => 0, // 上边距
                        "bottom" => 0, // 下边距
                        "both" => 0 // 左右边距
                    ],
                ],
                'value' => [
                    // todo 组件的数据结构,根据业务,自行定义,命名规范建议采用小驼峰,字段要尽可能简单,避免太长
                ]
            ]
        ]
    ]
];

定义组件的语言包

前端定义编辑属性组件

代码位置:admin/src/addon/shop/views/diy/components

根据components.php定义的path路径,需要在前端定义编辑属性组件

例如:path为edit-goods-list,编辑属性组件文件名称就是:edit-goods-list.vue

edit编辑属性组件

增加这段代码,即可调用框架封装好的组件公共样式

html
<!-- 组件样式 -->
<slot name="style"></slot>

关键代码截图 效果图

组件忽略属性

每个组件可以根据自身业务情况,设置忽略组件样式,防止出现不可控效果 关键代码,为空时表示不忽略

javascript
const diyStore = useDiyStore()
diyStore.editComponent.ignore = ['pageBgColor', 'marginTop', 'marginBottom', 'marginBoth', 'componentBgUrl'] // 忽略公共属性

目前可忽略的属性如下:

属性说明
pageBgColor底部背景颜色
componentBgUrl组件背景图
componentBgColor组件背景颜色
marginTop上边距
marginBottom下边距
marginBoth左右边距
topRounded上圆角
bottomRounded下圆角

具体原理可查看代码了解

组件验证

每个组件可以自定义验证规则,点击保存时会触发 关键代码截图

json
{
	"code": true, // 验证状态,true:通过,false:未通过
	"message": "" // 提示信息
}
typescript
// 组件验证
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.php定义的组件关键字key,需要在uni-app手机端定义渲染组件

例如:组件关键字key为GoodsList。渲染组件名称就是:goods-list,将驼峰命名改成横杠 - 分割即可。注意:组件文件名称是小写 关键代码

vue
<template>
    <view :style="warpCss">
        <!-- todo 背景图加遮罩层【可选,若不需要,可以移除】 -->
        <view :style="maskLayer"></view>
        <!-- todo 根据业务自行编写渲染组件代码 -->
        <!-- diyComponent 可以获取当前组件的数据结构,可以打印查看 -->
        <view class="diy-comp">{{ diyComponent }}</view>
    </view>
</template>

<script setup lang="ts">
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 lang="scss" scoped>
</style>

开发环境下,建议手动修改diy-group组件代码,引入自己开发的自定义组件

文件位置:uni-app/src/addon/components/diy/group/index.vue 关键代码

vue
<template v-if="component.componentName == 'GoodsList'">
   <diy-goods-list :component="component" :global="data.global" :index="index" />
</template>

import diyGoodsList from '@/addon/shop/components/diy/goods-list/index.vue';

完成上面的操作后,点击添加组件,即可看到渲染后的组件效果

基于 MIT 协议发布