vue3中子组件v-model接受动态多层对象嵌套传参

2,095 阅读1分钟

周末在封装一个Form组件,供业务层便捷使用, 父组件:

<CommonForm
      v-model="form"
      :layout-config="layoutConfig"
      :option="option"
      @on-success="onSubmitSuccess"
    >
</CommonForm>
const form = ref({
    subObj:{
         title:'清新空气'
           }
    subObj2:{
          status:{
             pdstatus:0  
                 }
           }
     })
const option.value = {
 column: [
      {
        label: '标题',
        prop: 'subObj.title',
        type: 'input',
      },

      {
        label: '盘点状态',
        prop: 'subObj2.status.pdstatus',
        type: 'select',
        dicCode: 'PdStatus',
      }, 
    ],
}

子组件用modelValue接收form,且通过遍历循环生成form-item 子组件CommonForm代码:

 <template v-for="item in columnItems" :key="item.prop">
      <template
        v-if="
          item.type === TableColumnOrFormTypeEnum.INPUT ||
          item.type === TableColumnOrFormTypeEnum.TEXTAREA ||
          (!item.type && item.prop)
        "
      >
        <el-form-item
          v-if="!item.isShowCallBack || item.isShowCallBack()"
          :label="item.label"
          :prop="item.prop"
          :style="{
            flex: setFlex(item),
          }"
        >
          <el-input
            v-model="form[item.prop]"
            clearable
            :disabled="item.disabled"
            :placeholder="item.placeholder || '请输入' + item.label"
            :readonly="item.readonly"
            :type="item.type || 'text'"
          />
        </el-form-item>
      </template>     <template v-if="item.type === TableColumnOrFormTypeEnum.SELECT">
        <el-form-item
          v-if="!item.isShowCallBack || item.isShowCallBack()"
          :label="item.label"
          :prop="item.prop"
          :style="{
            flex: setFlex(item),
          }"
        >
          <CommonSelect
            v-model="form[item.prop]"
            :dic-code="item.dicCode"
            :dic-url="item.dicUrl"
            :disabled="item.disabled"
            :opition-list="item.opitionList"
            :placeholder="item.placeholder || '请选择' + item.label"
            :readonly="item.readonly"
          />
        </el-form-item>
      </template>
  </template>     

此时子组件无法获取到form子对象的值,因为form[item.prop]实际上是form.['subObj.title'],而实际上我需要的是form.subObj.title或者form['subObj']['title'],然而v-model是非常受限制的,无法支持函数,三目运算符,以及一个变量替换另一个变量。思索之后,得到一个解决方案,则是在当前子组件再封装一层子组件,改变form源到form.subObj,改造子组件CommonForm如下:

 <template v-for="item in columnItems" :key="item.prop">
    <CommonFormItem v-model="form" :item="item" :set-flex="setFlex" />
  <template>

子组件CommonFormItem代码如下:

  <template
    v-if="
      item.type === TableColumnOrFormTypeEnum.INPUT ||
      item.type === TableColumnOrFormTypeEnum.TEXTAREA ||
      (!item.type && item.prop)
    "
  >
    <el-form-item
      v-if="!item.isShowCallBack || item.isShowCallBack()"
      :label="item.label"
      :prop="item.prop"
      :style="{
        flex: setFlex(item),
      }"
    >
      <el-input
        v-model="form[keys.slice(-1)]"
        clearable
        :disabled="item.disabled"
        :placeholder="item.placeholder || '请输入' + item.label"
        :readonly="item.readonly"
        :type="item.type || 'text'"
      />
    </el-form-item>
  </template>
   <template v-if="item.type === TableColumnOrFormTypeEnum.SELECT">
    <el-form-item
      v-if="!item.isShowCallBack || item.isShowCallBack()"
      :label="item.label"
      :prop="item.prop"
      :style="{
        flex: setFlex(item),
      }"
    >
      <CommonSelect
        v-model="form[keys.slice(-1)]"
        :dic-code="item.dicCode"
        :dic-url="item.dicUrl"
        :disabled="item.disabled"
        :opition-list="item.opitionList"
        :placeholder="item.placeholder || '请选择' + item.label"
        :readonly="item.readonly"
      />
    </el-form-item>
  </template>
  const keys = computed(() => {
        const keyArray = props.item.prop?.split('.') || []
        return keyArray
      })
      const getObjectKeyValue = () => {
        if (!keys.value || !keys.value.length) return
        const { modelValue } = props
        const currentKeys = keys.value.slice(0, -1)
        if (!currentKeys || !currentKeys.length) {
          return modelValue
        }
        const currentForm = currentKeys.reduce((pre, cur) => {
          return pre ? pre[cur] : undefined
        }, modelValue)
        return currentForm
      }

      const form = ref(getObjectKeyValue())

子组件CommonFormItem的v-model=form[keys.slice(-1)], 以上keys求出来值为['subObj','titie'] getObjectKeyValue()做的功能则是把form源做修改,标题Item源改为form.subObj以及盘点状态Item的源为form.subObj2.status. 则两个item的v-model的值分别form.subObj['title']和form.subObj2.status['pdstatus'].

完美解决v-mdel对动态传参数,且多层嵌套的不支持的问题