高效便捷的省市区选择——Area省市区选择封装实战攻略

前言

省市区选择是许多移动端应用中常见的功能需求,而 vantarea 组件已经为我们提供了一个非常好用的选择方案。但是,在某些场景下,我们需要对 area 进行二次封装,更加定制化的省市区选择功能,以满足更加具体的业务需求。本文将会向大家介绍如何在 vant 中对 area 进行二次封装,并提供一些实用的封装技巧和方法,帮助大家实现更加完美的省市区选择方案。


实现思路

  1. 首先定义一个子组件页面用来封装选择器;
  2. 在父组件中(使用的页面)引入封装组件(子组件)并注册,然后在页面中使用,在父组件中给标签(注册的组件名)上绑定多个属性, 属性上挂载需要传递的值,通过 props 在子组件(封装文件)接收数据;
  3. 在子组件中自定义确定的事件,调用这个事件后,子组件通过 this.$emit('自定义事件名',要传递的数据) 发送父组件可以监听的数据,最后父组件监听子组件事件,调用事件并接收传递过来的数据。

安装

vant 官方提供了一份默认的省市区数据,可以通过 @vant/area-data 引入:

npm i @vant/area-data

定义的参数

参数描述
selectValue绑定值 model
keyValue绑定的 key 字段
columnsNum绑定的 value 字段
rules绑定的 option 数据源
required是否显示红色 * 校验

封装文件

<template>
  <div>
    <van-field v-model="textValue" v-bind="$attrs" :name="$attrs.name" :placeholder="placeholder" :rules="rules" :required="required"
      :readonly="readonly" :is-link="islink" @click="show = !show" />
    <van-popup v-model="show" position="bottom" overlay close-on-click-overlay>
      <van-area :area-list="areaList" :columns-num="columnsNum" :columns-placeholder="columnsplaceholder" @confirm="onConfirm"
        @cancel="show = !show" />
    </van-popup>
  </div>
</template>

<script>
import { areaList } from "@vant/area-data";
export default {
  props: {
    required: Boolean,
    readonly: Boolean,
    islink: Boolean,
    columnsNum: Number,
    rules: Array,
    selectValue: String,
    keyValue: String,
  },
  data() {
    return {
      areaList,
      show: false,
      textValue: "",
      selectOptions: [],
      placeholder: "",
      columnsplaceholder: ["请选择", "请选择", "请选择"],
      provinceList: null,
      cityList: null,
      countyList: null,
      province: "",
      city: "",
      county: "",
    };
  },
  methods: {
    onConfirm(areaData) {
      // 判断打开选择器后是否必填省/市/区
      if (this.columnsNum === 1) {
        this.provinceOn(areaData);
      } else if (this.columnsNum === 2) {
        this.provinceCityOn(areaData);
      } else {
        this.provincesMunicipalitiesOn(areaData);
      }
    },
    // 省时必填校验
    provinceOn(areaData) {
      const province = areaData[0];
      if (!province.name) {
        this.$toast("请选择省");
        return;
      }
      this.acquireOn(areaData);
    },
    // 省市时必填校验
    provinceCityOn(areaData) {
      const province = areaData[0];
      const city = areaData[1];
      if (!province.name) {
        this.$toast("请选择省");
        return;
      }
      if (!city.name) {
        this.$toast("请选择市");
        return;
      }
      this.acquireOn(areaData);
    },
    // 省市区时必填校验
    provincesMunicipalitiesOn(areaData) {
      const province = areaData[0];
      const city = areaData[1];
      const area = areaData[2];
      if (!province.name) {
        this.$toast("请选择省");
        return;
      }
      if (!city.name) {
        this.$toast("请选择市");
        return;
      }
      if (!area.name) {
        this.$toast("请选择区");
        return;
      }
      this.acquireOn(areaData);
    },
    // 校验完后执行确定方法赋值name/code
    acquireOn(areaData) {
      // 获取选中name/code值
      let names = "";
      let codes = "";
      areaData.forEach((item, index) => {
        if (!item) {
          return "";
        }
        if (index > 0) {
          names += "/" + item.name;
          codes += "/" + item.code;
        } else {
          names += item.name;
          codes += item.code;
        }
      });
      if (this.keyValue === "code") {
        this.textValue = codes;
      } else {
        this.textValue = names;
      }
      this.show = !this.show;
      this.$emit("confirm", this.textValue);
      // 更新回显值
      this.getValue(this.keyValue);
    },
    // 通过code值获取地区name
    getNameByCode(value, list) {
      let name = "";
      if (!value) {
        return;
      }
      Object.keys(list).forEach((key) => {
        if (value.toString() === key.toString()) {
          name = list[key];
        }
      });
      return name;
    },
    // 地区回显
    getValue(type) {
      if (!this.selectValue || !this.selectValue.length) {
        this.textValue = "";
        return;
      }
      const selectList = this.selectValue.split("/");
      let provinceName,
        cityName,
        countyName = "";
      switch (type) {
        case "code":
          if (Number(this.columnsNum) === 1) {
            provinceName = this.getNameByCode(selectList[0], this.provinceList);
            this.textValue = provinceName;
          } else if (Number(this.columnsNum) === 2) {
            provinceName = this.getNameByCode(selectList[0], this.provinceList);
            cityName = this.getNameByCode(selectList[1], this.cityList);
            this.textValue = provinceName + "/" + cityName;
          } else {
            provinceName = this.getNameByCode(selectList[0], this.provinceList);
            cityName = this.getNameByCode(selectList[1], this.cityList);
            countyName = this.getNameByCode(selectList[2], this.countyList);
            this.textValue = provinceName + "/" + cityName + "/" + countyName;
          }
          break;
        case "name":
          this.textValue = this.selectValue;
          break;
      }
    },
  },
  watch: {
    // json数据
    areaList: {
      handler(newVal) {
        if (newVal) {
          const length = Object.keys(newVal).length;
          if (length) {
            this.cityList = newVal.city_list;
            this.countyList = newVal.county_list;
            this.provinceList = newVal.province_list;
          }
        }
      },
      immediate: true,
      deep: true,
    },
    // 控制选择完整省市区还是部分
    columnsNum: {
      handler(newVal) {
        switch (newVal) {
          case 1:
            this.placeholder = "省";
            this.columnsplaceholder = ["请选择"];
            break;
          case 2:
            this.placeholder = "省/市";
            this.columnsplaceholder = ["请选择", "请选择"];
            break;
          default:
            this.placeholder = "省/市/区";
            this.columnsplaceholder = ["请选择", "请选择", "请选择"];
        }
      },
      immediate: true,
    },
    selectValue: {
      handler(newValue) {
        this.$nextTick(() => {
          if (this.keyValue) {
            this.getValue(this.keyValue);
          }
        });
      },
      immediate: true,
    },
    keyValue: {
      handler(newValue) {
        if (newValue) {
          this.getValue(newValue);
        }
      },
      immediate: true,
    },
  },
};
</script>

使用文件

<template>
  <div>
    <van-form validate-first>
      <AreaSelect name="cjPovo" label="车辆注册地" :selectValue="cjPovo" :readonly="true" :keyValue="`name`" :columnsNum="3"
        @confirm="areaConfirm" :required="true" :rules="rules.cjPovo" />
      <div class="btnBomBox">
        <van-button round size="small" block @click="submitOn" type="info">提交</van-button>
      </div>
    </van-form>
  </div>
</template>

<script>
import AreaSelect from "@/components/areaSelect/index";
export default {
  components: {
    AreaSelect,
  },
  data() {
    return {
      cjPovo: "",
      rules: {
        cjPovo: [
          {
            required: true,
            message: "请选择车辆注册地",
          },
        ],
      },
    };
  },
  methods: {
    // 点击确定
    areaConfirm(data) {
      this.cjPovo = data;
    },
    // 提交
    submitOn() {
      console.log(this.cjPovo);
    },
  },
};
</script>

<style scoped>
.btnBomBox {
  padding: 0px 16px;
  display: flex;
  justify-content: center;
}
</style>

实现效果

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水星记_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值