自定义组件
自定义组件用于对表单设计器基础组件的拓展,此功能基于 vue 开发,使用自定义组件需要对 vue 技术有一定的了解。
本文以 luckysheet 为例,开发一个自定义组件。
一.引入依赖
首先找到组件库管理页面
设置完基本信息后点击组件依赖项,新建依赖
然后找到官网 luckysheet 的依赖,将其添加到组件库依赖中,此处需要注意引入的js文件只能为umd格式,样式文件只能是css。添加依赖的方式有两种
1.CDN引入
将依赖CDN粘贴到依赖URL中即可,注意使用CDN引入时需要连接外网
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/css/pluginsCss.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/plugins.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/css/luckysheet.css' />
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/assets/iconfont/iconfont.css' />
<script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/js/plugin.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/luckysheet.umd.js"></script>
2.本地引入
将所需依赖打包放至根目录 public/lib 文件夹下,使用相对路径写入到依赖URL中,建议此方法引入
二.设计组件
打开代码编辑器,找到 src/components 文件夹,新建 zc-custom-luckysheet 文件夹,在 zc-custom-luckysheet文件夹下新建 src 文件夹用于放置组件主体代码 index.vue 和组件属性编辑代码 attrs-panel.vue ,并且新建index.js 用于导出组件
1.组件设计器
打开 index.vue 文件,在编辑器中加入如下模板
<template>
<div class="zc-custom-luckysheet">
<el-form-item
class="zc-custom-luckysheet"
:label="label"
:prop="zcAsyncValidatorProp"
:rules="zcAsyncValidatorRules"
>
<!-- 组件设计区域 -->
</el-form-item>
<!-- 其他区域 -->
</div>
</template>
<script>
import ZcFormElement from 'zerocloud-form-core/src/components/zc-form-element'
export default {
name: 'zc-custom-luckysheet',
extends: ZcFormElement,
data(){
return{}
},
mounted(){},
methods:{}
}
</script>
<style lang="scss" scoped>
</style>
el-form-item 标签内放置需要展示的组件代码,标签外放置与组件展示无关的代码,例如 dialog 弹出框、Notification 通知等
ZcFormElement 为表单设计器的基础类,封装有表单的基础属性,以下列举几种常用属性,更多了解请查看源码
- label:表单项左侧标签名称
- zcAsyncValidatorProp:表单验证的字段名
- zcAsyncValidatorRules:表单验证的规则
- zcDesignMode:设计模式,用于区分表单设计模式和预览/生产模式
- zcValue:组件绑定字段数据
- $attrs:组件的基础数据
根据 luckysheet 官网教程初始化表格
添加表格容器
<el-form-item
class="zc-custom-luckysheet"
:label="label"
:prop="zcAsyncValidatorProp"
:rules="zcAsyncValidatorRules"
>
<!-- luckysheet表格容器 -->
<div class="container" v-if="!zcDesignMode" :style="{ width: $attrs.width, height: $attrs.height } >
<div id="luckysheet" class="luckysheet" :style="{ width: $attrs.width, height: $attrs.height }></div>
</div>
<div class="empty" v-else>luckysheet</div>
</el-form-item>
添加初始化数据、方法
data(){
return{
// luckysheet 表格参数
options: {
name: 'Cell', //工作表名称
lang: 'zh',
container: 'luckysheet', // 容器id
data: [
{
name: 'Cell', //工作表名称
color: '', //工作表颜色
index: 0, //工作表索引
status: 1, //激活状态
order: 0, //工作表的下标
hide: 0, //是否隐藏
row: 100, //行数
column: 50, //列数
defaultRowHeight: 30, //自定义行高
defaultColWidth: 100, //自定义列宽
celldata: [],
},
],
},
}
},
mounted(){
this.init() // 初始化vue页面完成后调用初始化 luckysheet 函数
},
methods:{
init(){
if(this.zcDesignMode) return
luckysheet.create(this.options) // 初始化 luckysheet
}
}
由于 luckysheet 操作的是dom,同一设计页面不能出现多个 luckysheet 容器,而设计页面和预览页面是属于一个 vue 页面的,所以在此初始化时进行判断,只有在预览时初始化,设计界面不予展示
添加样式,因为 luckysheet 默认样式为 absolute ,所以父容器必须设置属性 position: relative
<style lang="scss" scoped>
.container {
position: relative;
}
</style>
2.组件属性编辑器
打开 attrs-panel.vue 文件,在编辑器中加入如下模板
<!--luckysheet属性编辑器-->
<template>
<div class="zc-custom-luckysheet-attrs-panel">
<el-form v-if="currentComp" v-bind="compAttrsFormConfig">
<el-form-item label="宽度">
<el-input v-model="currentComp.attrs.width" placeholder="px"></el-input>
</el-form-item>
<el-form-item label="高度">
<el-input v-model="currentComp.attrs.height" placeholder="px"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script>
import lodash from 'lodash'
import ZcFormDesignerStoreMixin from 'zerocloud-form-designer-core/src/mixins/designer-store-mixin.js'
export default {
name: 'zc-custom-luckysheet-attrs-panel',
mixins: [
ZcFormDesignerStoreMixin
],
components: {},
data() {
return {}
},
methods: {}
}
</script>
此模板是基于 element ui 中的 form 表单进行的数据绑定,但是开发者在实际开发中可以不局限于此种形式,只要可以进行数据的改动,即可实现对属性的编辑
ZcFormDesignerStoreMixin 是组件属性编辑器的混入类,封装有表单的基础属性
currentComp为此组件绑定的数据
{
"tag": "zc-custom-luckysheet", // 组件标志
"name": "luckysheet", // 组件名称
"icon": "el-icon-data-line", // 组件图表
"attrs": { // 组件基础信息
"label": "表格",
"width": "800px",
"height": "500px"
},
"zcAttrs": { // ZeroCloud平台数据,用于数据绑定、数据映射等
"databind": null
}
}
在此处更改的数据可以同步更新到 index.vue 中,用于设计组件的属性、样式等
3.导出组件
打开 index.js 文件,在编辑器中加入如下模板
import ZcCustomLuckysheet from './src/index.vue'
ZcCustomLuckysheet.install = function (Vue) {
Vue.component(ZcCustomLuckysheet.name, ZcCustomLuckysheet)
}
export default ZcCustomLuckysheet
打开 src 根目录下的 index.js 文件,根据模板导入组件设计器和属性编辑器
import ZcCustomAutograph from './components/zc-custom-autograph/src/index.vue'
import ZcCustomAutographAttrsPanel from './components/zc-custom-autograph/src/attrs-panel.vue'
import ZcCustomLuckysheet from './components/zc-custom-luckysheet/src/index.vue'
import ZcCustomLuckysheetAttrsPanel from './components/zc-custom-luckysheet/src/attrs-panel.vue'
const components = [ ZcCustomAutograph , ZcCustomLuckysheet ]
const attrsPanels = [ ZcCustomAutographAttrsPanel , ZcCustomLuckysheetAttrsPanel ]
const install = function (Vue) {
if (install.installed) return
components.map((component) => Vue.component(component.name, component))
attrsPanels.map((component) => Vue.component(component.name, component))
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
let componentsMap = {}
components.forEach((component) => {
componentsMap[component.name] = component
})
let attrsPanelsMap = {}
attrsPanels.forEach((component) => {
attrsPanelsMap[component.name] = component
})
export default {
install,
componentsMap,
attrsPanelsMap,
}
三.组件定义
打开组件库,组件定义选项,将上述属性编辑器中的 currentComp 写入数组中,点击保存即可完成自定义组件的基础搭建
进入到表单设计器中即可发现新建的组件库与自定义的组件
点击预览,可以看到 luckysheet 初始化成功
四.数据交互
以上步骤完成了自定义组件基础框架的搭建,但是仅仅展示一个页面是远远不够的,所以在自定义组件模块中也嵌入了 ZeroCloud 平台的数据交互模块
1.配置数据源
在 attrs-panel.vue 文件中导入数据源组件、添加数据源方法,并且将组件添加到页面中(此处仅展示数据源相关代码)
<template>
<div class="zc-custom-luckysheet-attrs-panel">
<el-form>
<el-form-item label="数据源">
<el-button icon="el-icon-zc-data" style="width:100%" @click="editDataSource">配置数据源</el-button>
</el-form-item>
</el-form>
<!-- 数据源组件 -->
<DataSourceConfigDrawer :visible.sync="datasourceDlg.visible" :model="datasourceDlg.model" @confirm="saveDataSource" />
</div>
</template>
<script>
import DataSourceConfigDrawer from 'zerocloud-form-designer-core/src/components/dialogs/datasource/datasource-config-drawer.vue'
export default {
components: {
DataSourceConfigDrawer,
},
data() {
return {
// 数据源对话框
datasourceDlg: {
visible: false,
model: null
}
}
},
methods: {
// 编辑数据源
editDataSource() {
this.datasourceDlg.model = this.currentComp.zcAttrs.datasource;
this.datasourceDlg.visible = true;
},
// 保存数据源回调
saveDataSource(dsModel) {
// 判断数据源是否更改,若已更改,则清空数据映射
let srcModel = lodash.cloneDeep(this.currentComp.zcAttrs.datasource);
let modified = this.isDataSourceModified(srcModel, dsModel);
if (modified) {
this.$set(this.currentComp.zcAttrs, 'datamap', []);
}
this.$set(this.currentComp.zcAttrs, 'datasource', lodash.cloneDeep(dsModel));
}
}
}
</script>
此时表单设计器中组件属性可以看到编辑数据源按钮,点击即可配置绑定数据源
打开 index.vue ,进行数据选取的配置操作(此处仅展示数据选取相关代码)
<template>
<div class="zc-custom-luckysheet">
<el-form-item
class="zc-custom-luckysheet"
:label="label"
:prop="zcAsyncValidatorProp"
:rules="zcAsyncValidatorRules"
>
<el-button @click="openDialog"> 选择数据 </el-button>
</el-form-item>
<el-dialog
:visible.sync="dataDlg.visible"
title="自定义数据"
append-to-body
@close="cancel"
>
<el-table
ref="table"
:data="page.rows"
@selection-change="onSelectionChange"
>
<el-table-column
type="selection"
width="50px"
align="center"
></el-table-column>
<el-table-column type="index" label="序号"></el-table-column>
<el-table-column label="编号" prop="MaterialCode"></el-table-column>
<el-table-column label="名称" prop="MaterialName"></el-table-column>
<el-table-column label="价格" prop="MaterialPrice"></el-table-column>
</el-table>
<div style="text-align: center">
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:current-page.sync="page.index"
:page-size.sync="page.size"
:total="page.total"
:page-sizes="[10, 20, 50]"
@current-change="loadData()"
@size-change="loadData()"
></el-pagination>
</div>
<div slot="footer">
<el-button type="primary" @click="confirm">确认</el-button>
<el-button @click="cancel">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dataDlg: {
visible: false,
},
keyword: null,
page: {
index: 1,
size: 10,
rows: [],
},
selectedRows: [],
}
},
computed: {
// 数据源
zcDataSource() {
return this.zcAttrs.datasource
},
},
watch: {
'dataDlg.visible'(val) {
if (val) {
this.loadData()
} else {
this.page.index = 1
this.page.rows = []
}
},
},
methods: {
// 加载数据
loadData() {
let index = this.page.index
let size = this.page.size
// 常量
const consts = {
page: {
keyword: this.keyword == null ? `''` : `'${this.keyword}'`,
index: index,
size: size,
},
}
if (!this.zcDataSource) return
let datasource = lodash.cloneDeep(this.zcDataSource)
// 系统数据源
if (datasource.type == 'sysDataSource') {
let dsConfig = datasource.config
let ds = dsConfig.ds
let objType = dsConfig.objType
let objName = dsConfig.objName
let objConfig = dsConfig.objConfig
// 表/视图
if (objType == 'tableview') {
let pageSearchConfig = {
index,
size,
keyword: this.keyword,
sorters: this.sorters,
filters: {
op: 'and',
rules: [],
groups: [
lodash.cloneDeep(this.filters),
lodash.cloneDeep(objConfig.filters),
],
},
}
pageSearchConfig.filters = this.loadData_convertExpFilterGroupValue(
pageSearchConfig.filters
)
pageSearchConfig.filters = this.fillFilterGroupValues(
pageSearchConfig.filters,
consts
)
this.loading = true
this.zcDsManagerInst
.sysDs_getTableviewPageData(ds, objName, pageSearchConfig)
.then((data) => {
console.log('data', data)
this.loading = false
let result = { ...data }
result.total = +result.total
this.page = result
console.log('this.page', this.page)
})
.catch((err) => {
this.loading = false
})
}
}
},
loadData_convertExpFilterGroupValue(filterGroup) {
if (!filterGroup) {
return {
op: 'and',
rules: [],
groups: [],
}
}
let rules = filterGroup.rules || []
rules.forEach((r) => {
if (isNaN(parseFloat(r.v))) {
r.v = `"${r.v}"`
}
})
let groups = filterGroup.groups || []
groups.forEach((g, gIndex) => {
groups[gIndex] = this.loadData_convertExpFilterGroupValue(g)
})
return filterGroup
},
// 勾选表格行
onSelectionChange(selection) {
this.selectedRows = [...selection]
},
openDialog() {
this.dataDlg.visible = true
},
confirm() {
let selection = this.selectedRows || []
if (!selection || selection.length == 0) return
this.mapData(selection)
this.cancel()
},
cancel() {
this.dataDlg.visible = false
}
}
}
</script>
只需在 loadData 方法中传入 page 配置项即可获取数据,此处使用了 element ui 的 table 表格组件进行数据展示,但是开发者在实际开发中可以不局限于此种形式。当用户选择完数据点击确定后,所选择数据便被储存到selectedRows 内
开发者可以根据选中数据对组件进行交互,例如将所选数据展示到 luckysheet 表格中
在 methods 中添加setData方法,并且在点击确定的方法中调用此方法
confirm() {
......
this.setData()
},
setData() {
let selection = this.selectedRows || []
let celldata = []
// 表头
let i = 0
for (let key in selection[0]) {
let cell = {
r: 0,
c: i,
v: {
ct: { fa: 'General', t: 'g' },
m: key,
v: key,
},
}
celldata.push(cell)
i++
}
// 表体
selection.forEach((row, index) => {
let k = 0
for (let key in row) {
let cell = {
r: index + 1,
c: k,
v: {
ct: { fa: 'General', t: 'g' },
m: row[key],
v: row[key],
},
}
celldata.push(cell)
k++
}
})
luckysheet.destroy()
this.$nextTick(() => {
this.options.data[0].celldata = celldata // 初始化数据
delete this.options.data[0].data // 初始化后lucky sheet使用数据,必须清空,不然不会渲染celldata
luckysheet.create(this.options)
})
},
选取数据后即可渲染至表格
2.数据映射(此功能需要先配置数据源)
在 attrs-panel.vue 文件中导入数据映射组件、添加数据映射方法,并且将组件添加到页面中(此处仅展示数据映射相关代码)
<!--luckysheet属性编辑器-->
<template>
<div class="zc-custom-luckysheet-attrs-panel">
<el-form v-if="currentComp" v-bind="compAttrsFormConfig">
<el-form-item label="数据映射">
<el-button :icon="loadingDsSchema ? 'el-icon-loading' : 'el-icon-zc-datamap'" :disabled="loadingDsSchema" style="width:100%" @click="editDatamap">编辑映射</el-button>
</el-form-item>
</el-form>
<DatamapDrawer :visible.sync="datamapDlg.visible" :fields="datamapDlg.fields" :rules="datamapDlg.rules" @confirm="saveDatamap" />
</div>
</template>
<script>
import DatamapDrawer from 'zerocloud-form-designer-core/src/components/dialogs/datamap/datamap-drawer.vue'
export default {
components: { DatamapDrawer },
data() {
return {
// 数据映射对话框
datamapDlg: {
visible: false,
rules: [],
fields: []
},
}
},
methods: {
// 编辑数据映射
editDatamap() {
this.datamapDlg.rules = this.currentComp.zcAttrs.datamap;
this.datamapDlg.fields = this.dsSchema.map(item => {
return {
label: item.name,
value: item.name
};
});
this.datamapDlg.visible = true;
},
// 保存数据映射
saveDatamap(rules) {
this.$set(this.currentComp.zcAttrs, 'datamap', rules);
}
}
}
</script>
此时表单设计器中组件属性可以看到编辑映射按钮,点击即可配置数据映射
3.编辑选项(用于单选、多选、下拉等 label、value形式的数据选择)
由于此模块不适用于 luckysheet 组件,故新建 zc-custom-checkbox 自定义组件用于展示,新建过程不在赘述,具体内容请参考上述文档说明。注意,此组件是基于 element ui 的 checkbox 组件自定义的,而系统内已经内置了element ui 的依赖,所以不需要额外的导入依赖