看板
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import { baseProps } from '../common/props';
|
||||
|
||||
/**
|
||||
* u-city-select 城市选择器 Props
|
||||
* @description 用于选择省、市、区三级行政区域,支持回显和自定义初始值。
|
||||
*/
|
||||
export const CitySelectProps = {
|
||||
...baseProps,
|
||||
/** 控制弹窗显示与隐藏(v-model) */
|
||||
modelValue: { type: Boolean, default: false },
|
||||
/** 默认选中的省市区名称数组 */
|
||||
defaultRegion: { type: Array as PropType<string[]>, default: () => [] },
|
||||
/** 默认选中的省市区编码数组 */
|
||||
areaCode: { type: Array as PropType<string[]>, default: () => [] },
|
||||
/** 是否允许点击遮罩关闭弹窗 */
|
||||
maskCloseAble: { type: Boolean, default: true },
|
||||
/** 弹窗层级 */
|
||||
zIndex: { type: [String, Number] as PropType<string | number>, default: 0 }
|
||||
};
|
||||
|
||||
export type CitySelectProps = ExtractPropTypes<typeof CitySelectProps>;
|
||||
@@ -0,0 +1,276 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user