美文网首页
el-form动态表单

el-form动态表单

作者: Xbbing | 来源:发表于2023-03-23 17:43 被阅读0次

    效果

    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>
    

    数据校验

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

    参数结构

    // config为表单数据
    // formData为绑定数据结构
    created() {
            this.config = {
                labelWidth: '120px',  //  label宽度
                labelPosition: 'right', // label对齐方式
                size: 'medium', //  表单尺寸
                formItems: [ // 表单元素
                    {
                        label: "自定义表单", //表单名称
                        name: "slotName", // formData绑定的key
                        span: 8, // el-col的span
                        slotName: 'testSlot', // 具名插槽
                        rules: [ // 校验规则
                            {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", // 是否需要校验
                    },
                    {
                        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: "级联选择器",
                        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 表示拥有嵌套关系。否则将值“平铺”在form对象",
                        options: { // 表单具体属性
                            items:[
                                {
                                    label: "选项1",
                                    name: "option1"
                                },
                                {
                                    label: "选项2",
                                    name: "option2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true"
                    },
                    {
                        label: "多选框组",
                        name: "checkboxGroup",
                        component: "checkboxGroup",
                        span: 24,
                        options: {
                            items:[
                                {
                                    label: "选项1",
                                    value: "option1"
                                },
                                {
                                    label: "选项2",
                                    value: "option2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true" // 动态显示隐藏此表单
                    },
                    {
                        label: "单选",
                        name: "radio",
                        component: "radio",
                        options: {
                            items:[
                                {
                                    label: "选项1",
                                    value: "1"
                                },
                                {
                                    label: "选项2",
                                    value: "2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true"
                    },
                    {
                        label: "开关",
                        name: "required",
                        span: 12,
                        message: "演示如何使用表达式动态显隐和必填,试试打开和关闭开关",
                        component: "switch",
                    },
                    {
                        label: "日期/时间",
                        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: "数值",
                        name: "number",
                        component: "number",
                    },
                    {
                        label: "颜色",
                        name: "color",
                        component: "color",
                    },
                    {
                        label: "评分",
                        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 || '请选择'"></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:{
            // 监听modelValue变化复制给当前绑定元素实现绑定
            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()
                        })
                    }   
                })
            },
            //处理远程选项数据
            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)
                    }
                })
            },
            //合并深结构对象
            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 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

    // 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为表单数据
      // formData为绑定数据结构
      created() {
            this.config = {
                labelWidth: '120px',  //  label宽度
                labelPosition: 'right', // label对齐方式
                size: 'medium', //  表单尺寸
                formItems: [ // 表单元素
                    {
                        label: "自定义表单", //表单名称
                        name: "slotName", // formData绑定的key
                        span: 8, // el-col的span
                        slotName: 'testSlot', // 具名插槽
                        rules: [ // 校验规则
                            {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", // 是否需要校验
                    },
                    {
                        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: [ // 校验规则
                            {required: true, message: "Please input Activity name", trigger: "blur"}
                        ],
                    },
                    {
                        label: "级联选择器",
                        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 表示拥有嵌套关系。否则将值“平铺”在form对象",
                        options: { // 表单具体属性
                            items:[
                                {
                                    label: "选项1",
                                    name: "option1"
                                },
                                {
                                    label: "选项2",
                                    name: "option2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true"
                    },
                    {
                        label: "多选框组",
                        name: "checkboxGroup",
                        component: "checkboxGroup",
                        span: 24,
                        options: {
                            items:[
                                {
                                    label: "选项1",
                                    value: "option1"
                                },
                                {
                                    label: "选项2",
                                    value: "option2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true" // 动态显示隐藏此表单
                    },
                    {
                        label: "单选",
                        name: "radio",
                        component: "radio",
                        options: {
                            items:[
                                {
                                    label: "选项1",
                                    value: "1"
                                },
                                {
                                    label: "选项2",
                                    value: "2"
                                }
                            ]
                        },
                        hideHandle: "$.required==true"
                    },
                    {
                        label: "开关",
                        name: "required",
                        span: 12,
                        message: "演示如何使用表达式动态显隐和必填,试试打开和关闭开关",
                        component: "switch",
                    },
                    {
                        label: "日期/时间",
                        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: "数值",
                        name: "number",
                        component: "number",
                    },
                    {
                        label: "颜色",
                        name: "color",
                        component: "color",
                    },
                    {
                        label: "评分",
                        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>
    
    

    相关文章

      网友评论

          本文标题:el-form动态表单

          本文链接:https://www.haomeiwen.com/subject/pbiirdtx.html