Files
delivery-uniapp/sheep/ui/su-region-picker/su-region-picker.vue

278 lines
7.5 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>
<su-popup :show="show" @close="onCancel" round="20">
<view class="ui-region-picker">
<su-toolbar
:cancelColor="cancelColor"
:confirmColor="confirmColor"
:cancelText="cancelText"
:confirmText="confirmText"
title="选择区域"
@cancel="onCancel"
@confirm="onConfirm('confirm')"
/>
<view class="ui-picker-body">
<picker-view
:value="state.currentIndex"
@change="change"
class="ui-picker-view"
@pickstart="pickstart"
@pickend="pickend"
>
<!-- 省级选择列 -->
<picker-view-column>
<view class="ui-column-item" v-for="province in provinceList" :key="province.id">
<view :style="getSizeByNameLength(province.name)">{{ province.name }}</view>
</view>
</picker-view-column>
<!-- 市级选择列 -->
<picker-view-column v-if="props.level >= 2">
<view class="ui-column-item" v-for="city in cityList" :key="city.id">
<view :style="getSizeByNameLength(city.name)">{{ city.name }}</view>
</view>
</picker-view-column>
<!-- 区级选择列 -->
<picker-view-column v-if="props.level >= 3">
<view class="ui-column-item" v-for="district in districtList" :key="district.id">
<view :style="getSizeByNameLength(district.name)">{{ district.name }}</view>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</su-popup>
</template>
<script setup>
/**
* picker picker弹出选择器
* @property {Object} params 需要显示的参
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配默认false
* @property {Boolean} show-time-tag 时间模式时,是否显示后面的年月日中文提示
* @property {String} cancel-color 取消按钮的颜色
* @property {String} confirm-color 确认按钮的颜色
* @property {String} confirm-text 确认按钮的文字
* @property {String} cancel-text 取消按钮的文字
* @property {String} default-region 默认选中的地区,
* @property {String} default-code 默认选中的地区
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker默认true
* @property {String Number} z-index 弹出时的z-index值默认1075
* @property {Array} default-selector 数组形式其中每一项表示选择了range对应项中的第几个
* @property {String} range-key 当range参数的元素为对象时指定Object中的哪个key的值作为选择器显示内容
* @property {Number} level 选择器层级1-只显示省2-显示省市3-显示省市区默认3
* @event {Function} confirm 点击确定按钮,返回当前选择的值
* @event {Function} cancel 点击取消按钮,返回当前选择的值
*/
import { computed, reactive } from 'vue';
const props = defineProps({
show: {
type: Boolean,
default: false,
},
// "取消"按钮的颜色
cancelColor: {
type: String,
default: '#6666',
},
// "确定"按钮的颜色
confirmColor: {
type: String,
default: 'var(--ui-BG-Main)',
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消',
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确认',
},
// 选择器层级1-只显示省2-显示省市3-显示省市区
level: {
type: Number,
default: 3,
validator: (value) => [1, 2, 3].includes(value),
},
});
const areaData = uni.getStorageSync('areaData');
const getSizeByNameLength = (name) => {
let length = name.length;
if (length <= 7) return '';
if (length < 9) {
return 'font-size:28rpx';
} else {
return 'font-size: 24rpx';
}
};
const state = reactive({
currentIndex: Array(props.level).fill(0),
moving: false, // 列是否还在滑动中,微信小程序如果在滑动中就点确定,结果可能不准确
});
const emits = defineEmits(['confirm', 'cancel', 'change']);
const provinceList = areaData;
const cityList = computed(() => {
const province = areaData?.[state.currentIndex[0]];
return province?.children || [];
});
const districtList = computed(() => {
const city = cityList.value?.[state.currentIndex[1]];
return city?.children || [];
});
// 标识滑动开始,只有微信小程序才有这样的事件
const pickstart = () => {
// #ifdef MP-WEIXIN
state.moving = true;
// #endif
};
// 标识滑动结束
const pickend = () => {
// #ifdef MP-WEIXIN
state.moving = false;
// #endif
};
const init = () => {};
const onCancel = () => {
emits('cancel');
};
// 用户更改picker的列选项
const change = (e) => {
const newIndex = [...e.detail.value];
let shouldResetSubsequent = false;
// 检查每一列是否发生变化,如果上级发生变化,需要重置下级
for (let i = 0; i < props.level; i++) {
if (state.currentIndex[i] !== newIndex[i]) {
shouldResetSubsequent = true;
// 重置当前列之后的所有列
for (let j = i + 1; j < props.level; j++) {
newIndex[j] = 0;
}
break;
}
}
if (shouldResetSubsequent) {
state.currentIndex = newIndex;
} else {
// 只有最后一列变化,不需要重置
state.currentIndex = newIndex;
}
emits('change', state.currentIndex);
};
// 用户点击确定按钮
const onConfirm = (event = null) => {
// #ifdef MP-WEIXIN
if (state.moving) return;
// #endif
let index = state.currentIndex;
let province = provinceList?.[index[0]];
if (!province) return;
let result = {
province_name: province.name,
province_id: province.id,
};
if (props.level >= 2) {
let city = cityList.value?.[index[1]];
if (city?.name) {
result.city_name = city.name;
result.city_id = city.id;
}
}
if (props.level >= 3) {
let district = districtList.value?.[index[2]];
if (district?.name) {
result.district_name = district.name;
result.district_id = district.id;
}
}
if (event) emits(event, result);
};
</script>
<style lang="scss" scoped>
.ui-region-picker {
position: relative;
z-index: 999;
}
.ui-picker-view {
height: 100%;
box-sizing: border-box;
}
.ui-picker-header {
width: 100%;
height: 90rpx;
padding: 0 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
font-size: 30rpx;
background: #fff;
position: relative;
}
.ui-picker-header::after {
content: '';
position: absolute;
border-bottom: 1rpx solid #eaeef1;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
bottom: 0;
right: 0;
left: 0;
}
.ui-picker__title {
color: #333;
}
.ui-picker-body {
width: 100%;
height: 500rpx;
overflow: hidden;
background-color: #fff;
}
.ui-column-item {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #333;
padding: 0 8rpx;
}
.ui-btn-picker {
padding: 16rpx;
box-sizing: border-box;
text-align: center;
text-decoration: none;
}
.ui-opacity {
opacity: 0.5;
}
.ui-btn-picker--primary {
color: blue;
}
.ui-btn-picker--tips {
color: red;
}
</style>