文章目录
- 前言
- 一、配置化的前提
- 二、配置的相关组件
- 1、新建form.vue组件
- 2、新建input.vue组件
- 3、新建select.vue组件
- 4、新建v-html.vue组件
- 5、新建upload.vue组件
- 6、新建switch.vue组件
- 7、新建radio.vue组件
- 8、新建checkbox.vue组件
- 9、新建date.vue组件
- 10、新建time-picker.vue组件
- 11、新建cascader.vue组件
- 12、新建/ueditor/组件
- 13、新建/kind-editor组件
- 二、配置的表单及使用
前言
网站基本离不开表单提交,结合之前保险项目及各种检验平台,都是需要填写大量数据,均涉及多个不同表单提交。
所以表单提交配置化是很有必要的 – 基于面向对象
也有很多业务需求需要表格行编辑,可参考 基于element-plus定义表格行内编辑配置化
本文以vue3+element-plus
为例,由于时间有限,部分配置可结合项目实际扩展
先展示实际效果图
一、配置化的前提
表单内一般包含
- 需要什么字段
- 字段填写形式(输入框、下拉框、多级联动、开关、日期、附件上传、富文本编辑器等等)
- 字段校验(是否必填、存在特殊校验,例如邮箱格式等)
- 字段排序及各种属性配置
- …
二、配置的相关组件
以下文件均建立在 @/componentsform-configuration
目录下
1、新建form.vue组件
用于接收配置信息
<template><el-form class="customForm":model="fields":rules="rules"ref="formData":size="size":disabled="disabled":validate-on-rule-change="validateOnRuleChange":hide-required-asterisk="hideAsterisk":label-position="labelPosition":label-width="labelWidth"><el-row :gutter="gutter"><el-col v-for="(item, index) in formData" v-show="item.show" :key="item.field + '-' + index" :span="item.colSize"><el-form-item :prop="item.field" :label="item.title" :class="item.class"><component v-model:content="fields[item.field]" v-model="fields[item.field]" :property="{...item.property, name: item.field}" :is="item.type" @fieldChange="(val) => change(val, item.field)" /></el-form-item></el-col></el-row></el-form>
</template>
<script>
import Input from '@/components/form-configuration/input.vue'
import Select from '@/components/form-configuration/select.vue'
import Vhtml from '@/components/form-configuration/v-html.vue'
import Upload from '@/components/form-configuration/upload.vue'
import Switch from '@/components/form-configuration/switch.vue'
import Radio from '@/components/form-configuration/radio.vue'
import Checkbox from '@/components/form-configuration/checkbox.vue'
import Date from '@/components/form-configuration/date.vue'
import TimePicker from '@/components/form-configuration/time-picker.vue'
import Cascader from '@/components/form-configuration/cascader.vue'
import UEditor from '@/components/form-configuration/ueditor/index.vue'
import KindEditor from '@/components/form-configuration/kind-editor/index.vue'
import { defineComponent, reactive, ref, watch, computed, nextTick } from 'vue'export default {components: {Input,Select,Vhtml,Upload,Switch,Radio,Checkbox,Date,TimePicker,Cascader,},props: {disabled: {type: Boolean,default: false // },size: {type: String,default: 'default' // "", "default", "small", "large"},gutter: {type: Number,default: 20},formData: {type: Object,default() {return {}}},hideAsterisk: {type: Boolean,default: false},validateOnRuleChange: {type: Boolean,default: false},fields: {}, // form相关字段值rules: {}, // form校验labelWidth: {type: String,default: '180px'},labelPosition: {type: String,default: 'right', // left/right/top}},setup(props, { emit }) {const change = (val, field) => {emit('change', {val, field}) // 触发}return {change}}
}
</script>
<style lang="less" scoped>
</style>
2、新建input.vue组件
<template><el-input v-model="input":type="fieldProperty.type":clearable="fieldProperty.clearable":placeholder="fieldProperty.placeholder" :maxlength="fieldProperty.maxlength":readonly="fieldProperty.readonly"@blur="blur"@change="change"autocomplete="on":name="fieldProperty.name":disabled="fieldProperty.disabled" />
</template>
<script lang="ts">
import { computed } from 'vue'
export default {props: {modelValue: [String, Number], // 组件绑定值content: [String, Number], // 组件绑定值property: {type: Object,default() {return {}}}},setup(props, { emit }) {const fieldProperty = computed(() => {return {placeholder: '请输入', // 提示语maxlength: -1, // 可输入最大长度readonly: false, // 是否只读disabled: false, // 是否可输入clearable: false, // 是否可清空输入框type: 'text', // 输入框类型 input | textarea | password | text...props.property}})const input = computed({get() {return props.modelValue},set(val) {emit('update:modelValue', val)}})const change = (val: any) => {emit('fieldChange', val) // 触发}const blur = () => {emit('fieldBlur', input.value) // 触发}return {input,change,blur,fieldProperty}},
}
</script>
<style lang="less" scoped></style>
3、新建select.vue组件
<template><el-select v-model="val"ref="selectRef":multiple="fieldProperty.multiple":filterable="fieldProperty.filterable":clearable="fieldProperty.clearable":disabled="fieldProperty.disabled"@change="change":placeholder="fieldProperty.placeholder"><el-option v-for="(item, index) in fieldProperty.data":key="item[fieldProperty.value] + item[fieldProperty.label] + '-' + index":label="item[fieldProperty.label]" :value="item[fieldProperty.value]"><!-- {{ item[fieldProperty.label] }} --></el-option></el-select>
</template>
<script lang="ts">
import { computed } from 'vue'
export default {props: {modelValue : [String, Number, Array],property: {type: Object,default() {return {}}}},setup(props, { emit }) {const fieldProperty = computed(() => {return {placeholder: '请选择', // 提示语data: [], // 下拉可选数据label: 'label', // 选择框的文案字段value: 'value', // 选择框的值字段multiple: false, // 是否多选filterable: true, // 是否可搜索clearable: false, // 是否可以清空选项disabled: false, // 是否禁用...props.property}})const val = computed({get() {return props.modelValue },set(val) {emit('update:modelValue', val) // 触发}})const change = (val: any) => {emit('fieldChange', val) // 触发}return {val,fieldProperty,change} }
}
</script>
<style lang="less" scoped>
</style>
4、新建v-html.vue组件
<template><div v-html="val" class="pre-line" style="line-height: 26px"></div>
</template>
<script>
export default {props: {modelValue: [String, Number]},model: {prop: 'modelValue', // 指定 v-model 要绑定的参数叫什么名字,来自于 props 中定义的参数event: 'change' // 指定要触发的事件名字,将被用于 $emit},data() {return {}},computed: {val: {// 这里的计算属性使用了 getter、setter,可以简化代码// 可参见链接 https://cn.vuejs.org/v2/guide/computed.html#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E7%9A%84-setterget() {return this.modelValue},set(val) {this.$emit('change', val) // 触发}}},methods: {}
}
</script>
<style lang="less" scoped>
</style>
5、新建upload.vue组件
<template><el-uploadref="upload"class="upload-demo":file-list="val":multiple="fieldProperty.multiple":limit="fieldProperty.limit":disabled="fieldProperty.disabled"action="#":auto-upload="false":on-exceed="handleExceed":on-preview="handlePreview":on-change="handleChange":accept="fieldProperty.accept":on-remove="handleRemove"><el-button type="primary" :loading="fieldProperty.loading">{{ fieldProperty.btnName }}</el-button><div class="el-upload__tip">{{ fieldProperty.tips}}</div></el-upload>
</template>
<script>
import { watch, reactive, computed, ref } from 'vue'
import { ElMessage, genFileId } from 'element-plus'
import { DownloadFileAPI } from "@/server/Base"
import { formDataDownFile } from "@/utils"
export default {props: {modelValue: [String, Number, Array], // 组件绑定值property: {type: Object,default() {return {}}}},setup(props, { emit }) {const upload = ref()const fieldProperty = computed(() => {return {multiple: false, // 是否可多选limit: 10, // 依次最大可上传btnName: '点击上传', // 出发上传按钮文案tips: '', // 上传文案提示accept: '', // 文件上传类型disabled: false,maximum: 20, // 默认最大 20M ; 1M = 1024b * 1024KBloading: false,...props.property}})const val = computed({get() {return props.modelValue },set(val) {emit('update:modelValue', val) // 触发}})const handleChange = (file, fileList) => {const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLocaleLowerCase();const whiteList = fieldProperty.value.accept.toLocaleLowerCase().split(',')if (whiteList.indexOf('.'+fileSuffix) === -1 && fieldProperty.value.accept) {val.value = fileList.filter((fie) => fie.uid !== file.uid)ElMessage.error('The file type can only be ' + fieldProperty.value.accept)return false;}if (file.size > fieldProperty.value.maximum * 1024 * 1024) {val.value = fileList.filter((fie) => fie.uid !== file.uid)ElMessage.error('The maximum upload size is ' + fieldProperty.value.maximum + 'M')return}val.value = fileListemit('fieldChange', { file, index: fileList.length - 1, fileList}) // 触发当前上传附件}const handleRemove = (file, fileList) => {val.value = fileListemit('fieldChange', fileList) // 触发}const handlePreview = (file) => {const params = {fullName: file.url || file.filePath?.FullName,oriFileName: file.name || file.filePath?.OriginalName}const action = DownloadFileAPI(params)formDataDownFile(action, params)}const handleExceed = (files) => {console.log('100000', fieldProperty.multiple)if (!fieldProperty.multiple) {upload.value.clearFiles()const file = files[0]file.uid = genFileId()upload.value.handleStart(file) }}return {handleChange,handleRemove,handleExceed,handlePreview,fieldProperty,upload,val}}
}
</script>
<style lang="less" scoped>
</style>
6、新建switch.vue组件
<template><el-switch v-model="val":disabled="fieldProperty.disabled":active-color="fieldProperty.activeColor":inactive-color="fieldProperty.inactiveColor"></el-switch>
</template>
<script>
import { computed, reactive} from 'vue'
export default {name: 'SmSwitch',props: {modelvalue: [Boolean],property: {type: Object,default() {return {}}}},setup(props, { emit }) {const fieldProperty = reactive({activeColor: '#13ce66',inactiveColor: '#DCDFE6',disabled: false,...props.property})const val = computed({get() {return props.modelvalue},set(val) {emit('update:modelvalue', val) // 触发}})return {val,fieldProperty}}
}
</script>
<style lang="less" scoped></style>
7、新建radio.vue组件
<template><el-radio-group v-model="val" class="ml-4" :disabled="fieldProperty.disabled" :size="fieldProperty.size"><el-radio v-for="(item, index) in fieldProperty.data" :key="item + index + RandomNumber()" :label="item[fieldProperty.value]">{{ item[fieldProperty.label] }}</el-radio></el-radio-group>
</template>
<script lang="ts">
import { computed, reactive} from 'vue'
import { RandomNumber } from '@/utils'
export default {name: 'Radio',props: {modelvalue: [Boolean],property: {type: Object,default() {return {}}}},setup(props, { emit }) {const fieldProperty = reactive({readonly: false,disabled: false,label: 'label', // 选择框的文案字段value: 'value', // 选择框的值字段data: [],size: 'default', // 'large' | 'default' | 'small'...props.property})const val = computed({get() {return props.modelvalue},set(val) {emit('update:modelvalue', val) // 触发}})return {val,fieldProperty,RandomNumber}}
}
</script>
<style lang="less" scoped></style>
8、新建checkbox.vue组件
<template><el-checkbox-groupv-if="fieldProperty.data.length"v-model="val":disabled="fieldProperty.disabled"@change="handleCheckedChange"><el-checkboxv-for="(item, index) in fieldProperty.data":key="item.label + index + RandomNumber()":disabled="item.disabled":label="item.value">{{ item.label }}</el-checkbox></el-checkbox-group><el-checkboxv-elsev-model="val":disabled="fieldProperty.disabled":label="fieldProperty.label"/>
</template>
<script lang="ts">
import { computed, reactive } from "vue";
import { RandomNumber } from "@/utils";
import type { CheckboxData } from "@/interface/form";
export default {name: "Radio",props: {modelvalue: [Array, Boolean],property: {type: Object,default() {return {};},},},setup(props, { emit }) {const fieldProperty = computed(() => {return {readonly: false,disabled: false,label: "label", // 单选框的文案字段data: [] as Array<CheckboxData>,size: "default", // 'large' | 'default' | 'small'...props.property,};});const handleCheckedChange = () => {};const val = computed({get() {return props.modelvalue;},set(val) {emit("update:modelvalue", val); // 触发},});return {val,RandomNumber,handleCheckedChange,fieldProperty,};},
};
</script>
<style lang="less" scoped></style>
9、新建date.vue组件
<template><el-date-pickerstyle="width: 100%"v-model="date":type="fieldProperty.type":disabled="fieldProperty.disabled":range-separator="fieldProperty.ngeSeparator":placeholder="fieldProperty.placeholder":start-placeholder="fieldProperty.startPlaceholder":disabled-date="fieldProperty.disabledDate":end-placeholder="fieldProperty.endPlaceholder"@change="change"/>
</template>
<script lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
export default {props: {modelValue: [String, Number], // 组件绑定值content: [String, Number], // 组件绑定值property: {type: Object,default() {return {}}}},setup(props, { emit }) {const { t } = useI18n()const fieldProperty = computed(() => {return {startPlaceholder: t('Startdate'), // 提示语endPlaceholder: t('Enddate'), // 提示语placeholder: t('Selectdate'), // 提示语readonly: false, // 是否只读disabled: false, // 是否可输入ngeSeparator: t('To'), type: 'date', // year / month / date / dates / datetime / week / datetimerange / daterange / monthrangedisabledDate: (time: Date) => {const beginDateVal = props.property.EffectDateif(beginDateVal){return time.valueOf()<new Date(beginDateVal).valueOf()- 24*60*60*1000}const endDateVal = props.property.endDateif(endDateVal){return time.valueOf() > new Date(beginDateVal).valueOf()- 24*60*60*1000}},...props.property}})const date = computed({get() {return props.modelValue},set(val) {emit('update:modelValue', val)}})const change = (val: any) => {emit('fieldChange', val) // 触发}return {date,change,fieldProperty}},
}
</script>
<style lang="less" scoped></style>
10、新建time-picker.vue组件
<template><el-time-pickerstyle="width: 100%"v-model="date":is-range="fieldProperty.type":disabled="fieldProperty.disabled":range-separator="fieldProperty.ngeSeparator":placeholder="fieldProperty.placeholder":start-placeholder="fieldProperty.startPlaceholder":end-placeholder="fieldProperty.endPlaceholder"/>
</template>
<script lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
export default {props: {modelValue: [String, Number], // 组件绑定值content: [String, Number], // 组件绑定值property: {type: Object,default() {return {}}}},setup(props, { emit }) {const { t } = useI18n()const fieldProperty = computed(() => {return {startPlaceholder: t('Startdate'), // 提示语endPlaceholder: t('Enddate'), // 提示语placeholder: t('Selectdate'), // 提示语readonly: false, // 是否只读disabled: false, // 是否可输入ngeSeparator: t('To'), type: true, // true / false...props.property}})const date = computed({get() {return props.modelValue},set(val) {emit('update:modelValue', val)}})return {date,fieldProperty}},
}
</script>
<style lang="less" scoped></style>
11、新建cascader.vue组件
<template><el-cascaderv-model="value":filterable="fieldProperty.filterable":placeholder="fieldProperty.placeholder":disabled="fieldProperty.disabled":clearable="fieldProperty.clearable":options="fieldProperty.data":collapse-tags="true":collapse-tags-tooltip="true"@change="handleChange"/>
</template>
<script lang="ts">
import { computed, reactive, watch } from 'vue'
export default {props: {modelValue: Array, // 组件绑定值content: Array, // 组件绑定值property: {type: Object,default() {return {}}}},setup(props, { emit }) {const fieldProperty = computed(() => {return {placeholder: '请选择', // 提示语disabled: false, // 是否可输入clearable: true, // 是否支持清空选项filterable: true, // 是否可搜索data: [],...props.property}})const value = computed({get() {return props.modelValue},set(val) {emit('update:modelValue', val)}})const handleChange = (val) => {console.log('handleChange', val)}return {value,fieldProperty,handleChange}},
}
</script>
<style lang="less" scoped></style>
12、新建/ueditor/组件
- index.vue
<template><vue-ueditor-wrapv-model="content":config="VueUeditorWrapConfig":editor-id="editorId"></vue-ueditor-wrap>
</template>
<script>
import { default as VueUeditorWrapConfig } from "./ueditor.js";
import { RandomNumber } from '@/utils'
export default {props: {value: [String],contentDetail: {type: String,default: "",},property: {type: Object,default() {return {};},},},model: {prop: "value", // 指定 v-model 要绑定的参数叫什么名字,来自于 props 中定义的参数event: "change", // 指定要触发的事件名字,将被用于 $emit},data() {return {show: false,VueUeditorWrapConfig,isPosting: false,edit_show: true,index: 0,editorId: 'editor' + RandomNumber()};},computed: {content: {get() {console.log('10000', this.value)return this.value;},set(val) {this.$emit("change", val);},},},
};
</script>
<style lang="less" scoped>
/* 弹框输入框长度 */
.lengthWidth {width: 80%;
}.editor_if {width: 100%;height: 400px;
}.edui1 {z-index: 90 !important;
}
</style>
- ueditor.js
// let env = process.env.NODE_ENV || ''
let ueditorPath = ''
// if (env === 'development') {
// ueditorPath = '/static/UEditor/'
// } else if (env === 'production') {
// ueditorPath = '/static/UEditor/'
// } else {
// ueditorPath = '/static/UEditor/'
// }
ueditorPath = '/ueditor/'
export default {UEDITOR_HOME_URL: ueditorPath,maximumWords: 1000 * 100,// toolbars: [// [// 'source', '|', 'undo', 'redo', '|',// 'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|',// 'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',// 'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|',// 'directionalityltr', 'directionalityrtl', 'indent', '|',// 'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|',// 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|',// 'horizontal', 'date', 'time',// 'inserttable', 'simpleupload', 'preview',// ]// ],toolbars: [['source', '|', 'undo', 'redo', '|','bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|','rowspacingtop', 'rowspacingbottom', 'lineheight', '|','customstyle', 'paragraph', 'fontfamily', 'fontsize', '|','directionalityltr', 'directionalityrtl', 'indent', '|','justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|','imagenone', 'imageleft', 'imageright', 'imagecenter', '|','horizontal']],enableAutoSave: false,elementPathEnabled: false,disabledTableInTable: false,serverUrl: '' // 暂无接口 APP_CONFIG.baseURL + '/file/upload'
}
13、新建/kind-editor组件
- index.vue
<template><vue3-kind-editor:id="editorId":height="fieldProperty.height":width="fieldProperty.width"v-model="content":items="fieldProperty.items":loadStyleMode="fieldProperty.loadStyleMode"></vue3-kind-editor>
</template>
<script lang="ts" src="./index.js" />
<style lang="less" scoped>
/* 弹框输入框长度 */
.lengthWidth {width: 80%;
}.editor_if {width: 100%;height: 400px;
}.edui1 {z-index: 90 !important;
}
</style>
- index.js
// import Vue3KindEditor from '@zhj-target/vue3-kind-editor'
import { computed } from 'vue';
import Vue3KindEditor from './editor/index.vue'
import { RandomNumber } from '@/utils'
window._instances = []
export default {components: {Vue3KindEditor,},props: {value: [String],property: {type: Object,default() {return {};},},},model: {prop: "value", // 指定 v-model 要绑定的参数叫什么名字,来自于 props 中定义的参数event: "change", // 指定要触发的事件名字,将被用于 $emit},setup(props, { emit }) {const fieldProperty = computed(() => {return {height: '150px',width: '100%',loadStyleMode: false,items: ['preview', 'template', '|', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline','removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist','insertunorderedlist', '|', 'link'],...props.property}})const content = computed({get() {return props.value},set(val) {emit('update:value', val) // 触发}})const editorId = computed(() => {return 'editor' + RandomNumber()})return {editorId,content,fieldProperty}}
};
- editor/index.vue
<template><div class="kindeditor"><textarea :id="id" name="content">{{ state.outContent }}</textarea></div>
</template>
<script lang="ts" src="./index.ts" />
<style scoped>
.kindeditor {width: 100%;
}
</style>
- editor/index.js
import { onMounted, reactive, watch } from "vue"
import '@public/kind-editor/themes/default/default.css'
import '@public/kind-editor/kindeditor-all.js'
import '@public/kind-editor/lang/zh-CN.js'
import '@public/kind-editor/lang/en.js'
export default {name: 'vue3-kind-editor',props: {modelValue: {type: String,default: ''},id: {type: String,required: true},width: {type: String},height: {type: String},minWidth: {type: Number,default: 650},minHeight: {type: Number,default: 100},items: {type: Array,default: function () {return ['source', '|', 'undo', 'redo', '|', 'preview', 'print', 'code', 'cut', 'copy', 'paste','plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright','justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript','superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/','formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold','italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image','media', 'insertfile', 'table', 'hr', 'pagebreak','anchor', 'link', 'unlink',]}},noDisableItems: {type: Array,default: function () {return ['source', 'fullscreen']}},filterMode: {type: Boolean,default: true},htmlTags: {type: Object,default: function () {return {font: ['color', 'size', 'face', '.background-color'],span: ['style'],div: ['class', 'align', 'style'],table: ['class', 'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'style'],'td,th': ['class', 'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor', 'style'],a: ['class', 'href', 'target', 'name', 'style'],embed: ['src', 'width', 'height', 'type', 'loop', 'autostart', 'quality','style', 'align', 'allowscriptaccess', '/'],img: ['src', 'width', 'height', 'border', 'alt', 'title', 'align', 'style', '/'],hr: ['class', '/'],br: ['/'],'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6': ['align', 'style'],'tbody,tr,strong,b,sub,sup,em,i,u,strike': []}}},wellFormatMode: {type: Boolean,default: true},resizeType: {type: Number,default: 2},themeType: {type: String,default: 'default'},langType: {type: String,default: 'en'},designMode: {type: Boolean,default: true},fullscreenMode: {type: Boolean,default: false},basePath: {type: String},themesPath: {type: String},pluginsPath: {type: String,default: ''},langPath: {type: String},minChangeSize: {type: Number,default: 5},loadStyleMode: {type: Boolean,default: true},urlType: {type: String,default: ''},newlineTag: {type: String,default: 'p'},pasteType: {type: Number,default: 2},dialogAlignType: {type: String,default: 'page'},shadowMode: {type: Boolean,default: true},zIndex: {type: Number,default: 811213},useContextmenu: {type: Boolean,default: true},syncType: {type: String,default: 'form'},indentChar: {type: String,default: '\t'},cssPath: {type: [String, Array]},cssData: {type: String},bodyClass: {type: String,default: 'ke-content'},colorTable: {type: Array},afterCreate: {type: Function},afterChange: {type: Function},afterTab: {type: Function},afterFocus: {type: Function},afterBlur: {type: Function},afterUpload: {type: Function},uploadJson: {type: String},fileManagerJson: {type: Function},allowPreviewEmoticons: {type: Boolean,default: true},allowImageUpload: {type: Boolean,default: true},allowFlashUpload: {type: Boolean,default: true},allowMediaUpload: {type: Boolean,default: true},allowFileUpload: {type: Boolean,default: true},allowFileManager: {type: Boolean,default: false},fontSizeTable: {type: Array,default: function () {return ['9px', '10px', '12px', '14px', '16px', '18px', '24px', '32px']}},imageTabIndex: {type: Number,default: 0},formatUploadUrl: {type: Boolean,default: true},fullscreenShortcut: {type: Boolean,default: false},extraFileUploadParams: {type: Array,default: function () {return []}},filePostName: {type: String,default: 'imgFile'},fillDescAfterUploadImage: {type: Boolean,default: false},afterSelectFile: {type: Function},pagebreakHtml: {type: String,default: '<hr style=”page-break-after: always;” class=”ke-pagebreak” />'},allowImageRemote: {type: Boolean,default: true},autoHeightMode: {type: Boolean,default: false},fixToolBar: {type: Boolean,default: false},tabIndex: {type: Number}},setup(props: any, { emit }: any) {const state = reactive({editor: {} as any,outContent: props.modelValue})watch(() => props.modelValue, (val: any) => {state.editor && val !== state.outContent && state.editor.html(val)})watch(() => state.outContent, (val: any) => {emit('update:modelValue', val)})onMounted(async() => {state.editor = (window as any).KindEditor.create('#' + props.id, {width: props.width,height: props.height,minWidth: props.minWidth,minHeight: props.minHeight,items: props.items,noDisableItems: props.noDisableItems,filterMode: props.filterMode,htmlTags: props.htmlTags,wellFormatMode: props.wellFormatMode,resizeType: props.resizeType,themeType: props.themeType,langType: props.langType,designMode: props.designMode,fullscreenMode: props.fullscreenMode,basePath: props.basePath,themesPath: props.cssPath,pluginsPath: props.pluginsPath,langPath: props.langPath,minChangeSize: props.minChangeSize,loadStyleMode: props.loadStyleMode,urlType: props.urlType,newlineTag: props.newlineTag,pasteType: props.pasteType,dialogAlignType: props.dialogAlignType,shadowMode: props.shadowMode,zIndex: props.zIndex,useContextmenu: props.useContextmenu,syncType: props.syncType,indentChar: props.indentChar,cssPath: props.cssPath,cssData: props.cssData,bodyClass: props.bodyClass,colorTable: props.colorTable,afterCreate: props.afterCreate,afterChange: function () {props.afterChangestate.outContent = this.html()},afterTab: props.afterTab,afterFocus: props.afterFocus,afterBlur: props.afterBlur,afterUpload: props.afterUpload,uploadJson: props.uploadJson,fileManagerJson: props.fileManagerJson,allowPreviewEmoticons: props.allowPreviewEmoticons,allowImageUpload: props.allowImageUpload,allowFlashUpload: props.allowFlashUpload,allowMediaUpload: props.allowMediaUpload,allowFileUpload: props.allowFileUpload,allowFileManager: props.allowFileManager,fontSizeTable: props.fontSizeTable,imageTabIndex: props.imageTabIndex,formatUploadUrl: props.formatUploadUrl,fullscreenShortcut: props.fullscreenShortcut,extraFileUploadParams: props.extraFileUploadParams,filePostName: props.filePostName,fillDescAfterUploadImage: props.fillDescAfterUploadImage,afterSelectFile: props.afterSelectFile,pagebreakHtml: props.pagebreakHtml,allowImageRemote: props.allowImageRemote,autoHeightMode: props.autoHeightMode,fixToolBar: props.fixToolBar,tabIndex: props.tabIndex})})return {state}}}
二、配置的表单及使用
- 定义表单配置类
interface Rules {[key: string]: Array<Rule>;
}
interface Rule {required: boolean;message?: string;trigger: string;validator?: Function;
}
interface unKnow {[key: string]: any;
}
type DefaultFields = unKnow;
interface FormData {[key: string]: FormDataInfo;
}
interface FormDataInfo {title?: string;field?: string;colSize: number;show: boolean;type: string;class?: Array<string>;zIndex?: number;property: FieldProperty;
}
type SelectList = unKnow;
interface FieldProperty {disabled?: boolean;readonly?: boolean;placeholder?: string;maxlength?: number;label?: string;value?: string;filterable?: boolean;clearable?: boolean;data?: Array<SelectList>;multiple?: boolean;btnName?: string;activeColor?: string;inactiveColor?: string;img?: string;TitleT?: string;time?: number;type?: string | number;search?: boolean;del?: boolean;sameAsSupplier?: boolean;loading?: boolean;limit?: number;accept?: string;
}
class CancalBookingEntity {public formRules: Rules = {};public formFields: DefaultFields = {};public formData: FormData = {};constructor() {this.formFields = {Input: "",Select: "",Vhtml: "<div style='color: red'>color</div>",Upload: "",Switch: "",Radio: "0",Checkbox: true,Date: "",TimePicker: "",Cascader: "",UEditor: "",KindEditor: "",};this.formData = {Input: {type: "Input",colSize: 12,show: true,class: [],title: "Input",field: "Input",property: {type: "text",placeholder: "text",},},Reason: {type: "Select",colSize: 12,show: true,class: [],title: "Select",field: "Select",property: {data: [{label: "请选择",value: "",},{label: "Select",value: "Select",},],label: "label",value: "value",},},Vhtml: {type: "Vhtml",colSize: 12,show: true,class: [],title: "Vhtml",field: "Vhtml",property: {},},upload: {title: "upload:",field: "upload",type: "Upload",colSize: 12,show: true,property: {readonly: false,multiple: true,btnName: "Browse...",},},Switch: {title: "Switch:",field: "Switch",type: "Switch",colSize: 12,show: true,property: {readonly: false,multiple: true,btnName: "Browse...",},},Radio: {type: "Radio",colSize: 12,show: true,title: "Radio",field: "Radio",class: [],property: {data: [{ label: "男", value: "1" },{ label: "女", value: "0" },],},},Checkbox: {type: "Checkbox",colSize: 12,show: true,class: [],title: "Checkbox",field: "Checkbox",property: {label: "",},},Date: {type: "Date",colSize: 12,show: true,class: [],title: "Date",field: "Date",property: {placeholder: "Date",type: "daterange",},},TimePicker: {type: "TimePicker",colSize: 12,show: true,class: [],title: "TimePicker",field: "TimePicker",property: {placeholder: "TimePicker",},},Cascader: {type: "Cascader",colSize: 12,show: true,class: [],title: "Cascader",field: "Cascader",property: {data: [{value: "CN",label: "中国",children: [{value: "CN",label: "中国",children: [{value: "CN",label: "中国",},],},],},],},},UEditor: {type: 'UEditor',colSize: 24,show: true,class: [],title: 'UEditor',field: 'UEditor',property: {placeholder: 'UEditor',}},KindEditor: {type: 'KindEditor',colSize: 24,show: true,class: [],title: 'KindEditor',field: 'KindEditor',property: {placeholder: 'KindEditor',}},};}
}
export default CancalBookingEntity;
- 引入上传配置数据并应用form组件
<FormListclass="register-info-form"ref="FormList":fields="businessInquiry.formFields":formData="businessInquiry.formData":rules="businessInquiry.formRules"labelWidth="120px"/>
export default {setup() {const FormListRef = ref()const businessInquiry = reactive(new BusinessInquiry())const validate = throttle(() => {companyInfo.value.$refs.formData.validate((valid: boolean) => {if (valid) ...})}, 1500)return {businessInquiry,FormList,validate }},
}
以上配置完即可显示配置好的表单啦~ ,更多详细细节后续有时间慢慢加上,然后再附上案例项目