场景
当vform中所存在的组件并不能满足需求的,需要自己新增自己公司的组件。基于这种需求下,根据vform的代码,进行修改实现需求。
定位文件
vform这个低代码框架,扩展组件都放在extension文件夹中。在2022版本中他内置了两个扩展card和alert,用得较多的就是card卡片容器。
// extension-loader.js
import Vue from 'vue'
import {
addContainerWidgetSchema,
addCustomWidgetSchema
} from '@/components/form-designer/widget-panel/widgetsConfig'
import * as PERegister from '@/components/form-designer/setting-panel/propertyRegister'
import * as PEFactory from '@/components/form-designer/setting-panel/property-editor-factory'
import {cardSchema} from "@/extension/samples/extension-schema"
import CardWidget from '@/extension/samples/card/card-widget'
import CardItem from '@/extension/samples/card/card-item'
import {registerCWGenerator} from '@/utils/sfc-generator'
import {cardTemplateGenerator} from '@/extension/samples/extension-sfc-generator'
import {alertSchema} from "@/extension/samples/extension-schema"
import AlertWidget from '@/extension/samples/alert/alert-widget'
import {registerFWGenerator} from '@/utils/sfc-generator'
import {alertTemplateGenerator} from '@/extension/samples/extension-sfc-generator'
// INFO micro组件开始导入
/* -------------------micro components-------------------- */
import {microCustomerSchema} from '@/extension/samples/extension-schema'
import MicroCustomerWidget from '@/extension/samples/micro/micro-customer-widget'
/* ------------------------------------------------------- */
export const loadExtension = function () {
/**
* 加载容器组件步骤:
* 1. 加载组件Json Schema;
* 2. 全局注册容器组件,容器组件有两种状态——设计期和运行期,故需要注册两个组件;
* 3. 全局注册属性编辑器组件(基本属性、高级属性、事件属性);
* 4. 注册容器组件的代码生成器;
* 5. 加载完毕。
*/
addContainerWidgetSchema(cardSchema) //加载组件Json Schema
/* -------------------------------------------------- */
Vue.component(CardWidget.name, CardWidget) //注册设计期的容器组件
Vue.component(CardItem.name, CardItem) //注册运行期的容器组件
/* -------------------------------------------------- */
PERegister.registerCPEditor('card-folded', 'card-folded-editor',
PEFactory.createBooleanEditor('folded', 'extension.setting.cardFolded'))
PERegister.registerCPEditor('card-showFold', 'card-showFold-editor',
PEFactory.createBooleanEditor('showFold', 'extension.setting.cardShowFold'))
PERegister.registerCPEditor('card-cardWidth', 'card-cardWidth-editor',
PEFactory.createInputTextEditor('cardWidth', 'extension.setting.cardWidth'))
let shadowOptions = [
{label: 'never', value: 'never'},
{label: 'hover', value: 'hover'},
{label: 'always', value: 'always'},
]
PERegister.registerCPEditor('card-shadow', 'card-shadow-editor',
PEFactory.createSelectEditor('shadow', 'extension.setting.cardShadow',
{optionItems: shadowOptions}))
/* -------------------------------------------------- */
registerCWGenerator('card', cardTemplateGenerator) //注册容器组件的代码生成器
/* -------------------------------------------------- */
/* 容器组件加载完毕 end */
/**
* 加载字段组件步骤:
* 1. 加载组件Json Schema;
* 2. 全局注册字段组件,字段组件设计期和运行期共用,故需要仅需注册一个组件;
* 3. 全局注册属性编辑器组件(基本属性、高级属性、事件属性);
* 4. 注册字段组件的代码生成器;
* 5. 加载完毕。
*/
addCustomWidgetSchema(alertSchema) //加载组件Json Schema
/* -------------------------------------------------- */
Vue.component(AlertWidget.name, AlertWidget) //注册组件
/* -------------------------------------------------- */
PERegister.registerCPEditor('alert-title', 'alert-title-editor',
PEFactory.createInputTextEditor('title', 'extension.setting.alertTitle'))
let typeOptions = [
{label: 'success', value: 'success'},
{label: 'warning', value: 'warning'},
{label: 'info', value: 'info'},
{label: 'error', value: 'error'},
]
// PERegister.registerCPEditor('alert-type', 'alert-type-editor',
// PEFactory.createSelectEditor('type', 'extension.setting.alertType',
// {optionItems: typeOptions}))
/* type属性映射已存在,无须再注册,故只需注册属性编辑器即可!! */
Vue.component('alert-type-editor',
PEFactory.createSelectEditor('type', 'extension.setting.alertType',
{optionItems: typeOptions}))
PERegister.registerCPEditor('alert-description', 'alert-description-editor',
PEFactory.createInputTextEditor('description', 'extension.setting.description'))
PERegister.registerCPEditor('alert-closable', 'alert-closable-editor',
PEFactory.createBooleanEditor('closable', 'extension.setting.closable'))
PERegister.registerCPEditor('alert-closeText', 'alert-closeText-editor',
PEFactory.createInputTextEditor('closeText', 'extension.setting.closeText'))
PERegister.registerCPEditor('alert-center', 'alert-center-editor',
PEFactory.createBooleanEditor('center', 'extension.setting.center'))
PERegister.registerCPEditor('alert-showIcon', 'alert-showIcon-editor',
PEFactory.createBooleanEditor('showIcon', 'extension.setting.showIcon'))
let effectOptions = [
{label: 'light', value: 'light'},
{label: 'dark', value: 'dark'},
]
PERegister.registerCPEditor('alert-effect', 'alert-effect-editor',
PEFactory.createRadioButtonGroupEditor('effect', 'extension.setting.effect',
{optionItems: effectOptions}))
PERegister.registerEPEditor('alert-onClose', 'alert-onClose-editor',
PEFactory.createEventHandlerEditor('onClose', []))
/* -------------------------------------------------- */
registerFWGenerator('alert', alertTemplateGenerator) //注册字段组件的代码生成器
/* -------------------------------------------------- */
/* 字段组件加载完毕 end */
/*INFO: xy-micro组件 */
addCustomWidgetSchema(microCustomerSchema) //加载到widgetConfig的customWidget内部
/* -------------------------------------------------- */
Vue.component(MicroCustomerWidget.name, MicroCustomerWidget) //注册客户组件
/* -------------------------------------------------- */
PERegister.registerCPEditor( 'micro-button-span', 'micro-button-span-editor')
PERegister.registerCPEditor( 'micro-customer-span', 'micro-customer-span-editor')
/*INFO: END 注册自定义组件结束 */
}
因为代码杂糅故将他重构,
import { initCardWidget } from './widget/card/card-widget-loader'
import { initAlertWidget } from "./widget/alert/alert-widget-loader"
import { initDictWidget } from './widget/dict/dict-widget-loader'
import { initFuzzySearchWidget } from './widget/fuzzy-search/fuzzy-search-widget-loader'
import { initButtonFields } from './widget/button/button-widget-loader'
import { initComponentWidget } from './widget/component/component-widget-loader'
import { initBodyWidget } from "./widget/body-area/body-widget-loader"
import { initGridColFields } from "./widget/grid-col/grid-col-widget-loader"
import { initInputFields } from "./widget/input/input-widget-loader"
import { initStaticTextFields } from "./widget/static-text/static-text-widget-loader"
import { initSubformContainer } from "./widget/subform/subform-container"
import { initDataTableLoader } from "./widget/data-table/data-table-loader"
import { initDateFields } from "./widget/date/date-widget-loader"
import { initProgressWidget } from "./widget/progress/progress-widget-loader"
// @todo 后续自动加载插件
export const loadExtension = function () {
// 容器组件加载
initCardWidget()
initBodyWidget()
initSubformContainer()
// 字段组件加载
initAlertWidget()
initDictWidget()
initFuzzySearchWidget()
// 添加button的类型属性
initButtonFields()
initComponentWidget()
initGridColFields()
initInputFields()
initStaticTextFields()
initDataTableLoader()
initDateFields()
initProgressWidget()
}
添加的步骤
首先是config的配置,即导出JSON中属于这个组件的参数JSON,就是schema
这个schema添加到
他有了这个配置,导出的JSON就会有这个配置信息。 那他组件是怎么弄上去的呢?
自定义组件格式
用form-item-wrapper进行包裹,内部就放自己想添加的组件的名字。字段根据配置自己绑定就可。需要记住的是setting的参数如果没有,就需要自己注册setting的设置组件。才能绑定上值。
widget的组件
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules"
:design-state="designState"
:parent-widget="parentWidget"
:parent-list="parentList"
:index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex"
:sub-form-col-index="subFormColIndex"
:sub-form-row-id="subFormRowId">
<hydra-fuzzy-search :class="[selected ? 'selected' : '']"
:span="field.options.span"
:label="field.options.label"
v-model="fieldModel"
:data-value="field.options.dataValue"
:config="field.options.cfgMeta"
prop="customerId"
displayProp="customerName"
:disabled="field.options.disabled"
:multiple="field.options.multiple"
v-bind="transferExternal(field.options)"
@change="onChangeFuzzySearch" />
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from "@/components/form-designer/form-widget/field-widget/form-item-wrapper"
import emitter from '@/utils/emitter'
import i18n, { translate } from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "fuzzy-search-widget",
componentName: 'FieldWidget', //必须固定为FieldWidget,用于接收父级组件的broadcast事件
components: {
FormItemWrapper
},
inject: ['refList', 'globalOptionData', 'globalModel'],
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引,从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引,从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id,唯一id且不可变 */
type: String,
default: ''
},
},
data() {
return {
fieldModel: "",
rules: []
}
},
computed: {
selected() {
return !!this.designer && this.field.id === this.designer.selectedId
},
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* 注意:子组件mounted在父组件created之后、父组件mounted之前触发,故子组件mounted需要用到的prop
需要在父组件created中初始化!! */
this.registerToRefList()
this.initOptionItems()
this.initFieldModel()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
onChangeFuzzySearch(val) {
// let d = val ? val.id : ''
this.handleChangeEvent(val)
},
transferExternal(options) {
let query = {};
if (options.hasOwnProperty('vfromSetQueryLimit') && !!options.vfromSetQueryLimit) {
query.vfromSetQueryLimit = new Function( 'query', options.vfromSetQueryLimit);
}
if (options.hasOwnProperty('vformLimitData') && !!options.vformLimitData) {
query.vformLimitData = new Function( 'query', options.vformLimitData);
}
return query;
}
}
}
</script>
<style lang="scss" scoped>
@import "@/styles/global.scss";
</style>
schemal数据
export const fuzzySearchSchema = {
type: 'fuzzy-search',
icon: 'rate-field',
formItemFlag: true,
isFuzzySearch: true,
options: {
dataValue: '',
limitValue: '',
cfgMeta: {},
name: '',
label: '对象引用',
labelAlign: '',
type: 'text',
defaultValue: "",
placeholder: '',
columnWidth: '200px',
size: '',
labelWidth: null,
labelHidden: false,
readonly: false,
disabled: false,
hidden: false,
clearable: true,
showPassword: false,
required: false,
requiredHint: '',
validation: '',
validationHint: '',
multiple: false,
jsonable: false,
chain: false,
//-------------------
onCreated: '',
onMounted: '',
onInput: '',
onChange: '',
onFocus: '',
onBlur: '',
onValidate: '',
vfromSetQueryLimit: '',
vformLimitData: ''
}
}
schemaLoader的setting设置组件
fuzzy-search-widget-loader.js
import Vue from 'vue'
import * as PERegister from '@/components/form-designer/setting-panel/propertyRegister'
import * as PEFactory from '@/components/form-designer/setting-panel/property-editor-factory'
import FuzzySearchWidget from './fuzzy-search-widget'
export function initFuzzySearchWidget() {
Vue.component(FuzzySearchWidget.name, FuzzySearchWidget)
let fuzzyDataValue = PEFactory.createInputTextEditor('dataValue', 'extension.fuzzySearch.dataValue')
PERegister.registerCPEditor('fuzzy-search-dataValue', 'data-value-editor', fuzzyDataValue)
let limitValue = PEFactory.createInputTextEditor('limitValue', 'extension.fuzzySearch.limitValue')
PERegister.registerCPEditor('fuzzy-search-limitValue', 'limit-value-editor', limitValue)
let cfgMeta = PEFactory.createInputTextEditor('cfgMeta', 'extension.fuzzySearch.cfgMeta')
PERegister.registerCPEditor('fuzzy-search-cfgMeta', 'cfgmeta-value-editor', cfgMeta)
let isJson = PEFactory.createBooleanEditor('jsonable', 'extension.fuzzySearch.jsonable')
PERegister.registerCPEditor('fuzzy-search-jsonable', 'jsonable-value-editor', isJson)
let isMultiple = PEFactory.createBooleanEditor('multiple', 'extension.fuzzySearch.multiple')
PERegister.registerCPEditor('fuzzy-search-multiple', 'multiple-value-editor', isMultiple)
let dataTableComponent = PEFactory.createEventHandlerEditor('vfromSetQueryLimit', ['query'])
PERegister.registerEPEditor('vfromSetQueryLimit', 'vform-setquery-limit', dataTableComponent)
let vformLimitDataComponent = PEFactory.createEventHandlerEditor('vformLimitData', ['query'])
PERegister.registerEPEditor('vformLimitData', 'vform-limit-data', vformLimitDataComponent)
let Chain = PEFactory.createBooleanEditor('chain', 'extension.input.chain')
PERegister.registerCPEditor('chain', 'fuzzy-search-chain-editor', Chain)
}
PEFactory中有创建setting组件的方法。会挂载到Vue的component上面,根据规则就有setting右边的控制组件
总结
当form-designer的widget-panel中widgetsConfig有了定义组件的schema,同时setting有了控制schema的loader的时候,自己的组件就添加成功了。