vue3 + ant-design-vue自定义组合表单项formItem

效果图:

重点:
1、父级rc-form-item 上不要加 prop,要分别加在内部的各个表单元素上

2、自定组件内部表单元素上的prop写法要用这种拼接的方式
:prop="props.attValName + '.selectVal'"
:prop="props.attValName + '.' + index + '.value'"
而不是下面这种,下面的写法无法触发校验
:prop="props.attValName[ index ].value'"

3、自定义组件写prop的那一层要写rules

4、要达到父级form能控制自定义组件内部的各个表单元素的校验和数据回显、重置等能力,
自定义组件的state定义的数据格式尤为重要,以地址级联组件AttrAddress为例:
emit('update:modelValue', state.attVals)
即props.modelValue 所包含的数据,要被state.attVals全部包含,这样就可以由父级的form完全托管校验重置等能力。

源码:
index.vue

<template>
  <!-- 自定义组件 使用样例 -->
  <div>
    <rc-form ref="formRef" :model="form" :rules="rules" label-width="146px">
      <rc-form-item label-width="0">
        <!-- 注意这里的 rc-form-item 不要加prop 做规则校验,通过required控制是否必填,内部校验-->
        <AttrSelectAlias
          v-model="form.attrSelectAlias"
          :att-id="238290"
          att-name="属性值可别名"
          att-val-name="attrSelectAlias"
          :required="true"
          extra=""
        />
      </rc-form-item>
      <rc-form-item label-width="0">
        <!-- 注意这里的 rc-form-item 不要加prop 做规则校验,通过required控制是否必填,内部校验-->
        <AttrSelectPercent
          v-model="form.attrSelectPercent"
          :att-id="217746"
          att-name="复选百分比"
          att-val-name="attrSelectPercent"
          :required="true"
          extra=""
          label-width="146px"
        />
      </rc-form-item>

      <rc-form-item label-width="0">
        <!-- 注意这里的 rc-form-item 不要加prop 做规则校验,通过required控制是否必填,内部校验-->
        <AttrCombinedInputs
          v-model="form.combinedInputs2"
          extra=""
          :att-id="238296"
          att-name="组合输入框有前缀"
          att-val-name="combinedInputs2"
          :required="true"
          :prefix-list="prefixList2"
          :unit-list="unitList1"
        />
      </rc-form-item>

      <rc-form-item label-width="0">
        <!-- 注意这里的 rc-form-item 不要加prop 做规则校验,通过required控制是否必填,内部校验-->
        <AttrCascader
          v-model="form.cascader"
          :att-id="238291"
          att-name="级联选择"
          att-val-name="cascader"
          :required="true"
          label-width="146px"
        />
      </rc-form-item>

      <rc-form-item label-width="0">
        <!-- 注意这里的 rc-form-item 不要加prop 做规则校验,通过required控制是否必填,内部校验-->
        <AttrAddress
          v-model="form.attrAddress2"
          :att-id="238841"
          att-name="地址级联选择4级"
          att-val-name="attrAddress2"
          :level="4"
          :required="true"
          label-width="146px"
        />
      </rc-form-item>

      <rc-form-item>
        <rc-button type="primary" size="large" @click="submitForm(formRef)"> 提交 </rc-button>
        <rc-button plain size="large" @click="resetForm(formRef)"> 重置 </rc-button>
      </rc-form-item>
    </rc-form>
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'ant-design-vue'

import AttrSelectAlias from './components/AttrSelectAlias/index.vue'
import AttrSelectPercent from './components/AttrSelectPercent/index.vue'
import AttrCombinedInputs, {
  prefixList1,
  prefixList2,
  unitList1,
} from './components/AttrCombinedInputs/index.vue'
import AttrCascader from './components/AttrCascader/index.vue'
import AttrAddress from './components/AttrAddress/index.vue'

const formRef = ref<FormInstance>()
const form = reactive({
  attrSelectAlias: { selectVal: '1093079', alias: '我是别名' },
  // attrSelectAlias: undefined,
  attrSelectPercent: [], // 复选百分比
  // combinedInputs2:undefined,
  combinedInputs2: [
    { prefix: '长', value: '100', valueUnit: 'cm' },
    { prefix: '宽', value: '50', valueUnit: 'ml' },
    { prefix: '高', value: '10', valueUnit: 'cm' },
  ],
  // cascader: undefined, // 级联选择
  cascader: [1093079, 1093081], // 级联选择
  attrAddress2: { address: ['1', '2901', '55549'], isOverSea: false },
})
const rules = reactive<FormRules>({})

const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log('submit!===', form)
    } else {
      console.log('error submit!-fields', fields)
    }
  })
}

const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}
</script>
<style lang="scss" scoped></style>

./components/AttrSelectAlias/index.vue

<template>
  <!-- 属性值 可别名:下拉选择框 + 输入框 -->
  <rc-form-item
    :label="props.attName"
    :prop="props.attValName + '.selectVal'"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVal.selectVal"
      placeholder="请选择"
      :multiple="props.multiple"
      clearable
      style="width: 320px"
      @change="onSelectChange"
    >
      <rc-option
        v-for="item in state.options"
        :key="item.id"
        :label="item.name"
        :value="item.id.toString()"
      />
    </rc-select>
  </rc-form-item>

  <rc-form-item
    style="margin-left: 8px"
    :prop="props.attValName + '.alias'"
    :rules="{
      required: props.required,
      message: '请输入别名',
      trigger: 'blur',
    }"
  >
    <rc-input
      v-model="state.attVal.alias"
      placeholder="请输入别名"
      style="width: 320px"
      @input="onInputChange"
    />
  </rc-form-item>

  <div v-if="extra" class="extra"> 备注&示例:{{ extra }} </div>
</template>

<script lang="ts">
interface AttValue {
  selectVal: string
  alias: string
}
interface AttValueItem {
  id: number
  name: string
}
</script>
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import mockData from '../../mock.json'

const props = defineProps({
  modelValue: {
    type: Object,
    default: undefined,
  },
  attId: {
    type: Number,
    default: undefined,
  },
  attName: {
    type: String,
    default: undefined,
  },
  attValName: {
    type: String,
    default: undefined,
  },
  required: {
    type: Boolean,
    default: false,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  extra: {
    type: String,
    default: '',
  },
  labelWidth: {
    type: String,
    default: '',
  },
})
const emit = defineEmits(['update:modelValue', 'change', 'blur'])

const state = reactive<{
  attVal: AttValue
  options: AttValueItem[]
}>({
  attVal: { selectVal: '', alias: '' },
  options: [],
})
onMounted(() => {
  getData()
})

const getData = () => {
  if (!props.attId) return
  // 接口获取选择框下拉选项
  setTimeout(() => {
    state.options = mockData
    initAttVal()
  }, 300)
}

const initAttVal = () => {
  // console.log('props.modelValue===', props.modelValue)
  if (!props.modelValue || props.modelValue?.length == 0) {
    // 初始没有值
    state.attVal = { selectVal: '', alias: '' }
  } else {
    // 回显
    state.attVal = props.modelValue as AttValue
  }

  emit('update:modelValue', state.attVal)
  emit('change')
}

const onInputChange = () => {
  // console.log('onInputChange===', state.alias)
}
const onSelectChange = () => {
  // console.log('onSelectChange===', state.selectVal)
}
</script>
<style lang="scss" scoped>
.extra {
  margin-left: 8px;
  color: var(--rcd-color-text-200);
  font-size: 12px;
}
</style>

./components/AttrSelectPercent/index.vue

<template>
  <!-- 复选百分比:选择框+输入框+%  -->
  <rc-form-item
    v-for="(attVal, index) in state.attVals"
    :key="index"
    :style="{ marginBottom: index == state.attVals.length - 1 ? '0' : '20px' }"
  >
    <rc-form-item
      :label="index == 0 ? props.attName : ''"
      :prop="props.attValName + '.' + index + '.value'"
      :rules="{
        required: props.required,
        message: '请选择',
        trigger: 'change',
      }"
      :label-width="props.labelWidth"
    >
      <rc-select
        v-model="state.attVals[index].value"
        placeholder="请选择"
        :style="{
          width: '220px',
        }"
        @change="onSelectChange"
      >
        <rc-option
          v-for="item in state.options"
          :key="item.id"
          :label="item.name"
          :value="item.id.toString()"
        />
      </rc-select>
    </rc-form-item>

    <rc-form-item
      :prop="props.attValName + '.' + index + '.percentage'"
      :rules="{
        required: props.required,
        message: '请输入',
        trigger: 'blur',
      }"
    >
      <rc-input
        v-model="state.attVals[index].percentage"
        placeholder="请输入"
        class="input"
        :maxlength="3"
        @change="onInputChange"
      >
        <template #append> % </template>
      </rc-input>

      <rc-button style="margin-left: 8px" @click.prevent="removeDomain(attVal)"> 删除 </rc-button>
    </rc-form-item>
  </rc-form-item>
  <rc-button style="margin-left: 8px" @click="addDomain"> 新增 </rc-button>

  <div v-if="extra" class="extra"> 备注&示例:{{ extra }} </div>
</template>

<script lang="ts">
interface AttValItem {
  value: string
  percentage: string
  valUnit: string
}
interface AttValueItem {
  id: number
  name: string
}
</script>

<script setup lang="ts">
import { reactive, onMounted } from 'vue'
import mockData from '../../mock.json'

const props = defineProps({
  modelValue: {
    type: Array,
    default: undefined,
  },
  attId: {
    type: Number,
    default: undefined,
  },
  attName: {
    type: String,
    default: undefined,
  },
  attValName: {
    type: String,
    default: undefined,
  },
  required: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  extra: {
    type: String,
    default: '',
  },
  labelWidth: {
    type: String,
    default: '',
  }
})
const emit = defineEmits(['update:modelValue', 'change'])

const state = reactive<{
  attVals: AttValItem[]
  options: AttValueItem[]
}>({
  attVals: [],
  options: [],
})

onMounted(() => {
  getData()
})

const removeDomain = (item: AttValItem) => {
  const index = state.attVals.indexOf(item)
  if (index !== -1) {
    state.attVals.splice(index, 1)
  }
}

const addDomain = () => {
  state.attVals.push({ value: '', percentage: '', valUnit: '%' })
}

const getData = () => {
  if (!props.attId) return
  // 接口获取选择框下拉选项
  setTimeout(()=>{
    state.options = mockData
    initAttVal()

    emit('update:modelValue', state.attVals)
    emit('change')
  },300)
}

const initAttVal = () => {
  // console.log('props.modelValue===', props.modelValue)
  if (!props.modelValue || props.modelValue?.length == 0) {
    // 初始没有值
    addDomain()
  } else {
    // 回显
    state.attVals = props.modelValue as AttValItem[]
  }
}

const onInputChange = () => {
  // console.log('onInputChange===', state.attVal)
}
const onSelectChange = () => {
  // console.log('onSelectChange===', state.attVal)
}
</script>
<style lang="scss" scoped>
.extra {
  margin-left: 8px;
  color: var(--rcd-color-text-200);
  font-size: 12px;
}
.input {
  width: 104px;
  margin-left: -4px;
  :deep(.rcd-input__wrapper) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }
}
</style>

./components/AttrCombinedInputs/index.vue

<template>
  <!-- 组合类型:前缀(+/-/''/长/宽/高) + 输入框 + 单位下拉选择框  -->
  <rc-form-item v-for="(prefix, index) in state.prefixList" :key="index">
    <rc-form-item
      :label="index == 0 ? props.attName : ''"
      :prop="props.attValName + '.' + index + '.value'"
      :rules="{
        required: props.required,
        message: '请输入',
        trigger: 'blur',
      }"
    >
      <rc-input
        v-model="state.attVal[index].value"
        placeholder="请输入"
        :style="{ width: state.prefixList.length == 1 ? '320px' : '200px' }"
        @change="onInputChange"
      >
        <template v-if="prefix" #prepend>
          <div style="width: 14px">
            {{ prefix }}
          </div>
        </template>
        <template #append>
          <rc-select style="width: 71px" />
        </template>
      </rc-input>
    </rc-form-item>
    <rc-form-item :prop="props.attValName + '.' + index + '.valueUnit'">
      <rc-select
        v-model="state.attVal[index].valueUnit"
        class="select"
        :style="{ marginRight: index == state.prefixList.length - 1 ? 0 : '24px' }"
        @change="onUnitChange"
      >
        <rc-option v-for="(unit, idx) in state.unitList" :key="idx" :label="unit" :value="unit" />
      </rc-select>
    </rc-form-item>
  </rc-form-item>

  <div v-if="extra" class="extra"> 备注&示例:{{ extra }} </div>
</template>

<script lang="ts">
interface AttValItem {
  prefix: string
  value: string
  valueUnit: string
}
// 自测数据
export const prefixList2 = ['长', '宽', '高'] // 有前缀
export const prefixList1 = [''] // 没有前缀
export const unitList1 = ['mm', 'cm', 'm']
</script>

<script setup lang="ts">
import { reactive, onMounted } from 'vue'

const props = defineProps({
  modelValue: {
    type: Array,
    default: undefined,
  },
  attId: {
    type: Number,
    default: undefined,
  },
  attName: {
    type: String,
    default: undefined,
  },
  attValName: {
    type: String,
    default: undefined,
  },
  required: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  prefixList: {
    type: Array,
    default() {
      return []
    },
  },
  unitList: {
    type: Array,
    default() {
      return []
    },
  },
  extra: {
    type: String,
    default: '',
  },
})
const emit = defineEmits(['update:modelValue', 'change'])

const state = reactive({
  attVal: [] as AttValItem[],
  prefixList: [] as string[],
  unitList: [] as string[],
})

onMounted(() => {
  getData()
})

const getData = () => {
  setTimeout(() => {
    state.prefixList = props.prefixList as string[]
    state.unitList = props.unitList as string[]

    initAttVal()

    emit('update:modelValue', state.attVal)
    emit('change')
  }, 1000)
}

const initAttVal = () => {
  // console.log('props.modelValue===', props.modelValue)
  if (!props.modelValue || props.modelValue?.length == 0) {
    // 初始没有值
    state.attVal = state.prefixList.map((prefix) => {
      return { prefix, value: '', valueUnit: state.unitList[0] }
    })
  } else {
    // 回显
    state.attVal = state.prefixList.map((prefix) => {
      const temp = props.modelValue?.find((item: any) => item.prefix == prefix) as AttValItem
      return { prefix, value: temp?.value, valueUnit: temp?.valueUnit }
    })
  }
}

const onInputChange = () => {
  // console.log('onInputChange===', state.attVal)
}
const onUnitChange = () => {
  // console.log('onUnitChange===', state.attVal)
}
</script>
<style lang="scss" scoped>
.extra {
  margin-left: 8px;
  color: var(--rcd-color-text-200);
  font-size: 12px;
}

.select {
  width: 72px;
  margin-left: -72px;
  z-index: 2;

  :deep(.rcd-input__wrapper) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }
}
</style>

./components/AttrCascader/index.vue

<template>
  <!-- 针对动态获取数据,且级联层级不确定的情况,有的选项层级多,有的选项层级少 -->
  <rc-form-item
    :label="props.attName"
    :prop="props.attValName + '.' + 0"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
    :label-width="props.labelWidth"
  >
    <rc-select
      v-model="state.attVals[0]"
      placeholder="请选择"
      :disabled="props.disabled"
      style="width: 320px; margin-right: 8px"
      @change="onChange1"
    >
      <rc-option
        v-for="item in option.options1"
        :key="item.id"
        :label="item.name"
        :value="item.id"
      />
    </rc-select>
  </rc-form-item>
  <rc-form-item
    v-if="option.options2.length > 0"
    :prop="props.attValName + '.' + 1"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVals[1]"
      placeholder="请选择"
      :disabled="props.disabled"
      style="width: 320px"
      @change="onChange2"
    >
      <rc-option
        v-for="item in option.options2"
        :key="item.id"
        :label="item.name"
        :value="item.id"
      />
    </rc-select>
  </rc-form-item>

  <div v-if="extra" class="extra"> 备注&示例:{{ extra }} </div>
</template>

<script lang="ts">
interface AttValueByLevelItem {
  id: number
  name: string
}
</script>
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import mockData from '../../mock.json'

const props = defineProps({
  modelValue: {
    type: Array,
    default: undefined,
  },
  attId: {
    type: Number,
    default: undefined,
  },
  attName: {
    type: String,
    default: undefined,
  },
  attValName: {
    type: String,
    default: undefined,
  },
  required: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  extra: {
    type: String,
    default: '',
  },
  labelWidth: {
    type: String,
    default: '',
  },
})
const emit = defineEmits(['update:modelValue', 'change'])

const state = reactive({
  attVals: [] as number[],
})

const option = reactive<{ [key: string]: AttValueByLevelItem[] }>({
  options1: [],
  options2: [],
})

const getData = (level: number, parentId?: number, cb?: (hasNext: boolean) => void) => {
  // const params = {
  //   attId: props.attId,
  //   attLevel: level,
  //   parentId,
  // }
  // getAttValueByLevel(params).then((res) => {
  //   option['options' + level] = res || []
  //   cb && cb(res ? true : false)
  // })
  setTimeout(()=>{
    const res = mockData
    option['options' + level] = res || []
    cb && cb(res ? true : false)
  },300)
}

const onChange1 = (val: number) => {
  state.attVals.splice(1, 1) // 删掉第二个
  option.options2 = []
  getData(2, val)
}
const onChange2 = (val: number) => {
  //
}

const initAttVal = () => {
  // console.log('props.modelValue===', props.modelValue)
  if (!props.modelValue || props.modelValue?.length == 0) {
    // 初始没有值
  } else {
    // 回显
    state.attVals = props.modelValue as number[]
    if (props.modelValue.length > 1) {
      getData(2, state.attVals[0])
    }
  }
}

onMounted(() => {
  getData(1, undefined, () => {
    initAttVal()
    emit('update:modelValue', state.attVals)
    emit('change')
  })
})
</script>
<style lang="scss" scoped>
.extra {
  margin-left: 8px;
  color: var(--rcd-color-text-200);
  font-size: 12px;
}
</style>

./components/AttrAddress/index.vue

<template>
  <!-- 地址级联选择,动态获取下一级,且层级数不定,可能3级可能4级 -->
  <rc-form-item
    style="width: 100%"
    :label="props.attName"
    :label-width="props.labelWidth"
    :prop="props.attValName + '.isOverSea'"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-radio-group v-model="state.attVals.isOverSea" @change="onIsOverSeaChange">
      <rc-radio :label="false"> 国内 </rc-radio>
      <rc-radio :label="true"> 海外 </rc-radio>
    </rc-radio-group>
  </rc-form-item>

  <rc-form-item
    :label-width="props.labelWidth"
    :prop="props.attValName + '.address.' + 0"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVals.address[0]"
      placeholder="请选择"
      style="width: 200px; margin-right: 8px"
      @change="(v: any) => onChange(v, 0 + 1)"
    >
      <rc-option
        v-for="item in option['options' + (0 + 1)]"
        :key="item.districtId"
        :label="item.districtName"
        :value="item.districtId.toString()"
      />
    </rc-select>
  </rc-form-item>

  <rc-form-item
    v-if="option.options2.length > 0"
    :prop="props.attValName + '.address.' + 1"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVals.address[1]"
      placeholder="请选择"
      style="width: 200px; margin-right: 8px"
      @change="(v: any) => onChange(v, 1 + 1)"
    >
      <rc-option
        v-for="item in option['options' + (1 + 1)]"
        :key="item.districtId"
        :label="item.districtName"
        :value="item.districtId.toString()"
      />
    </rc-select>
  </rc-form-item>

  <rc-form-item
    v-if="option.options3.length > 0"
    :prop="props.attValName + '.address.' + 2"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVals.address[2]"
      placeholder="请选择"
      style="width: 200px; margin-right: 8px"
      @change="(v: any) => onChange(v, 2 + 1)"
    >
      <rc-option
        v-for="item in option['options' + (2 + 1)]"
        :key="item.districtId"
        :label="item.districtName"
        :value="item.districtId.toString()"
      />
    </rc-select>
  </rc-form-item>

  <rc-form-item
    v-if="option.options4.length > 0"
    :prop="props.attValName + '.address.' + 3"
    :rules="{
      required: props.required,
      message: '请选择',
      trigger: 'change',
    }"
  >
    <rc-select
      v-model="state.attVals.address[3]"
      placeholder="请选择"
      style="width: 200px"
      @change="(v: any) => onChange(v, 3 + 1)"
    >
      <rc-option
        v-for="item in option['options' + (3 + 1)]"
        :key="item.districtId"
        :label="item.districtName"
        :value="item.districtId.toString()"
      />
    </rc-select>
  </rc-form-item>

  <div v-if="extra" class="extra"> 备注&示例:{{ extra }} </div>
</template>

<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import { getAddressByParentId } from '@/services/productIntroduction'

const props = defineProps({
  modelValue: {
    type: Object,
    default: undefined,
  },
  attId: {
    type: Number,
    default: undefined,
  },
  // 可选层级
  level: {
    type: Number,
    default: 1,
  },
  attName: {
    type: String,
    default: undefined,
  },
  attValName: {
    type: String,
    default: undefined,
  },
  required: {
    type: Boolean,
    default: false,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  extra: {
    type: String,
    default: '',
  },
  labelWidth: {
    type: String,
    default: '',
  },
})
const emit = defineEmits(['update:modelValue', 'change'])

interface AttValue {
  address: string[]
  isOverSea: boolean
}

const state = reactive<{ attVals: AttValue }>({
  attVals: {
    address: [],
    isOverSea: false,
  },
})

interface AddressItem {
  districtId: number
  districtName: string
  districtFatherId: number
}
const option = reactive<{ [key: string]: AddressItem[] }>({
  options1: [],
  options2: [],
  options3: [],
  options4: [],
})

const getData = (parentId: string, level: number, cb?: (hasNext: boolean) => void) => {
  getAddressByParentId({ parentId }).then((res) => {
    option['options' + level] = res.output?.areas || []
    cb && cb(res.output?.areas ? true : false)
  })
}

const onChange = (val: string, level: number) => {
  switch (level) {
    case 1:
      onChange1(val)
      break
    case 2:
      onChange2(val)
      break
    case 3:
      onChange3(val)
      break
    case 4:
      onChange4(val)
      break
  }
}

const onChange1 = (val: string) => {
  if (props.level <= 1) {
    //
  } else {
    // 截取数组,指定长度
    const len = state.attVals.address.length
    state.attVals.address.splice(1, len - 1)

    option.options2 = []
    option.options3 = []
    option.options4 = []

    getData(val, 2)
  }
}
const onChange2 = (val: string) => {
  if (props.level <= 2) {
    //
  } else {
    const len = state.attVals.address.length
    state.attVals.address.splice(2, len - 2)

    option.options3 = []
    option.options4 = []

    getData(val, 3)
  }
}
const onChange3 = (val: string) => {
  if (props.level <= 3) {
    //
  } else {
    const len = state.attVals.address.length
    state.attVals.address.splice(3, len - 3)

    option.options4 = []

    getData(val, 4)
  }
}
const onChange4 = (val: string) => {
  //
}

const onIsOverSeaChange = (isOverSea: number) => {
  // 重置地址选择框
  const len = state.attVals.address.length
  state.attVals.address.splice(0, len)
  option.options1 = []
  option.options2 = []
  option.options3 = []
  option.options4 = []
  const parentId = isOverSea ? '53283' : '4744'
  getData(parentId, 1) // 获取第一级选项
}

const initAttVal = () => {
  // console.log('props.modelValue===', props.modelValue)
  if (!props.modelValue) {
    // 初始没有值
  } else {
    // 回显
    state.attVals = props.modelValue as AttValue
    if (state.attVals.address.length > 1) {
      getData(state.attVals.address[0], 2)
    }
    if (state.attVals.address.length > 2) {
      getData(state.attVals.address[1], 3)
    }
    if (state.attVals.address.length > 3) {
      getData(state.attVals.address[2], 4)
    }
  }
}

onMounted(() => {
  const parentId = props.modelValue?.isOverSea ? '53283' : '4744'

  getData(parentId, 1, () => {
    initAttVal()
    emit('update:modelValue', state.attVals)
    emit('change')
  })
})
</script>
<style lang="scss" scoped>
.extra {
  margin-left: 8px;
  color: var(--rcd-color-text-200);
  font-size: 12px;
}
</style>

mock.json

[
    {
        "id": 1093079,
        "name": "测试1"
    },
    {
        "id": 1093080,
        "name": "测试2"
    },
    {
        "id": 1093081,
        "name": "测试3"
    }
]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342