使用v-modal,双向绑定省市区选择器?

来源:2-12 省市区选择组件-完善省市区联动组件并给父组件分发事件

爱学兮的小江江

2021-12-13

老师,你好
如何使用v-modal,双向绑定省市区选择器?
下面代码虽然能实现此功能,但感觉不太优雅,要使用 setTimeout + watch 去一级一级的控制下拉框列表数据的渲染,请问老师有更好的方法、思路吗?多谢多谢~~

<template>
  <div class="area-choose jiang-select-margin">
    <el-select v-model="province" placeholder="请选择省份" clearable>
      <el-option
        v-for="item in provinceList"
        :key="item.code"
        :value="item.code"
        :label="item.name"
      >
      </el-option>
    </el-select>
    <el-select
      v-model="city"
      placeholder="请选择城市"
      :disabled="!province"
      clearable
    >
      <el-option
        v-for="item in cityList"
        :key="item.code"
        :value="item.code"
        :label="item.name"
      ></el-option>
    </el-select>
    <el-select
      v-model="area"
      placeholder="请选择区域"
      :disabled="!province || !city"
      clearable
    >
      <el-option
        v-for="item in areaList"
        :key="item.code"
        :value="item.code"
        :label="item.name"
      >
      </el-option>
    </el-select>
    <el-select
      v-model="street"
      placeholder="请选择街道"
      :disabled="!province || !city || !area"
      clearable
    >
      <el-option
        v-for="item in streetList"
        :key="item.code"
        :value="item.code"
        :label="item.name"
      >
      </el-option>
    </el-select>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, watch, PropType } from "vue";
import pacsCode from "china-division/dist/pcas-code.json";

export interface AreaItem {
  name: string;
  code: string;
  children?: AreaItem[];
}
export interface addressProps {
  province: AreaItem;
  city: AreaItem;
  area: AreaItem;
  street: AreaItem;
}
export default defineComponent({
  name: "area-choose",
  emits: ["update:address"],
  props: {
    address: {
      type: Object as PropType<addressProps>,
      required: true,
    },
  },
  setup(props, { emit }) {
    const provinceList = ref(pacsCode);
    const province = ref("");
    const city = ref("");
    const area = ref("");
    const street = ref("");
    const cityList = ref<AreaItem[]>([]);
    const areaList = ref<AreaItem[]>([]);
    const streetList = ref<AreaItem[]>([]);
    watch(
      () => props.address,
      async (val) => {
        if (val) {
          province.value = props.address?.province.code;
          setTimeout(() => {
            city.value = props.address.city.code;
          }, 0);
          setTimeout(() => {
            area.value = props.address.area.code;
          }, 0);
          setTimeout(() => {
            street.value = props.address.street.code;
          }, 0);
        }
      },
      {
        immediate: true,
      }
    );
    watch(
      () => province.value,
      (val) => {
        if (val) {
          let cities = provinceList.value.find(
            (item) => item.code === val
          )?.children;
          if (cities) {
            cityList.value = cities;
          }
        }
        city.value = "";
        area.value = "";
        street.value = "";
      },
      {
        immediate: true,
      }
    );
    watch(
      () => city.value,
      (val) => {
        console.log("city.value", val);

        if (val) {
          let areas = cityList.value.find(
            (item) => item.code === val
          )?.children;
          if (areas) {
            areaList.value = areas;
          }
        }
        area.value = "";
        street.value = "";
      },
      {
        immediate: true,
      }
    );
    watch(
      () => area.value,
      (val) => {
        if (val) {
          let streets = areaList.value.find(
            (item) => item.code === val
          )?.children;
          if (streets) {
            console.log(streets);
            streetList.value = streets;
          }
        }
        street.value = "";
      },
      {
        immediate: true,
      }
    );
    watch(
      () => street.value,
      (val) => {
        console.log(val);
        if (val) {
          let selectedProvince = {
            code: province.value,
            name: provinceList.value.find(
              (item) => item.code === province.value
            )?.name,
          };
          let selectedCity = {
            code: city.value,
            name: cityList.value.find((item) => item.code === city.value)?.name,
          };
          let selectedArea = {
            code: area.value,
            name: areaList.value.find((item) => item.code === area.value)?.name,
          };
          let selectedStreet = {
            code: val,
            name: streetList.value.find((item) => item.code === val)?.name,
          };
          emit("update:address", {
            province: selectedProvince,
            city: selectedCity,
            area: selectedArea,
            street: selectedStreet,
          });
        }
      }
    );
    return {
      province,
      city,
      area,
      street,
      provinceList,
      cityList,
      areaList,
      streetList,
    };
  },
});
</script>
<style scoped lang="less"></style>

写回答

2回答

五月的夏天

2021-12-14

这个组件不太建议v-model用法哈,这样会增加用户的心智负担和自己编码的难度 。

0
0

五月的夏天

2021-12-13

不需要定时器啊,课程里面用的方法应该就是最优的,可以参考课程内容 。

0
4
爱学兮的小江江
回复
五月的夏天
不是监听不到数据的问题。父组件传入的值是个对象: { province: { code: "13", name: "河北省", }, city: { code: "1303", name: "秦皇岛市", }, area: { code: "130306", name: "抚宁区", }, street: { code: "130306101", name: "留守营镇", }, } 问题在于,城市选择器的可选项城市列表,先由省份确定后,再传入选择的城市数据,选择框才显示该城市,否则选择框显示为空,所有我才想着使用定时器去控制其先后顺序。
2021-12-14
共4条回复

基于Vue3+Vite+TS,二次封装element-plus业务组件

集成大量实际样例,系统掌握前沿技术栈与二次组件库封装能力

447 学习 · 185 问题

查看课程