vue3 + ant-design-vue自定義組合表單項(xiàng)formItem

效果圖:

重點(diǎn):
1厌均、父級rc-form-item 上不要加 prop肴焊,要分別加在內(nèi)部的各個(gè)表單元素上

2就乓、自定組件內(nèi)部表單元素上的prop寫法要用這種拼接的方式
:prop="props.attValName + '.selectVal'"
:prop="props.attValName + '.' + index + '.value'"
而不是下面這種烹吵,下面的寫法無法觸發(fā)校驗(yàn)
:prop="props.attValName[ index ].value'"

3惧眠、自定義組件寫prop的那一層要寫rules

4打颤、要達(dá)到父級form能控制自定義組件內(nèi)部的各個(gè)表單元素的校驗(yàn)和數(shù)據(jù)回顯暴拄、重置等能力漓滔,
自定義組件的state定義的數(shù)據(jù)格式尤為重要,以地址級聯(lián)組件AttrAddress為例:
emit('update:modelValue', state.attVals)
即props.modelValue 所包含的數(shù)據(jù)乖篷,要被state.attVals全部包含响驴,這樣就可以由父級的form完全托管校驗(yàn)重置等能力。

源碼:
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 做規(guī)則校驗(yàn)撕蔼,通過required控制是否必填踏施,內(nèi)部校驗(yàn)-->
        <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 做規(guī)則校驗(yàn),通過required控制是否必填罕邀,內(nèi)部校驗(yàn)-->
        <AttrSelectPercent
          v-model="form.attrSelectPercent"
          :att-id="217746"
          att-name="復(fù)選百分比"
          att-val-name="attrSelectPercent"
          :required="true"
          extra=""
          label-width="146px"
        />
      </rc-form-item>

      <rc-form-item label-width="0">
        <!-- 注意這里的 rc-form-item 不要加prop 做規(guī)則校驗(yàn)畅形,通過required控制是否必填,內(nèi)部校驗(yàn)-->
        <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 做規(guī)則校驗(yàn)诉探,通過required控制是否必填日熬,內(nèi)部校驗(yàn)-->
        <AttrCascader
          v-model="form.cascader"
          :att-id="238291"
          att-name="級聯(lián)選擇"
          att-val-name="cascader"
          :required="true"
          label-width="146px"
        />
      </rc-form-item>

      <rc-form-item label-width="0">
        <!-- 注意這里的 rc-form-item 不要加prop 做規(guī)則校驗(yàn),通過required控制是否必填肾胯,內(nèi)部校驗(yàn)-->
        <AttrAddress
          v-model="form.attrAddress2"
          :att-id="238841"
          att-name="地址級聯(lián)選擇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: [], // 復(fù)選百分比
  // combinedInputs2:undefined,
  combinedInputs2: [
    { prefix: '長', value: '100', valueUnit: 'cm' },
    { prefix: '寬', value: '50', valueUnit: 'ml' },
    { prefix: '高', value: '10', valueUnit: 'cm' },
  ],
  // cascader: undefined, // 級聯(lián)選擇
  cascader: [1093079, 1093081], // 級聯(lián)選擇
  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
  // 接口獲取選擇框下拉選項(xiàng)
  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>
  <!-- 復(fù)選百分比:選擇框+輸入框+%  -->
  <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
  // 接口獲取選擇框下拉選項(xiàng)
  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
}
// 自測數(shù)據(jù)
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>
  <!-- 針對動(dòng)態(tài)獲取數(shù)據(jù)竖席,且級聯(lián)層級不確定的情況,有的選項(xiàng)層級多敬肚,有的選項(xiàng)層級少 -->
  <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) // 刪掉第二個(gè)
  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>
  <!-- 地址級聯(lián)選擇毕荐,動(dòng)態(tài)獲取下一級,且層級數(shù)不定艳馒,可能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"> 國內(nèi) </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 {
    // 截取數(shù)組憎亚,指定長度
    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) // 獲取第一級選項(xiàng)
}

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"
    }
]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弄慰,隨后出現(xiàn)的幾起案子第美,更是在濱河造成了極大的恐慌,老刑警劉巖陆爽,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件什往,死亡現(xiàn)場離奇詭異,居然都是意外死亡慌闭,警方通過查閱死者的電腦和手機(jī)别威,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驴剔,“玉大人省古,你說我怎么就攤上這事∽心猓” “怎么了衫樊?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我科侈,道長载佳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任臀栈,我火速辦了婚禮蔫慧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘权薯。我一直安慰自己姑躲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布盟蚣。 她就那樣靜靜地躺著黍析,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屎开。 梳的紋絲不亂的頭發(fā)上阐枣,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音奄抽,去河邊找鬼蔼两。 笑死,一個(gè)胖子當(dāng)著我的面吹牛逞度,可吹牛的內(nèi)容都是我干的额划。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼档泽,長吁一口氣:“原來是場噩夢啊……” “哼俊戳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茁瘦,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤品抽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后甜熔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡突倍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年腔稀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羽历。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焊虏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秕磷,到底是詐尸還是另有隱情诵闭,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站疏尿,受9級特大地震影響瘟芝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜褥琐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一锌俱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敌呈,春花似錦贸宏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至析显,卻和暖如春鲫咽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叫榕。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工浑侥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晰绎。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓寓落,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荞下。 傳聞我的和親對象是個(gè)殘疾皇子伶选,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354