<template>
|
<u-popup :show="modelValue" mode="bottom" :popup="false" :mask="true" :closeable="true"
|
:safe-area-inset-bottom="true" close-icon-color="#ffffff" :z-index="uZIndex" :maskCloseAble="maskCloseAble"
|
@close="close">
|
<u-tabs v-if="modelValue" :list="genTabsList" :scrollable="true" :current="tabsIndex" @change="tabsChange"
|
ref="tabs" />
|
<view class="area-box">
|
<view class="u-flex" :class="{ 'change': isChange }">
|
<view class="area-item">
|
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
|
<scroll-view :scroll-y="true" style="height: 100%">
|
<u-cell-group>
|
<u-cell v-for="(item, index) in provinces" :title="item.label" :arrow="false"
|
:index="index" :key="index" @click="provinceChange(index)">
|
<template v-slot:right-icon>
|
<u-icon v-if="isChooseP && province === index" size="17"
|
name="checkbox-mark"></u-icon>
|
</template>
|
</u-cell>
|
</u-cell-group>
|
</scroll-view>
|
</view>
|
</view>
|
<view class="area-item">
|
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
|
<scroll-view :scroll-y="true" style="height: 100%">
|
<u-cell-group v-if="isChooseP">
|
<u-cell v-for="(item, index) in citys" :title="item.label" :arrow="false" :index="index"
|
:key="index" @click="cityChange(index)">
|
<template v-slot:right-icon>
|
<u-icon v-if="isChooseC && city === index" size="17"
|
name="checkbox-mark"></u-icon>
|
</template>
|
</u-cell>
|
</u-cell-group>
|
</scroll-view>
|
</view>
|
</view>
|
<view class="area-item">
|
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
|
<scroll-view :scroll-y="true" style="height: 100%">
|
<u-cell-group v-if="isChooseC">
|
<u-cell v-for="(item, index) in areas" :title="item.label" :arrow="false" :index="index"
|
:key="index" @click="areaChange(index)">
|
<template v-slot:right-icon>
|
<u-icon v-if="isChooseA && area === index" size="17"
|
name="checkbox-mark"></u-icon>
|
</template>
|
</u-cell>
|
</u-cell-group>
|
</scroll-view>
|
</view>
|
</view>
|
</view>
|
</view>
|
</u-popup>
|
</template>
|
|
<script setup lang="ts">
|
import { ref, computed, onMounted, PropType } from 'vue';
|
import provincesSource from "./province.js";
|
import citysSource from "./city.js";
|
import areasSource from "./area.js";
|
|
// 定义接口
|
interface Region {
|
label: string;
|
value: string;
|
}
|
|
interface CitySelectResult {
|
province: Region;
|
city: Region;
|
area: Region;
|
}
|
|
interface TabItem {
|
name: string;
|
}
|
|
// Props 定义
|
const props = defineProps({
|
// 通过双向绑定控制组件的弹出与收起
|
modelValue: {
|
type: Boolean,
|
default: false
|
},
|
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
|
defaultRegion: {
|
type: Array as PropType<string[]>,
|
default: () => []
|
},
|
// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
|
areaCode: {
|
type: Array as PropType<string[]>,
|
default: () => []
|
},
|
// 是否允许通过点击遮罩关闭Picker
|
maskCloseAble: {
|
type: Boolean,
|
default: true
|
},
|
// 弹出的z-index值
|
zIndex: {
|
type: [String, Number],
|
default: 0
|
}
|
});
|
|
// 事件定义
|
const emit = defineEmits<{
|
(e: 'update:modelValue', value: boolean): void;
|
(e: 'close'): void;
|
(e: 'city-change', result: CitySelectResult): void;
|
}>();
|
|
const cityValue = ref("");
|
const isChooseP = ref(false); // 是否已经选择了省
|
const province = ref(0); // 省级下标
|
const provinces = ref<Region[]>(provincesSource);
|
const isChooseC = ref(false); // 是否已经选择了市
|
const city = ref(0); // 市级下标
|
const citys = ref<Region[]>(citysSource[0]);
|
const isChooseA = ref(false); // 是否已经选择了区
|
const area = ref(0); // 区级下标
|
const areas = ref<Region[]>(areasSource[0][0]);
|
const tabsIndex = ref(0);
|
const tabs = ref();
|
|
// 计算属性
|
const isChange = computed(() => {
|
return tabsIndex.value > 1;
|
});
|
|
const genTabsList = computed((): TabItem[] => {
|
let tabsList: TabItem[] = [{
|
name: "请选择"
|
}];
|
|
if (isChooseP.value) {
|
tabsList[0].name = provinces.value[province.value].label;
|
tabsList[1] = {
|
name: "请选择"
|
};
|
}
|
|
if (isChooseC.value) {
|
tabsList[1].name = citys.value[city.value].label;
|
tabsList[2] = {
|
name: "请选择"
|
};
|
}
|
|
if (isChooseA.value) {
|
tabsList[2].name = areas.value[area.value].label;
|
}
|
|
return tabsList;
|
});
|
|
const uZIndex = computed(() => {
|
// 如果用户有传递z-index值,优先使用
|
return props.zIndex ? props.zIndex : 1075; // 假设$u.zIndex.popup为1075
|
});
|
|
// 方法
|
const setProvince = (label = "", value = "") => {
|
provinces.value.map((v, k) => {
|
if (value ? v.value == value : v.label == label) {
|
provinceChange(k);
|
}
|
});
|
};
|
|
const setCity = (label = "", value = "") => {
|
citys.value.map((v, k) => {
|
if (value ? v.value == value : v.label == label) {
|
cityChange(k);
|
}
|
});
|
};
|
|
const setArea = (label = "", value = "") => {
|
areas.value.map((v, k) => {
|
if (value ? v.value == value : v.label == label) {
|
isChooseA.value = true;
|
area.value = k;
|
}
|
});
|
};
|
|
const close = () => {
|
emit('update:modelValue', false);
|
emit('close');
|
};
|
|
const tabsChange = (value: { index: number }) => {
|
tabsIndex.value = value.index;
|
};
|
|
const provinceChange = (index: number) => {
|
isChooseP.value = true;
|
isChooseC.value = false;
|
isChooseA.value = false;
|
province.value = index;
|
citys.value = citysSource[index];
|
tabsIndex.value = 1;
|
};
|
|
const cityChange = (index: number) => {
|
isChooseC.value = true;
|
isChooseA.value = false;
|
city.value = index;
|
areas.value = areasSource[province.value][index];
|
tabsIndex.value = 2;
|
};
|
|
const areaChange = (index: number) => {
|
isChooseA.value = true;
|
area.value = index;
|
const result: CitySelectResult = {
|
province: provinces.value[province.value],
|
city: citys.value[city.value],
|
area: areas.value[area.value]
|
};
|
emit('city-change', result);
|
close();
|
};
|
|
// 生命周期钩子
|
onMounted(() => {
|
if (props.areaCode.length == 3) {
|
setProvince("", props.areaCode[0]);
|
setCity("", props.areaCode[1]);
|
setArea("", props.areaCode[2]);
|
} else if (props.defaultRegion.length == 3) {
|
setProvince(props.defaultRegion[0], "");
|
setCity(props.defaultRegion[1], "");
|
setArea(props.defaultRegion[2], "");
|
}
|
});
|
</script>
|
|
<style lang="scss">
|
.area-box {
|
width: 100%;
|
overflow: hidden;
|
height: 800rpx;
|
|
>view {
|
width: 150%;
|
transition: transform 0.3s ease-in-out 0s;
|
transform: translateX(0);
|
|
&.change {
|
transform: translateX(-33.3333333%);
|
}
|
}
|
|
.area-item {
|
width: 33.3333333%;
|
height: 800rpx;
|
}
|
}
|
</style>
|