277 lines
9.1 KiB
Vue
277 lines
9.1 KiB
Vue
<template>
|
||
<u-popup
|
||
v-model="popupValue"
|
||
:class="customClass"
|
||
:style="$u.toStyle(customStyle)"
|
||
mode="bottom"
|
||
:popup="false"
|
||
:mask="true"
|
||
:closeable="true"
|
||
:safe-area-inset-bottom="true"
|
||
close-icon-color="var(--u-white-color)"
|
||
:z-index="uZIndex"
|
||
:maskCloseAble="props.maskCloseAble"
|
||
@close="close"
|
||
>
|
||
<u-tabs
|
||
v-if="popupValue"
|
||
:list="genTabsList"
|
||
:is-scroll="true"
|
||
:current="tabsIndex"
|
||
@change="tabsChange"
|
||
ref="tabs"
|
||
></u-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-item
|
||
v-for="(item, index) in provincesList"
|
||
:title="item.label"
|
||
:arrow="false"
|
||
:index="index"
|
||
:key="index"
|
||
@click="provinceChange(index)"
|
||
>
|
||
<template v-if="isChooseP && province === index" #right-icon>
|
||
<u-icon size="34" name="checkbox-mark"></u-icon>
|
||
</template>
|
||
</u-cell-item>
|
||
</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-item
|
||
v-for="(item, index) in citys"
|
||
:title="item.label"
|
||
:arrow="false"
|
||
:index="index"
|
||
:key="index"
|
||
@click="cityChange(index)"
|
||
>
|
||
<template v-if="isChooseC && city === index" #right-icon>
|
||
<u-icon size="34" name="checkbox-mark"></u-icon>
|
||
</template>
|
||
</u-cell-item>
|
||
</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-item
|
||
v-for="(item, index) in areas"
|
||
:title="item.label"
|
||
:arrow="false"
|
||
:index="index"
|
||
:key="index"
|
||
@click="areaChange(index)"
|
||
>
|
||
<template v-if="isChooseA && area === index" #right-icon>
|
||
<u-icon size="34" name="checkbox-mark"></u-icon>
|
||
</template>
|
||
</u-cell-item>
|
||
</u-cell-group>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</template>
|
||
|
||
<script lang="ts">
|
||
export default {
|
||
name: 'u-city-select',
|
||
options: {
|
||
addGlobalClass: true,
|
||
// #ifndef MP-TOUTIAO
|
||
virtualHost: true,
|
||
// #endif
|
||
styleIsolation: 'shared'
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import provinces from '../../libs/util/province';
|
||
import citysData from '../../libs/util/city';
|
||
import areasData from '../../libs/util/area';
|
||
import { CitySelectProps } from './types';
|
||
import { $u } from '../..';
|
||
|
||
/**
|
||
* u-city-select 城市选择器
|
||
* @description 用于选择省、市、区三级行政区域,支持回显和自定义初始值。
|
||
* @property {Boolean} modelValue 控制弹窗显示与隐藏(v-model)
|
||
* @property {Array} defaultRegion 默认选中的省市区名称数组,如 ['山东省', '青岛市', '崂山区']
|
||
* @property {Array} areaCode 默认选中的省市区编码数组,如 ['440000', '440100', '440106']
|
||
* @property {Boolean} maskCloseAble 是否允许点击遮罩关闭弹窗(默认 true)
|
||
* @property {String|Number} zIndex 弹窗层级(默认 0,自动适配)
|
||
* @event update:modelValue v-model 绑定值变化时触发
|
||
* @event city-change 选择省市区后触发,返回选中的省市区对象
|
||
* @example <u-city-select v-model="show" :defaultRegion="['山东省', '青岛市', '崂山区']" @city-change="onChange"></u-city-select>
|
||
*/
|
||
const props = defineProps(CitySelectProps);
|
||
|
||
// emits 定义
|
||
const emit = defineEmits(['update:modelValue', 'city-change']);
|
||
|
||
// 省市区选择相关响应式数据
|
||
const cityValue = ref('');
|
||
const isChooseP = ref(false); // 是否已选省
|
||
const province = ref(0); // 省下标
|
||
const provincesList = ref(provinces);
|
||
const isChooseC = ref(false); // 是否已选市
|
||
const city = ref(0); // 市下标
|
||
const citys = ref(citysData[0]);
|
||
const isChooseA = ref(false); // 是否已选区
|
||
const area = ref(0); // 区下标
|
||
const areas = ref(areasData[0][0]);
|
||
const tabsIndex = ref(0);
|
||
|
||
// v-model 双向绑定
|
||
const popupValue = computed({
|
||
get: () => props.modelValue,
|
||
set: val => emit('update:modelValue', val)
|
||
});
|
||
|
||
// 是否切换到区级
|
||
const isChange = computed(() => tabsIndex.value > 1);
|
||
|
||
// 顶部 tab 列表
|
||
const genTabsList = computed(() => {
|
||
const tabsList = [{ name: '请选择' }];
|
||
if (isChooseP.value) {
|
||
tabsList[0].name = provincesList.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;
|
||
});
|
||
|
||
// z-index 计算
|
||
const uZIndex = computed(() => (props.zIndex ? props.zIndex : (uni as any).$u?.zIndex?.popup || 1075));
|
||
|
||
// 初始化选中项
|
||
function init() {
|
||
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], '');
|
||
}
|
||
}
|
||
|
||
// 选中省
|
||
function setProvince(label = '', value = '') {
|
||
provincesList.value.forEach((v, k) => {
|
||
if (value ? v.value == value : v.label == label) {
|
||
provinceChange(k);
|
||
}
|
||
});
|
||
}
|
||
// 选中市
|
||
function setCity(label = '', value = '') {
|
||
citys.value.forEach((v, k) => {
|
||
if (value ? v.value == value : v.label == label) {
|
||
cityChange(k);
|
||
}
|
||
});
|
||
}
|
||
// 选中区
|
||
function setArea(label = '', value = '') {
|
||
areas.value.forEach((v, k) => {
|
||
if (value ? v.value == value : v.label == label) {
|
||
isChooseA.value = true;
|
||
area.value = k;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 关闭弹窗
|
||
function close() {
|
||
emit('update:modelValue', false);
|
||
}
|
||
// tab 切换
|
||
function tabsChange(index: number) {
|
||
tabsIndex.value = index;
|
||
}
|
||
// 省点击
|
||
function provinceChange(index: number) {
|
||
isChooseP.value = true;
|
||
isChooseC.value = false;
|
||
isChooseA.value = false;
|
||
province.value = index;
|
||
citys.value = citysData[index];
|
||
tabsIndex.value = 1;
|
||
}
|
||
// 市点击
|
||
function cityChange(index: number) {
|
||
isChooseC.value = true;
|
||
isChooseA.value = false;
|
||
city.value = index;
|
||
areas.value = areasData[province.value][index];
|
||
tabsIndex.value = 2;
|
||
}
|
||
// 区点击
|
||
function areaChange(index: number) {
|
||
isChooseA.value = true;
|
||
area.value = index;
|
||
const result = {
|
||
province: provincesList.value[province.value],
|
||
city: citys.value[city.value],
|
||
area: areas.value[area.value]
|
||
};
|
||
emit('city-change', result);
|
||
close();
|
||
}
|
||
|
||
// 挂载时初始化
|
||
onMounted(init);
|
||
</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>
|