This commit is contained in:
sc 2021-09-29 16:59:52 +08:00
parent 44841f38e0
commit 231bbf3c82
7 changed files with 395 additions and 611 deletions

View File

@ -8,9 +8,9 @@
-->
<template>
<el-skeleton v-if="loading || Object.keys(form).length==0" animated />
<el-skeleton v-if="renderLoading || Object.keys(form).length==0" animated />
<el-form v-else ref="form" :model="form" label-width="130px" :label-position="config.labelPosition">
<el-form v-else ref="form" :model="form" :label-width="config.labelWidth" :label-position="config.labelPosition" v-loading="loading" element-loading-text="Loading...">
<el-row :gutter="15">
<template v-for="(item, index) in config.formItems" :key="index">
<el-col :span="item.span || 24" v-if="!hideHandle(item)">
@ -52,6 +52,10 @@
<el-option v-for="option in item.options.items" :key="option.value" :label="option.label" :value="option.value"></el-option>
</el-select>
</template>
<!-- cascader -->
<template v-else-if="item.component=='cascader'" >
<el-cascader v-model="form[item.name]" :options="item.options.items" clearable></el-cascader>
</template>
<!-- date -->
<template v-else-if="item.component=='date'" >
<el-date-picker v-model="form[item.name]" :type="item.options.type" :shortcuts="item.options.shortcuts" :default-time="item.options.defaultTime" :value-format="item.options.valueFormat" :placeholder="item.options.placeholder || '请选择'"></el-date-picker>
@ -66,29 +70,31 @@
<el-radio v-for="_item in item.options.items" :key="_item.value" :label="_item.value">{{_item.label}}</el-radio>
</el-radio-group>
</template>
<!-- color -->
<template v-else-if="item.component=='color'" >
<el-color-picker v-model="form[item.name]" />
</template>
<!-- rate -->
<template v-else-if="item.component=='rate'" >
<el-rate style="margin-top: 6px;" v-model="form[item.name]"></el-rate>
</template>
<!-- slider -->
<template v-else-if="item.component=='slider'" >
<el-slider v-model="form[item.name]" :marks="item.options.marks"></el-slider>
</template>
<!-- noComponent -->
<template v-else>
未匹配到相应组件 {{item.component}}
</template>
<div v-if="item.message" class="el-form-item-msg">{{item.message}}</div>
</el-form-item>
</el-col>
</template>
<!-- <template v-for="(item, index) in config.items" :key="index">
<el-col v-if="!hideHandle(item)" :span="item.span || 24">
<el-form-item v-if="item.name" :label="item.label" :prop="item.name" :rules="rulesHandle(item)">
<component :is="`${item.component}-render`" v-model="form[item.name]" :item="item"></component>
</el-form-item>
<el-form-item v-else :label="item.label" :rules="rulesHandle(item)">
<component v-for="(_item, _index) in item.options.items" :key="_index" :is="`${item.component}-render`" v-model="form[_item.name]" :item="_item"></component>
</el-form-item>
</el-col>
</template> -->
<el-col :span="24">
<el-form-item>
<el-button type="primary" @click="submit">提交</el-button>
<el-button>取消</el-button>
<slot>
<el-button type="primary" @click="submit">提交</el-button>
</slot>
</el-form-item>
</el-col>
</el-row>
@ -99,49 +105,58 @@
import http from "@/utils/request"
import { defineAsyncComponent } from 'vue';
const inputRender = defineAsyncComponent(() => import('./items/input'));
//import inputRender from './items/input'
import selectRender from './items/select'
import checkboxRender from './items/checkbox'
import checkboxGroupRender from './items/checkboxGroup'
import switchRender from './items/switch'
import uploadRender from './items/upload'
const uploadRender = defineAsyncComponent(() => import('./items/upload'));
export default {
props: {
modelValue: { type: Object, default: () => {} },
config: { type: Object, default: () => {} }
config: { type: Object, default: () => {} },
loading: { type: Boolean, default: false },
},
components: {
inputRender,
selectRender,
checkboxRender,
checkboxGroupRender,
switchRender,
uploadRender
},
data() {
return {
form: {},
loading: false
renderLoading: false
}
},
watch:{
form(val){
this.$emit("update:modelValue", val)
modelValue(){
if(this.hasConfig){
this.deepMerge(this.form, this.modelValue)
}
},
config(){
this.render()
},
form:{
handler(val){
this.$emit("update:modelValue", val)
},
deep: true
}
},
computed: {
hasConfig(){
return Object.keys(this.config).length>0
},
hasValue(){
return Object.keys(this.modelValue).length>0
}
},
created() {
},
mounted() {
this.setForm()
this.getData()
if(this.hasConfig){
this.render()
}
},
methods: {
//form
setForm(){
render() {
this.config.formItems.forEach((item) => {
if(item.component == 'checkbox'){
if(item.name){
@ -163,11 +178,14 @@
this.form[item.name] = item.value
}
})
this.form = this.deepMerge(this.form, this.modelValue)
//this.form = Object.assign({}, this.form, this.modelValue)
if(this.hasValue){
this.form = this.deepMerge(this.form, this.modelValue)
}
this.getData()
},
//
getData() {
this.loading = true
this.renderLoading = true
var remoteData = []
this.config.formItems.forEach((item) => {
if(item.options && item.options.remote){
@ -178,19 +196,19 @@
}
})
Promise.all(remoteData).then(()=>{
this.loading = false
this.renderLoading = false
})
},
//
deepMerge(obj1, obj2) {
let key;
for (key in obj2) {
obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" && (obj2[key] && obj2[key].toString() === "[object Object]") ? this.deepMerge(obj1[key], obj2[key]) : (obj1[key] = obj2[key])
}
return {...obj1};
},
updata(val, item){
console.log(val, item)
return obj1
//return JSON.parse(JSON.stringify(obj1))
},
//
hideHandle(item){
if(item.hideHandle){
const exp = eval(item.hideHandle.replace(/\$/g,"this.form"))
@ -198,6 +216,7 @@
}
return false
},
//
rulesHandle(item){
if(item.requiredHandle){
const exp = eval(item.requiredHandle.replace(/\$/g,"this.form"))
@ -206,15 +225,16 @@
}
return item.rules
},
//
validate(valid, obj){
return this.$refs.form.validate(valid, obj)
},
resetFields(){
return this.$refs.form.resetFields()
},
//
submit(){
this.$refs.form.validate((valid) => {
if (valid) {
console.log(this.form)
alert('submit!')
}else{
return false
}
})
this.$emit("submit", this.form)
}
}
}

View File

@ -1,35 +0,0 @@
<template>
<el-checkbox v-model="value" :label="item.label"></el-checkbox>
</template>
<script>
export default {
name: 'checkboxGroupRender',
props: {
modelValue: [String, Number, Boolean, Date, Object, Array],
item: { type: Object, default: () => {} }
},
data() {
return {
value: this.modelValue
}
},
watch:{
modelValue(val){
this.value = val
},
value(val){
this.$emit("update:modelValue", val)
}
},
mounted() {
},
methods: {
}
}
</script>
<style>
</style>

View File

@ -1,37 +0,0 @@
<template>
<el-checkbox-group v-model="value">
<el-checkbox v-for="item in item.options.items" :key="item.value" :label="item.value">{{item.label}}</el-checkbox>
</el-checkbox-group>
</template>
<script>
export default {
name: 'checkboxGroupRender',
props: {
modelValue: [String, Number, Boolean, Date, Object, Array],
item: { type: Object, default: () => {} }
},
data() {
return {
value: this.modelValue || []
}
},
watch:{
modelValue(val){
this.value = val
},
value(val){
this.$emit("update:modelValue", val)
}
},
mounted() {
},
methods: {
}
}
</script>
<style>
</style>

View File

@ -1,35 +0,0 @@
<template>
<el-input v-model="value" :placeholder="item.options.placeholder"></el-input>
</template>
<script>
export default {
name: 'inputRender',
props: {
modelValue: [String, Number, Boolean, Date, Object, Array],
item: { type: Object, default: () => {} }
},
data() {
return {
value: this.modelValue
}
},
watch:{
modelValue(val){
this.value = val
},
value(val){
this.$emit("update:modelValue", val)
}
},
mounted() {
},
methods: {
}
}
</script>
<style>
</style>

View File

@ -1,39 +0,0 @@
<template>
<el-form-item :label="item.label" :rules="item.options.rules">
<el-select v-model="value" :multiple="item.options.multiple" :placeholder="item.options.placeholder" style="width: 100%;">
<el-option v-for="option in item.options.options" :key="option.value" :label="option.label" :value="option.value"></el-option>
</el-select>
</el-form-item>
</template>
<script>
export default {
name: 'selectRender',
props: {
modelValue: [String, Number, Boolean, Date, Object, Array],
item: { type: Object, default: () => {} }
},
data() {
return {
value: this.modelValue
}
},
watch:{
modelValue(val){
this.value = val
},
value(val){
this.$emit("update:modelValue", val)
}
},
mounted() {
},
methods: {
}
}
</script>
<style>
</style>

View File

@ -1,36 +0,0 @@
<template>
<el-switch v-model="value" />
<div v-if="item.message" class="el-form-item-msg">{{item.message}}</div>
</template>
<script>
export default {
name: 'switchRender',
props: {
modelValue: [String, Number, Boolean, Date, Object, Array],
item: { type: Object, default: () => {} }
},
data() {
return {
value: this.modelValue
}
},
watch:{
modelValue(val){
this.value = val
},
value(val){
this.$emit("update:modelValue", val)
}
},
mounted() {
},
methods: {
}
}
</script>
<style>
</style>

View File

@ -2,34 +2,30 @@
<el-main>
<el-row :gutter="15">
<el-col :lg="6">
<el-card shadow="never" header="1.参数配置">
<pre>{{config3}}</pre>
<el-card shadow="never" header="处理逻辑说明">
<el-timeline>
<el-timeline-item timestamp="两者数据并无顺序要求,只是获取表单值时需要自行设置loading">1.远程获取表单配置参数和表单的值</el-timeline-item>
<el-timeline-item timestamp="构建form对象, 循环渲染对应组件, 获取远程选项数据. 完成前组件将由骨架代替">2.根据配置构建表单组件</el-timeline-item>
<el-timeline-item timestamp="合并form对象和表单值">3.给表单赋值</el-timeline-item>
</el-timeline>
<el-alert title="当前版本为预览版本,并未在正式项目中实施,请谨慎使用. 更多配置以及图形化编辑器正在路上." type="warning" show-icon></el-alert>
</el-card>
<el-card shadow="never" header="双向绑定">
<el-button type="primary" @click="setName">改变输入框的值</el-button>
<el-button type="primary" @click="setConfig">改变配置labelPosition</el-button>
</el-card>
</el-col>
<el-col :lg="12">
<el-card shadow="never" header="2.渲染器">
<sc-form :config="config3" v-model="data2"></sc-form>
</el-card>
<el-card shadow="never" header="" v-if="false">
<el-form ref="formref" :model="d" label-width="100px" label-position="right" :rules="rules">
<h3 class="form-title">基础信息</h3>
<el-form-item label="活动名称" prop="name">
<el-input v-model="d.name"></el-input>
</el-form-item>
<h3 class="form-title">扩展信息</h3>
<el-form-item label="活动名称2" prop="name2">
<el-input v-model="d.name2"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">立即创建</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
<el-card shadow="never" header="动态表单渲染器">
<sc-form ref="formref" :config="config" v-model="form" :loading="loading">
<el-button type="primary" @click="save"> </el-button>
<el-button @click="reset"> </el-button>
</sc-form>
</el-card>
</el-col>
<el-col :lg="6">
<el-card shadow="never" header="3.表单输出">
<pre>{{data2}}</pre>
<el-card shadow="never" header="表单输出">
<pre>{{form}}</pre>
</el-card>
</el-col>
</el-row>
@ -46,372 +42,322 @@
},
data() {
return {
d: {
name: "",
name2: ""
},
rules: {
name2: [
{ required: true, message: '请输入活动名称', trigger: 'blur' }
]
},
data: {},
data2: {
name: "Activity name",
checkbox: {
option1: true
},
checkboxGroup: ["option1"],
select: ["1"],
select2: "1"
},
config2: {},
config3: {
labelWidth: '130px',
labelPosition: 'right',
size: 'medium',
formItems: [
{
label: "Activity name",
message: "123",
name: "name",
value: "123",
component: "input",
options: {
maxlength: "20",
placeholder: "Activity name",
},
rules: [
{required: true, message: "Please input Activity name", trigger: "blur"}
],
requiredHandle: "$.required==true",
},
{
label: "Select",
name: "select",
value: "",
component: "select",
span: 12,
options: {
remote: {
api: '/api/system/dic/get',
data: {name: 'a'}
},
multiple: true,
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
},
rules: [
{required: true, message: "Please input Activity name", trigger: "change"}
],
requiredHandle: "$.required==true",
},
{
label: "Select2",
name: "select2",
value: "",
component: "select",
span: 12,
options: {
remote: {
api: '/api/system/dic/get',
data: {name: 'b'}
},
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
}
},
{
label: "Checkbox",
name: "checkbox",
component: "checkbox",
span: 12,
options: {
items:[
{
label: "选项1",
name: "option1",
value: false
},
{
label: "选项2",
name: "option2",
value: false
}
]
}
},
{
label: "Checkbox group",
name: "checkboxGroup",
value: [],
component: "checkboxGroup",
span: 12,
options: {
items:[
{
label: "选项1",
value: "option1"
},
{
label: "选项2",
value: "option2"
}
]
}
},
{
label: "Required handle",
name: "required",
value: false,
component: "switch",
},
{
label: "Upload",
component: "upload",
options: {
items:[
{
label: "图像1",
name: "img1",
value: ""
},
{
label: "图像2",
name: "img2",
value: ""
}
]
},
hideHandle: "$.required==true"
},
{
label: "Date",
name: "date",
value: "",
component: "date",
options: {
type: "datetime",
valueFormat: "YYYY-MM-DD HH:mm:ss",
defaultTime: new Date(2000, 1, 1, 12, 0, 0),
shortcuts: [
{
text: '今天',
value: new Date(),
}
]
},
rules: [
{required: true, message: "Please input Data", trigger: "change"}
],
},
{
label: "Number",
name: "number",
value: 0,
component: "number",
},
{
label: "Radio",
name: "radio",
value: "1",
component: "radio",
options: {
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
}
},
]
},
config: {
labelWidth: '130px',
labelPosition: 'right',
size: 'medium',
items: [
{
label: "Required handle",
name: "required",
component: "switch",
span: 24,
},
{
label: "Hide handle",
name: "hide",
component: "switch",
span: 24,
},
{
label: "Activity name",
name: "name",
component: "input",
span: 24,
options: {
placeholder: "Activity name",
},
rules: [
{required: true, message: "Please input Activity name", trigger: "blur"}
],
requiredHandle: "$.required==true",
hideHandle: "$.hide==true"
},
{
label: "Checkbox",
name: "checkbox",
component: "checkbox",
span: 24,
options: {
items:[
{
label: "选项1",
value: "option1"
},
{
label: "选项2",
value: "option2"
}
]
}
},
{
label: "Checkbox group",
name: "checkboxGroup",
component: "checkboxGroup",
span: 24,
options: {
items:[
{
label: "选项1",
value: "option1"
},
{
label: "选项2",
value: "option2"
}
]
}
},
// {
// label: "",
// name: "type",
// component: "select",
// span: 12,
// options: {
// defaultValue: "",
// placeholder: "",
// options: [
// {
// label: '',
// value: '1',
// },
// {
// label: '',
// value: '2',
// }
// ],
// rules: [
// {
// required: true,
// message: "",
// trigger: "blur"
// }
// ]
// },
// dynamicHide: "$.switch==true"
// },
// {
// label: "",
// name: "type2",
// component: "select",
// span: 12,
// options: {
// multiple: true,
// defaultValue: "",
// placeholder: "",
// options: [
// {
// label: '',
// value: '1',
// },
// {
// label: '',
// value: '2',
// }
// ],
// rules: [
// {
// required: true,
// message: "",
// trigger: "blur"
// }
// ]
// },
// dynamicHide: "$.switch==true",
// },
// {
// label: "",
// name: "checkbox",
// component: "checkbox",
// span: 12,
// options: {
// items: [
// {
// label: "1",
// value: "1"
// },
// {
// label: "2",
// value: "2"
// }
// ]
// },
// dynamicHide: "$.switch==true"
// }
]
}
loading: false,
config: {},
form: {}
}
},
mounted() {
// setTimeout(()=>{
// this.config2 = this.config3
// },1000)
// setTimeout(()=>{
// this.data = this.data2
// },1500)
//, form
const config = {
labelWidth: '130px',
labelPosition: 'right',
size: 'medium',
formItems: [
{
label: "输入框",
name: "name",
value: "",
component: "input",
options: {
maxlength: "20",
placeholder: "Activity name",
},
rules: [
{required: true, message: "Please input Activity name", trigger: "blur"}
],
requiredHandle: "$.required==true",
},
{
label: "栅格(12/24)",
name: "name2",
value: "",
component: "input",
span: 12,
options: {
placeholder: "span: 12",
}
},
{
label: "栅格(12/24)",
name: "name3",
value: "",
component: "input",
span: 12,
options: {
placeholder: "span: 12",
}
},
{
label: "选择器(多选)",
name: "select",
value: "",
component: "select",
span: 24,
options: {
remote: {
api: '/api/system/dic/get',
data: {name: 'a'}
},
multiple: true,
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
},
rules: [
{required: true, message: "Please input Activity name", trigger: "change"}
],
requiredHandle: "$.required==true",
},
{
label: "选择器(单选)",
name: "select2",
value: "",
component: "select",
span: 24,
options: {
remote: {
api: '/api/system/dic/get',
data: {name: 'b'}
},
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
}
},
{
label: "级联选择器",
name: "cascader",
value: "",
component: "cascader",
span: 24,
options: {
items:[
{
label: "Guide",
value: "guide",
children: [
{
label: "Disciplines",
value: "disciplines"
},
{
label: "Consistency",
value: "consistency"
},
]
},
{
label: "Resource",
value: "resource",
children: [
{
label: "Axure Components",
value: "axure"
},
{
label: "Sketch Templates",
value: "sketch"
},
{
label: "Design Documentation",
value: "docs"
}
]
},
{
label: "Component",
value: "component"
},
]
}
},
{
label: "多选框",
name: "checkbox",
component: "checkbox",
span: 24,
options: {
items:[
{
label: "选项1",
name: "option1",
value: false
},
{
label: "选项2",
name: "option2",
value: false
}
]
}
},
{
label: "多选框组",
name: "checkboxGroup",
value: [],
component: "checkboxGroup",
span: 24,
options: {
items:[
{
label: "选项1",
value: "option1"
},
{
label: "选项2",
value: "option2"
}
]
}
},
{
label: "单选",
name: "radio",
value: "1",
component: "radio",
options: {
items:[
{
label: "选项1",
value: "1"
},
{
label: "选项2",
value: "2"
}
]
}
},
{
label: "开关",
name: "required",
message: "演示如何使用表达式动态显隐和必填,试试打开和关闭开关",
value: false,
component: "switch",
},
{
label: "上传",
component: "upload",
options: {
items:[
{
label: "图像1",
name: "img1",
value: ""
},
{
label: "图像2",
name: "img2",
value: ""
}
]
},
hideHandle: "$.required==true"
},
{
label: "日期/时间",
name: "date",
value: "",
component: "date",
options: {
type: "datetime",
valueFormat: "YYYY-MM-DD HH:mm:ss",
},
rules: [
{required: true, message: "Please input Data", trigger: "change"}
],
},
{
label: "滑块",
name: "slider",
value: 0,
component: "slider",
options: {
marks: {
0: '0°C',
8: '8°C',
37: '37°C'
}
}
},
{
label: "数值",
name: "number",
value: 0,
component: "number",
},
{
label: "颜色",
name: "color",
value: "",
component: "color",
},
{
label: "评分",
name: "rate",
value: 0,
component: "rate",
},
]
}
const form = {
name: "Activity name",
checkbox: {
option1: true
},
checkboxGroup: ["option1"],
select: ["1"],
select2: "1",
cascader: ['resource','docs']
}
setTimeout(()=>{
this.config = config
},500)
this.loading = true
setTimeout(()=>{
this.form = form
this.loading = false
},1500)
},
methods: {
onSubmit(){
this.$refs.formref.validate((valid, obj) => {
console.log(obj)
setName(){
this.form.name = "New Title"
},
setConfig(){
this.config.formItems[1].span = this.config.formItems[1].span==24?12:24
this.config.formItems[2].span = this.config.formItems[2].span==24?12:24
},
save(){
this.$refs.formref.validate((valid) => {
if (valid) {
console.log(this.form)
alert('submit!')
}else{
return false
}
})
},
reset(){
this.$refs.formref.resetFields()
}
}
}