VForm自定义组件

一、自定义字段组件

1. 创建组件

在这里插入图片描述
创建的组件名称和组件内部的name属性需一致,以防组件调用失败。

2、配置组件参数

在这里插入图片描述

3、添加自定义属性

1、在/src/components/form-designer/widget-panel/widgetsConfig.js添加属性
在这里插入图片描述
2、在/src/components/form-designer/setting-panel/property-editor创建对应的属性编辑器xxx-editor.vue
在这里插入图片描述
3、在/src/components/form-designer/setting-panel/propertyRegister.js中注册组件
在这里插入图片描述
4、在/src/lang/zh-CN.js添加中英文对照
在这里插入图片描述

4、编译、打包及项目集成

1、编译打包

npm run lib

在这里插入图片描述
2、在自己项目中创建/src/lib/vform目录
在这里插入图片描述
将生成的designer.style.css和designer.umid.js复制到创建的目录下
3、在vite.config.js中配置

optimizeDeps: {
    include: ['@/../lib/vform/designer.umd.js']  //此处路径必须跟main.js中import路径完全一致!
},
build: {
    /* 其他build生产打包配置省略 */
    //...
    commonjsOptions: {
        include: /node_modules|lib/  //这里记得把lib目录加进来,否则生产打包会报错!!
    }
}

4、在main.js中引入

import vForm from '@/lib/vform/designer.umd.js'
import './lib/vform/designer.style.css'

app.use(vForm)

二、自定义树形容器组件

1. 配置组件参数

{
        type: "tree",
        icon: "tree",
        category: "container",
        widgetList: [],
        options: {
            name: "tree42275",
            label: "tree",
            size: "default",
            disabled: false,
            hidden: false,
            parentChildLinkAble: true,
            customClass: [],
            dsEnabled: false,
            dsName: "",
            dataSetName: "",
            showCheckBox: true,
            filter: true,
            defaultExpandAllNode: true,
            expandRetractAllNode: true,
            selectClearAllNode: true,
            expandOnClickNode: true,
            nodeEdit: true,
            draggable: false,
            lazy: false,
            treeDataEdit: true,
            onCreated: "",
            onMounted: "",
            onNodeClick: "",
            onNodeCheck: "",
            onNodeContextmenu: "",
            onCheckChange: "",
            treeData: [
                {
                    id: 1,
                    label: "一级 1",
                    children: [
                        {
                            id: 2,
                            label: "二级 1-1",
                            children: [
                                {
                                    id: 3,
                                    label: "三级 1-1-1"
                                }
                            ]
                        }
                    ]
                },
                {
                    id: 4,
                    label: "一级 2",
                    children: [
                        {
                            id: 5,
                            label: "二级 2-1",
                            children: [
                                {
                                    id: 6,
                                    label: "三级 2-1-1"
                                }
                            ]
                        },
                        {
                            id: 7,
                            label: "二级 2-2",
                            children: [
                                {
                                    id: 8,
                                    label: "三级 2-2-1"
                                }
                            ]
                        }
                    ]
                },
                {
                    id: 9,
                    label: "一级 3",
                    children: [
                        {
                            id: 10,
                            label: "二级 3-1",
                            children: [
                                {
                                    id: 11,
                                    label: "三级 3-1-1"
                                }
                            ]
                        },
                        {
                            id: 12,
                            label: "二级 3-2",
                            children: [
                                {
                                    id: '3-2-1',
                                    label: "三级 3-2-1"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    }

2. 创建树形组件

<template>
	<container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList"
					   :index-of-parent-list="indexOfParentList">
		<div :key="widget.id" class="tree-container"
			 :class="{'selected': selected}" @click.stop="selectWidget(widget)">
			<el-input v-if="widget.options.filter" v-model="filterText" placeholder="输入关键字进行过滤"/>
			<el-row class="padding5-top-bottom" v-if="widget.options.expandRetractAllNode || widget.options.selectClearAllNode">
				<el-col>
					<el-button-group>
						<el-button v-if="widget.options.expandRetractAllNode" type="primary" plain round @click="expandAll">展开/收缩</el-button>
						<el-button v-if="widget.options.selectClearAllNode" type="primary" plain round @click="selectAll">选中/取消选中</el-button>
					</el-button-group>
				</el-col>
			</el-row>
			<el-tree
				ref="tree"
				node-key="id"
				:props="defaultProps"
				:data="widget.options.treeData"
				:show-checkbox="widget.options.showCheckBox"
				:default-expand-all="widget.options.defaultExpandAllNode"
				:expand-on-click-node="widget.options.expandOnClickNode"
				:draggable="widget.options.draggable"
				:filter-node-method="filterNode"
				@node-click="handleTreeNodeClick"
				@check="handleTreeNodeCheck"
				@node-contextmenu="handleTreeContextmenu"
				@check-change="handleTreeCheckChange"
			>
				<template #default="{ node, data }">
					<span class="custom-tree-node">
						  <span>{{ node.label }}</span>
						  <span v-if="widget.options.nodeEdit">
							  <el-button size="small" type="primary" link @click.stop="handleAppend(data)">添加</el-button>
							  <el-button size="small" type="primary" link @click.stop="handleRemove(node, data)">删除</el-button>
						  </span>
					</span>
				</template>
			</el-tree>
		</div>
	</container-wrapper>
</template>

<script>
import i18n from "@/utils/i18n";
import containerMixin from "@/components/form-designer/form-widget/container-widget/containerMixin";
import refMixinDesign from "@/components/form-designer/refMixinDesign";
import ContainerWrapper from "@/components/form-designer/form-widget/container-widget/container-wrapper.vue";
import FieldComponents from "@/components/form-designer/form-widget/field-widget";
import {toRaw} from "vue";
import {treeToArray} from "@/utils/util";

export default {
	name: "tree-widget",
	componentName: 'ContainerWidget',
	mixins: [i18n, containerMixin, refMixinDesign],
	inject: ['refList'],
	components: {
		ContainerWrapper,
		...FieldComponents,
	},
	props: {
		widget: Object,
		parentWidget: Object,
		parentList: Array,
		indexOfParentList: Number,
		designer: Object,
	},
	data() {
		return {
			filterText: '',
			isExpanded: true,
			isChecked: false,
			defaultProps: {
				children: 'children',
				label: 'label'
			},
			id: 1000
		}
	},
	computed: {
		selected() {
			return this.widget.id === this.designer.selectedId
		},

		customClass() {
			return this.widget.options.customClass || ''
		},
	},
	watch: {
		filterText(val) {
			this.$refs.tree.filter(val)
		}
	},
	methods: {
		filterNode(value, data) {
			if (!value) return true
			return data.label.includes(value)
		},
		expandAll() {
			this.isExpanded =!this.isExpanded
			const list = treeToArray(toRaw(this.widget.options.treeData));
			list.forEach((item, index) => {
				this.$refs.tree.store.nodesMap[list[index].id].expanded = this.isExpanded;
			})
		},
		selectAll() {
			this.isChecked =!this.isChecked
			const list = toRaw(this.widget.options.treeData);
			this.$refs.tree.setCheckedNodes(this.isChecked ? list : [])
		},
		handleRemove(node, data) {
			const parent = node.parent
			const children = parent.data.children || parent.data
			const index = children.findIndex((d) => d.id === data.id)
			children.splice(index, 1)
			this.widget.options.treeData = [...this.widget.options.treeData]
		},
		handleAppend(data) {
			const newChild = { id: this.id++, label: 'testNode', children: [] }
			if (!data.children) {
				data.children = []
			}
			data.children.push(newChild)
			this.widget.options.treeData = [...this.widget.options.treeData]
		}
	}
}
</script>

<style lang="scss" scoped>
div.tree-container {
	padding: 5px;
	border: 1px dashed #336699;
	box-sizing: border-box;
}

.tree-container.selected {
	outline: 2px solid $--color-primary !important;
}
.custom-tree-node {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: 14px;
	padding-right: 8px;
}
</style>

3. 创建禁止父子节点联动字段组件(其他组件类似)

在这里插入图片描述

<template>
	<el-form-item :label="i18nt('designer.setting.parentChildLinkAble')">
		<el-switch v-model="optionModel.parentChildLinkAble"></el-switch>
	</el-form-item>
</template>

<script>
import i18n from "@/utils/i18n";

export default {
	name: "parentChildLinkAble-editor",
	mixins: [i18n],
	props: {
		designer: Object,
		selectedWidget: Object,
		optionModel: Object,
	},
}
</script>

<style scoped>

</style>
  1. 配置中文映射
    在这里插入图片描述
    src/lang/zh-CN.js中的setting对象中添加
  2. 添加组件映射
    在这里插入图片描述
    src/components/form-designer/setting-panel/propertyRegister.js中的COMMON_PROPERTIES添加

5.创建事件属性组件

在这里插入图片描述
src/components/form-designer/setting-panel/property-editor/event-handler目录下创建,组件名称为事件名称-editor

6. 添加事件组件映射

在这里插入图片描述
src/components/form-designer/setting-panel/propertyRegister.js中的EVENT_PROPERTIES中添加。

7. 添加事件处理方法

在这里插入图片描述

handleTreeCheckChange(data, checked, indeterminate) {
    if (!!this.widget.options.onCheckChange) {
        let remoteFn = new Function('data', 'checked', 'indeterminate', this.widget.options.onCheckChange)
        remoteFn.call(this, data, checked, indeterminate)
    }
}

src/components/form-designer/form-widget/container-widget/containerMixin.js中添加

8.编译导入js即可

常见问题

  1. vite项目中添加自定义组件后不显示
    在这里插入图片描述
    删除node_moudles中的.vite目录,重新运行项目。
  2. 创建字段组件后不显示
    字段组件的文件名称必须是字段名+-editor;文件中的name属性必须根文件名一致
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值