el-form動(dòng)態(tài)表單

效果

image.png

組件使用

<sd-form ref="formref" :config="config" size="mini" border v-model="formData">
    <!-- 具名插槽 -->
    <template #testSlot>
        <el-input v-model="formData.slotName" placeholder="這是自定義表單"></el-input>
    </template>
    <el-button type="primary" @click="formSave">保 存</el-button>
</sd-form>

數(shù)據(jù)校驗(yàn)

// 測試保存
        formSave() {
            this.$refs.formref.validate((valid) => {
                if(valid) {
                    console.log(this.formData)
                }
            })
        }

參數(shù)結(jié)構(gòu)

// config為表單數(shù)據(jù)
// formData為綁定數(shù)據(jù)結(jié)構(gòu)
created() {
        this.config = {
            labelWidth: '120px',  //  label寬度
            labelPosition: 'right', // label對(duì)齊方式
            size: 'medium', //  表單尺寸
            formItems: [ // 表單元素
                {
                    label: "自定義表單", //表單名稱
                    name: "slotName", // formData綁定的key
                    span: 8, // el-col的span
                    slotName: 'testSlot', // 具名插槽
                    rules: [ // 校驗(yàn)規(guī)則
                        {required: true, message: "Please input Activity name", trigger: "blur"}
                    ],
                },
                {
                    label: "輸入框",
                    name: "name",
                    component: "input", // 表單類型
                    span: 8,
                    options: {
                        maxlength: "20",
                        placeholder: "Activity name",
                    },
                    rules: [
                        {required: true, message: "Please input Activity name", trigger: "blur"}
                    ],
                    requiredHandle: "$.required==true", // 是否需要校驗(yàn)
                },
                {
                    label: "柵格(12/24)",
                    name: "name2",
                    component: "input",
                    span: 8,
                    options: {
                        placeholder: "span: 12",
                    }
                },
                {
                    label: "柵格(12/24)",
                    name: "name3",
                    component: "input",
                    span: 24,
                    options: {
                        placeholder: "span: 12",
                    }
                },
                {
                    label: "級(jí)聯(lián)選擇器",
                    name: "cascader",
                    component: "cascader",
                    span: 12,
                    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: 12,
                    tips: "多選框配置加上 name 表示擁有嵌套關(guān)系。否則將值“平鋪”在form對(duì)象",
                    options: { // 表單具體屬性
                        items:[
                            {
                                label: "選項(xiàng)1",
                                name: "option1"
                            },
                            {
                                label: "選項(xiàng)2",
                                name: "option2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true"
                },
                {
                    label: "多選框組",
                    name: "checkboxGroup",
                    component: "checkboxGroup",
                    span: 24,
                    options: {
                        items:[
                            {
                                label: "選項(xiàng)1",
                                value: "option1"
                            },
                            {
                                label: "選項(xiàng)2",
                                value: "option2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true" // 動(dòng)態(tài)顯示隱藏此表單
                },
                {
                    label: "單選",
                    name: "radio",
                    component: "radio",
                    options: {
                        items:[
                            {
                                label: "選項(xiàng)1",
                                value: "1"
                            },
                            {
                                label: "選項(xiàng)2",
                                value: "2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true"
                },
                {
                    label: "開關(guān)",
                    name: "required",
                    span: 12,
                    message: "演示如何使用表達(dá)式動(dòng)態(tài)顯隱和必填,試試打開和關(guān)閉開關(guān)",
                    component: "switch",
                },
                {
                    label: "日期/時(shí)間",
                    name: "date",
                    span: 12,
                    component: "date",
                    options: {
                        type: "datetime",
                        valueFormat: "yyyy-MM-dd HH:mm:ss",
                    },
                    rules: [
                        {required: true, message: "Please input Data", trigger: "change"}
                    ],
                },
                {
                    label: "數(shù)值",
                    name: "number",
                    component: "number",
                },
                {
                    label: "顏色",
                    name: "color",
                    component: "color",
                },
                {
                    label: "評(píng)分",
                    name: "rate",
                    component: "rate",
                }
            ]
        }
        this.formData = {
            slotName: '自定義表單',
            name: '',
            name2: '',
            name3: '',
            cascader: '',
            checkbox: {},
            checkboxGroup: [],
            radio: '1',
            required: false,
            date: '',
            slider: 8,
            number: 0,
            color: '',
            rate: 0
        }
    }

組件封裝

<template>
    <el-form class="sd-form" :class="border && 'sd-form-border'" ref="form" :model="form" :size="size" :label-width="config.labelWidth" :label-position="config.labelPosition" v-loading="loading" element-loading-text="Loading...">
        <el-row>
            <template v-for="(item, index) in config.formItems">
                <el-col :span="item.span || 24" v-if="!hideHandle(item)" :key="index">
                    <sd-title  v-if="item.component=='title'"  :title="item.label"></sd-title>
                    <el-form-item v-else :prop="item.name" :rules="rulesHandle(item)">
                        <template #label>
                            <span ref="refFormItem" class="refFormItem">{{item.label}}</span>
                            <el-tooltip v-if="item.tips" :content="item.tips">
                                <el-icon><el-icon-question-filled /></el-icon>
                            </el-tooltip>
                        </template>
                        <template v-if="item.slotName">
                            <slot :name="item.slotName"></slot>
                        </template>
                        <!-- input -->
                        <template v-else-if="item.component=='input'" >
                            <el-input v-model="form[item.name]" :placeholder="item.options.placeholder" clearable :maxlength="item.options.maxlength" show-word-limit></el-input>
                        </template>
                        <!-- checkbox -->
                        <template v-else-if="item.component=='checkbox'" >
                            <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 -->
                        <template v-else-if="item.component=='checkboxGroup'" >
                            <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>
                        <!-- switch -->
                        <template v-else-if="item.component=='switch'" >
                            <el-switch v-model="form[item.name]" />
                        </template>
                        <!-- select -->
                        <template v-else-if="item.component=='select'" >
                            <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 -->
                        <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 || '請(qǐng)選擇'"></el-date-picker>
                        </template>
                        <!-- number -->
                        <template v-else-if="item.component=='number'" >
                            <el-input-number v-model="form[item.name]" controls-position="right"></el-input-number>
                        </template>
                        <!-- radio -->
                        <template v-else-if="item.component=='radio'" >
                            <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 -->
                        <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>
                            <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-col :span="24">
                <el-form-item>
                    <slot>
                        <el-button type="primary" @click="submit">提交</el-button>
                    </slot>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
</template>

<script>
import http from "@/utils/request"
import SdTitle from './components/scTitle.vue'
export default {
    props: {
        modelValue: { type: Object, default: () => {} },
        config: { type: Object, default: () => {} },
        loading: { type: Boolean, default: false },
        size: { type: String, default: 'medium' },
        border: { type: Boolean, default: false },
    },
    data() {
        return {
            form: {}
        }
    },
    components: {
        SdTitle
    },
    // 給modelValue綁定事件
    model: {
        prop: 'modelValue',
        event: 'getValue'
    },
    watch:{
        // 監(jiān)聽modelValue變化復(fù)制給當(dāng)前綁定元素實(shí)現(xiàn)綁定
        modelValue: {
            immediate: true,
            handler(val) {
                this.form = val;
            }
        },
        form:{
            handler(val){
                this.$emit("getValue", val)
            },
            deep: true,
            immediate: true
        }
    },
    computed: {
        hasConfig(){
            return Object.keys(this.config).length>0
        },
        hasValue(){
            return Object.keys(this.modelValue).length>0
        }
    },
    created() {
    
    },
    mounted() {
        this.getHeight()
    },
    methods: {
        // 初始化label高度
        getHeight() {
            this.$nextTick(() => {
                if(this.$refs.refFormItem) {
                    this.$refs.refFormItem.forEach((item, index) => {
                        const height = item.parentNode.parentNode.offsetHeight
                        document.querySelectorAll('.refFormItem')[index].style.height = height + 'px'
                        // this.$forceUpdate()
                    })
                }   
            })
        },
        //處理遠(yuǎn)程選項(xiàng)數(shù)據(jù)
        getData() {
            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)
                }
            })
        },
        //合并深結(jié)構(gòu)對(duì)象
        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))
        },
        //處理動(dòng)態(tài)隱藏
        hideHandle(item){
            if(item.hideHandle){
                const exp = eval(item.hideHandle.replace(/\$/g,"this.form"))
                return exp
            }
            return false
        },
        //處理動(dòng)態(tài)必填
        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
        },
        //數(shù)據(jù)驗(yàn)證
        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 lang="scss">
.sd-form {
    .el-row {
        display: flex;
        flex-wrap: wrap;
        .el-col {
            padding: 5px !important;
            box-sizing: border-box;
        }
    }
    .el-form-item {
        margin: 0;
        display: flex;
        align-items: center;
        width: 100% !important;
        height: 100%;
        .el-form-item__label {
            background: #EAF2FF;
            display: flex;
            justify-content: flex-end;
            align-items: center;
            span {
                display: flex;
                align-items: center;
                justify-content: flex-end;
            }
        }
        .el-form-item__content {
            margin: 0 !important;
            padding: 0 5px;
            box-sizing: border-box;
            flex: 1;
        }
    }
}
.sd-form-border {
    .el-row {
        border: 1px solid #e5e5e5;
        .el-col {
            border-right: 1px solid #e5e5e5;
            border-bottom: 1px solid #e5e5e5;
        }
    }
}
</style>

npm使用

npm i web-bing@0.1.4

// main.js 
import componentsBing from 'web-bing'
import 'web-bing/dist/web-bing.css'
Vue.use(componentsBing)

組件使用

<template>
  <div id="app">
    <sd-form ref="formref" :config="config" size="mini" border v-model="formData">
        <!-- 具名插槽 -->
        <template #testSlot>
            <el-input v-model="formData.slotName" placeholder="這是自定義表單"></el-input>
        </template>
        <!-- 具名插槽 -->
        <template #testSlot1>
            <el-input v-model="formData.slotName1" placeholder="這是自定義表單1"></el-input>
        </template>
        <el-button type="primary" @click="formSave">保 存</el-button>
    </sd-form>
  </div>
</template>

<script>

export default {
  name: 'App',
  data() {
    return {
      config: {},
      formData: {}
    }
  },
  // config為表單數(shù)據(jù)
  // formData為綁定數(shù)據(jù)結(jié)構(gòu)
  created() {
        this.config = {
            labelWidth: '120px',  //  label寬度
            labelPosition: 'right', // label對(duì)齊方式
            size: 'medium', //  表單尺寸
            formItems: [ // 表單元素
                {
                    label: "自定義表單", //表單名稱
                    name: "slotName", // formData綁定的key
                    span: 8, // el-col的span
                    slotName: 'testSlot', // 具名插槽
                    rules: [ // 校驗(yàn)規(guī)則
                        {required: true, message: "Please input Activity name", trigger: "blur"}
                    ],
                },
                {
                    label: "輸入框",
                    name: "name",
                    component: "input", // 表單類型
                    span: 8,
                    options: {
                        maxlength: "20",
                        placeholder: "Activity name",
                    },
                    rules: [
                        {required: true, message: "Please input Activity name", trigger: "blur"}
                    ],
                    requiredHandle: "$.required==true", // 是否需要校驗(yàn)
                },
                {
                    label: "柵格(12/24)",
                    name: "name2",
                    component: "input",
                    span: 8,
                    options: {
                        placeholder: "span: 12",
                    }
                },
                {
                    label: "柵格(12/24)",
                    name: "name3",
                    component: "input",
                    span: 24,
                    options: {
                        placeholder: "span: 12",
                    }
                },
                {
                    label: "自定義表單1", //表單名稱
                    name: "slotName1", // formData綁定的key
                    span: 12, // el-col的span
                    slotName: 'testSlot1', // 具名插槽
                    rules: [ // 校驗(yàn)規(guī)則
                        {required: true, message: "Please input Activity name", trigger: "blur"}
                    ],
                },
                {
                    label: "級(jí)聯(lián)選擇器",
                    name: "cascader",
                    component: "cascader",
                    span: 12,
                    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: 12,
                    tips: "多選框配置加上 name 表示擁有嵌套關(guān)系。否則將值“平鋪”在form對(duì)象",
                    options: { // 表單具體屬性
                        items:[
                            {
                                label: "選項(xiàng)1",
                                name: "option1"
                            },
                            {
                                label: "選項(xiàng)2",
                                name: "option2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true"
                },
                {
                    label: "多選框組",
                    name: "checkboxGroup",
                    component: "checkboxGroup",
                    span: 24,
                    options: {
                        items:[
                            {
                                label: "選項(xiàng)1",
                                value: "option1"
                            },
                            {
                                label: "選項(xiàng)2",
                                value: "option2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true" // 動(dòng)態(tài)顯示隱藏此表單
                },
                {
                    label: "單選",
                    name: "radio",
                    component: "radio",
                    options: {
                        items:[
                            {
                                label: "選項(xiàng)1",
                                value: "1"
                            },
                            {
                                label: "選項(xiàng)2",
                                value: "2"
                            }
                        ]
                    },
                    hideHandle: "$.required==true"
                },
                {
                    label: "開關(guān)",
                    name: "required",
                    span: 12,
                    message: "演示如何使用表達(dá)式動(dòng)態(tài)顯隱和必填,試試打開和關(guān)閉開關(guān)",
                    component: "switch",
                },
                {
                    label: "日期/時(shí)間",
                    name: "date",
                    span: 12,
                    component: "date",
                    options: {
                        type: "datetime",
                        valueFormat: "yyyy-MM-dd HH:mm:ss",
                    },
                    rules: [
                        {required: true, message: "Please input Data", trigger: "change"}
                    ],
                },
                {
                    label: "數(shù)值",
                    name: "number",
                    component: "number",
                },
                {
                    label: "顏色",
                    name: "color",
                    component: "color",
                },
                {
                    label: "評(píng)分",
                    name: "rate",
                    component: "rate",
                }
            ]
        }
        this.formData = {
            slotName: '自定義表單',
            slotName1: '123',
            name: '',
            name2: '',
            name3: '',
            cascader: '',
            checkbox: {},
            checkboxGroup: [],
            radio: '1',
            required: false,
            date: '',
            slider: 8,
            number: 0,
            color: '',
            rate: 0
        }
  },
  methods: {
    formSave() {

    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容