x-php-Admin/src/components/scForm/index.vue

267 lines
9.6 KiB
Vue
Raw Normal View History

2023-06-07 10:48:30 +00:00
<!--
* @Descripttion: 动态表单渲染器
-->
<template>
<el-skeleton v-if="renderLoading || Object.keys(form).length==0" animated />
<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)">
<sc-title v-if="item.component=='title'" :title="item.label"></sc-title>
2023-06-20 11:03:48 +00:00
<el-form-item v-else-if="item.component" :prop="item.name" :rules="rulesHandle(item)">
2023-06-07 10:48:30 +00:00
<template #label>
{{item.label}}
<el-tooltip v-if="item.tips" :content="item.tips">
<el-icon><el-icon-question-filled /></el-icon>
</el-tooltip>
</template>
<!-- input -->
2023-06-09 11:34:06 +00:00
<template v-if="item.component=='input'">
2023-06-07 10:48:30 +00:00
<el-input v-model="form[item.name]" :placeholder="item.options.placeholder" clearable :maxlength="item.options.maxlength" show-word-limit></el-input>
</template>
2023-06-09 11:34:06 +00:00
<!-- textarea -->
<template v-else-if="item.component=='textarea'">
<el-input v-model="form[item.name]" :placeholder="item.options.placeholder" :rows="item.options.rows" clearable :maxlength="item.options.maxlength" type="textarea" show-word-limit></el-input>
</template>
2023-06-07 10:48:30 +00:00
<!-- checkbox -->
2023-06-09 11:34:06 +00:00
<template v-else-if="item.component=='checkbox'">
2023-06-07 10:48:30 +00:00
<template v-if="item.name" >
<el-checkbox v-model="form[item.name][_item.name]" :label="_item.label" v-for="(_item, _index) in item.options.items" :key="_index"></el-checkbox>
</template>
<template v-else >
<el-checkbox v-model="form[_item.name]" :label="_item.label" v-for="(_item, _index) in item.options.items" :key="_index"></el-checkbox>
</template>
</template>
<!-- checkboxGroup -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='checkboxGroup'">
2023-06-07 10:48:30 +00:00
<el-checkbox-group v-model="form[item.name]">
<el-checkbox v-for="_item in item.options.items" :key="_item.value" :label="_item.value">{{_item.label}}</el-checkbox>
</el-checkbox-group>
</template>
<!-- upload -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='upload'">
2023-06-20 15:44:48 +00:00
<sc-upload v-model="form[item.name]" :cropper="item.options.cropper || false" :compress="1" :aspectRatio="item.options.aspectRatio || 1/1" :width="item.options.width || 148" :height="item.options.height || 148"></sc-upload>
2023-06-07 10:48:30 +00:00
</template>
2023-06-26 11:06:05 +00:00
<!-- updatemultiple -->
<template v-else-if="item.component=='updatemultiple'">
<sc-upload-multiple v-model="form[item.name]" draggable :width="item.options.width || 120" :height="item.options.height || 90" :limit="item.options.limit || 20" :tip="item.options.tip ||''"></sc-upload-multiple>
</template>
2023-06-07 10:48:30 +00:00
<!-- switch -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='switch'">
2023-06-12 11:22:59 +00:00
<el-switch v-model="form[item.name]" :active-value="item.options.activevalue || 1" :inactive-value="item.options.inactivevalue || 0" />
2023-06-07 10:48:30 +00:00
</template>
<!-- select -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='select'">
2023-06-07 10:48:30 +00:00
<el-select v-model="form[item.name]" :multiple="item.options.multiple" :placeholder="item.options.placeholder" clearable filterable style="width: 100%;">
<el-option v-for="option in item.options.items" :key="option.value" :label="option.label" :value="option.value"></el-option>
</el-select>
</template>
<!-- cascader -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='cascader'">
2023-06-07 10:48:30 +00:00
<el-cascader v-model="form[item.name]" :options="item.options.items" clearable></el-cascader>
</template>
<!-- date -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='date'">
2023-06-07 10:48:30 +00:00
<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>
</template>
<!-- number -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='number'">
2023-06-07 10:48:30 +00:00
<el-input-number v-model="form[item.name]" controls-position="right"></el-input-number>
</template>
<!-- radio -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='radio'">
2023-06-07 10:48:30 +00:00
<el-radio-group v-model="form[item.name]">
<el-radio v-for="_item in item.options.items" :key="_item.value" :label="_item.value">{{_item.label}}</el-radio>
</el-radio-group>
</template>
<!-- color -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='color'">
2023-06-07 10:48:30 +00:00
<el-color-picker v-model="form[item.name]" />
</template>
<!-- rate -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='rate'">
2023-06-07 10:48:30 +00:00
<el-rate style="margin-top: 6px;" v-model="form[item.name]"></el-rate>
</template>
<!-- slider -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='slider'">
2023-06-07 10:48:30 +00:00
<el-slider v-model="form[item.name]" :marks="item.options.marks"></el-slider>
</template>
<!-- tableselect -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='tableselect'">
2023-06-07 10:48:30 +00:00
<tableselect-render v-model="form[item.name]" :item="item"></tableselect-render>
</template>
<!-- editor -->
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='editor'">
2023-06-07 10:48:30 +00:00
<sc-editor v-model="form[item.name]" placeholder="请输入" :height="400"></sc-editor>
</template>
2023-06-20 11:03:48 +00:00
<template v-else-if="item.component=='text'">
2023-06-26 11:06:05 +00:00
<el-link type="info" :underline="false">{{ form[item.name] }}</el-link>
</template>
<template v-else-if="item.component=='avatar'">
<el-avatar :src="form[item.name]" size="small"></el-avatar>
<el-link v-if="item.options.subfield" type="info" style="padding: 0 10px;" :underline="false">{{ form[item.options.subfield] }}</el-link>
2023-06-25 11:06:43 +00:00
</template>
<template v-else-if="item.component=='formtable'">
<sc-form-table drag-sort="" placeholder="暂无数据" ref="videostable" :addTemplate="item.options.addTemplate || {}" :column="item.options.column || []" v-model="form[item.name]" :hideAdd="item.options.hideAdd || true" :hideDelete="item.options.hideDelete || true">
</sc-form-table>
2023-06-20 11:03:48 +00:00
</template>
2023-06-07 10:48:30 +00:00
<!-- noComponent -->
<template v-else>
<el-tag type="danger">[{{item.component}}] Component not found</el-tag>
</template>
<div v-if="item.message" class="el-form-item-msg">{{item.message}}</div>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<script>
import http from "@/utils/request"
import { defineAsyncComponent } from 'vue'
const tableselectRender = defineAsyncComponent(() => import('./items/tableselect'))
const scEditor = defineAsyncComponent(() => import('@/components/scEditor'))
export default {
props: {
modelValue: { type: Object, default: () => {} },
config: { type: Object, default: () => {} },
loading: { type: Boolean, default: false },
},
components: {
tableselectRender,
scEditor
},
data() {
return {
form: {},
renderLoading: false
}
},
watch:{
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() {
if(this.hasConfig){
this.render()
}
},
methods: {
//构建form对象
render() {
this.config.formItems.forEach((item) => {
2023-06-25 11:06:43 +00:00
item.options = item.options?item.options:[];
2023-06-07 10:48:30 +00:00
if(item.component == 'checkbox'){
if(item.name){
const value = {}
item.options.items.forEach((option) => {
value[option.name] = option.value
})
this.form[item.name] = value
}else{
item.options.items.forEach((option) => {
this.form[option.name] = option.value
})
}
}else{
this.form[item.name] = item.value
}
})
2023-06-25 11:06:43 +00:00
2023-06-07 10:48:30 +00:00
if(this.hasValue){
this.form = this.deepMerge(this.form, this.modelValue)
}
this.getData()
},
//处理远程选项数据
getData() {
this.renderLoading = true
var remoteData = []
this.config.formItems.forEach((item) => {
if(item.options && item.options.remote){
var req = http.get(item.options.remote.api, item.options.remote.data).then(res=>{
item.options.items = res.data
})
remoteData.push(req)
}
})
Promise.all(remoteData).then(()=>{
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
//return JSON.parse(JSON.stringify(obj1))
},
//处理动态隐藏
hideHandle(item){
if(item.hideHandle){
const exp = eval(item.hideHandle.replace(/\$/g,"this.form"))
return exp
}
return false
},
//处理动态必填
rulesHandle(item){
if(item.requiredHandle){
const exp = eval(item.requiredHandle.replace(/\$/g,"this.form"))
var requiredRule = item.rules.find(t => 'required' in t)
requiredRule.required = exp
}
return item.rules
},
//数据验证
validate(valid, obj){
return this.$refs.form.validate(valid, obj)
},
scrollToField(prop){
return this.$refs.form.scrollToField(prop)
},
resetFields(){
return this.$refs.form.resetFields()
},
//提交
submit(){
this.$emit("submit", this.form)
}
}
}
</script>
<style>
</style>