阅读 146

vue多区域选择(vue实现多选)

前言

这个不是三级联动的地址组件,这是在这个基础需求上增加多地区选择的功能; 我也不想这么个玩意的,但是产品需求就是有这么个东东。 上基友社区找了下,又木有这类型的组件,只能自己动手了。 虽然过程遇到了许许多多的坑点,但总算是搞出来了。

效果图

获取所有省份城市信息

网址:gist.github.com/afc163/7582…能够获取所有省份城市信息,我对其做了一个简单的处理

import provinces from "china-division/dist/provinces.json";
import cities from "china-division/dist/cities.json";
import areas from "china-division/dist/areas.json";

areas.forEach(area => {
  const matchCity = cities.filter(city => city.code === area.cityCode)[0];
  if (matchCity) {
    matchCity.children = matchCity.children || [];
    matchCity.children.push({
      label: area.name,
      value: area.name
    });
  }
});

cities.forEach(city => {
  const matchProvince = provinces.filter(
    province => province.code === city.provinceCode
  )[0];
  if (matchProvince) {
    matchProvince.children = matchProvince.children || [];
    matchProvince.children.push({
      label: city.name,
      value: city.name,
      children: city.children
    });
  }
});

const city = provinces.map(province => ({
  label: province.name,
  value: province.name,
  children: province.children
}));
console.log(city)
export default city复制代码

获取省份信息

image.png

样式搭建

<a-table :data-source="tableData" :pagination="false" style="margin: 0 30px">
        <a-table-column key="area" title="可配送区域" data-index="area">
          <template #default="{ record }">
            <span v-html="record.area" :title="record.areaName"></span>
          </template>
        </a-table-column>
        <a-table-column key="frist" title="首件" data-index="frist">
          <template #default="{ record }">
            <a-input-number v-model:value="record.frist" placeholder="请输入"></a-input-number>
          </template>
        </a-table-column>
        <a-table-column key="freight" title="运费(元)" data-index="freight">
          <template #default="{ record }">
            <a-input-number v-model:value="record.freight" placeholder="请输入"></a-input-number>
          </template>
        </a-table-column>
        <a-table-column key="second" title="续件" data-index="second">
          <template #default="{ record }">
            <a-input-number v-model:value="record.second" placeholder="请输入"></a-input-number>
          </template>
        </a-table-column>
        <a-table-column key="secondPrice" title="续件(元)" data-index="secondPrice">
          <template #default="{ record }">
            <a-input-number v-model:value="record.secondPrice" placeholder="请输入"></a-input-number>
          </template>
        </a-table-column>
        <a-table-column key="action" title="操作" data-index="action">
          <template #default="{ record,index }">
            <!-- {{index}} -->
            <span style="color: #f36d69" v-if="record.area !== '全国'" @click="delateArea(index)">删除</span>
          </template>
        </a-table-column>
      </a-table>
      <a-modal
        :title="'选择不包邮区域'"
        :visible="modal_visible"
        @cancel="handleCancel"
        width="1000px"
        @ok="handleAddOk()"
      >
        <area-modal ref="areaCheck" @Area="Area"></area-modal>
      </a-modal>      
复制代码

初始数据和点击显示隐藏事件

let tableData = reactive([
      {
        area: '全国',
        areaName: '全国',
        frist: 1,
        freight: 1111,
        second: 1,
        secondPrice: 111
      }
    ]);
     const showArea = () => {
      modal_visible.value = true;
    };
    const handleCancel = () => {
      modal_visible.value = false;
      // areaCheck.value.checkCanelAlls();
    };复制代码

选择区域模态框样式,我使用popover来控制鼠标移入后显示子集数据。增加checkbox输入来控制选择

<a-row>
    <a-col :span="24">
      <div class="allChoose">
        <input
          type="checkbox"
          @click="checkAll"
          class="box"
          :checked="choose.length === treeData.length"
          :class="0 < choose.length && choose.length < treeData.length ? 'check' : ''"
        />
        全选
        <!-- {{choose}}{{choose.length}} -- {{treeData.length}} -->
      </div>
      <span @click="checkCanelAll">清空</span>
    </a-col>
    <a-col :span="6" v-for="(item, index) in treeData" :key="index">
      <check-box-tree
        :tree-data="item"
        :all-check="all"
        :clear="clear"
        ref="checkBox"
        @chooseValue="chooseValue"
      ></check-box-tree>
    </a-col>
  </a-row>
  
  
  <!--check-box-tree-->
  
  <div class="treeTip">
    <a-popover class="popover" placement="bottom">
      <template #content>
        <div v-for="(item, i) in treeData.children">
          <input
            type="checkbox"
            class="box"
            :checked="checkAreaList.indexOf(item.label) >= 0"
            @click="checkedOne(item.label)"
          />
          {{ item.label }}
        </div>
      </template>
      <div class="treeDate">
        <input
          type="checkbox"
          @click="checkedChildAll()"
          class="box"
          :checked="checkAreaList.length === treeData.children.length"
          :class="0 < checkAreaList.length && checkAreaList.length < treeData.children.length ? 'check' : ''"
        />
        <span>{{ treeData.label }}</span>
        <!-- <span class="chooseNum">({{ checkAreaList.length === 0 ? checkAreaList.length: checkAreaList.length-1}}/{{ treeData.children.length }})</span> -->
        <span class="chooseNum">({{ checkAreaList.length }}/{{ treeData.children.length }})</span>
      </div>
    </a-popover>
  </div>复制代码

单独选中操作

const checkArea = ref({
      area: '' as String | '',
      checkArea: [] as String[]
    });
    const checkAreaList = ref([] as String[]);
    const isFlag = ref(1);
    const checek = ref();
    const checkedOne = (value: String): void => {
      // 同显示,判断是否存在的同时,获取其索引(如果存在的话)
      var idIndex = checkAreaList.value.indexOf(value);
      if (idIndex >= 0) {
        // 如果已经包含了该id, 则去除(单选按钮由选中变为非选中状态)
        checkAreaList.value.splice(idIndex, 1);
      } else {
        // 选中该checkbox
        checkAreaList.value.push(value);
      }
    };复制代码

多选

const checkedChildAll = (): void => {
      if (checkAreaList.value.length === props.treeData?.children?.length) {
        cancel();
      } else {
        props.treeData?.children?.map(item => {
          checkAreaList.value.push(item.label);
        });
        // console.log(checkAreaList.value)
        checkAreaList.value = Array.from(new Set(checkAreaList.value));
        isFlag.value = 1;
      }
    };复制代码

通过监听获取到的选中的信息,将选中的信息传递给父组件,在父组件中,对数据进行一个整合,获取已经选中的全部数据,在对数据进行一次去重,和去除城市为空的数据

const chooseValue = (val: any) => {
      // console.log(val,'-------')
      //获取选中的数据
      if (choose.value.length === 0) {
        choose.value = choose.value.concat([val]);
      } else {
        let isflag = true;
        for (let i = 0; i < choose.value.length; i++) {
          if (choose.value[i].area === val.area) {
            isflag = false;
            choose.value[i].checkArea = val.checkArea;
          }
        }
        if (isflag) {
          choose.value = choose.value.concat([val]);
        }
      }
    };
    watch(()=>choose.value, (newVal)=>{
      for(let i=0;i<newVal.length;i++){
        if(newVal[i].checkArea.length <= 0){
          newVal.splice(i,1)
        }
      }
    },{immediate:true, deep: true})复制代码

全选和取消,分别定义几种状态,传递给子组件,子组件对传过来的数据进行一次判断,调用全选或者对存储的数据进行一次清空。

总结

个人挺菜的,欢迎指正,自己感觉自己写的还有问题,不过暂时没有发现bug。感觉写的太繁琐了,emmmmmmmmm


作者:陌丶_
链接:https://juejin.cn/post/7028826461997891597


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐