new-ruoyi-geek/ruoyi-geek-app/components/u-city-select/u-city-select.vue

265 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>