这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
前言
这个不是三级联动的地址组件,这是在这个基础需求上增加多地区选择的功能; 我也不想这么个玩意的,但是产品需求就是有这么个东东。 上基友社区找了下,又木有这类型的组件,只能自己动手了。 虽然过程遇到了许许多多的坑点,但总算是搞出来了。
效果图
获取所有省份城市信息
网址: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
获取省份信息
样式搭建
<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