staff/components/community-selector/community-selector.vue

220 lines
5.0 KiB
Vue

<template>
<view class="community-selector">
<!-- 弹出层 -->
<wd-popup v-model="popupVisible" position="bottom" :safe-area-inset-bottom="true" @close="onCancel" custom-class="community-popup">
<view class="picker-container">
<!-- 头部操作区 -->
<view class="picker-header">
<text class="cancel-btn" @click="onCancel">取消</text>
<text class="title">选择小区</text>
<text class="confirm-btn" @click="onConfirm">确定</text>
</view>
<!-- 小区列表 -->
<scroll-view scroll-y class="community-list">
<view class="community-item"
v-for="(item, index) in communities"
:key="index"
:class="{'active': pickerIndex[0] === index}"
@click="selectCommunity(index)">
<text class="community-item-name">{{ item.label }}</text>
<text v-if="pickerIndex[0] === index" class="ri-check-line check-icon"></text>
</view>
<!-- 底部安全区域 -->
<view class="safe-area-padding"></view>
</scroll-view>
</view>
</wd-popup>
</view>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
const props = defineProps({
// 小区列表数据
communities: {
type: Array,
default: () => []
},
// 当前选中的小区ID
modelValue: {
type: [String, Number],
default: ''
},
// 是否显示弹出层
visible: {
type: Boolean,
default: false
},
communityList: {
type: Array,
default: () => []
},
defaultId: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue', 'update:visible', 'change', 'confirm', 'cancel'])
// 内部状态
const currentIndex = ref([0])
const pickerIndex = ref([0])
// 弹出层可见状态
const popupVisible = computed({
get() {
return props.visible
},
set(value) {
emit('update:visible', value)
}
})
// 设置默认选中的社区
const selectedCommunity = ref(null)
// 监听外部传入的值变化
watch(() => props.modelValue, (newVal) => {
if (newVal) {
const index = props.communities.findIndex(item => item.value === newVal)
if (index !== -1) {
currentIndex.value = [index]
pickerIndex.value = [index]
}
}
}, { immediate: true })
// 监听弹出层显示状态变化
watch(() => props.visible, (newVal) => {
if (newVal) {
// 弹出层显示时,重置选择器的值为当前选中值
const index = props.communities.findIndex(item => item.value === props.modelValue)
if (index !== -1) {
pickerIndex.value = [index]
} else if (props.communities.length > 0) {
pickerIndex.value = [0]
}
}
})
// 当社区列表变更时,自动选择第一个社区或默认社区
watch(() => props.communityList, (list) => {
if (list && list.length > 0) {
if (props.defaultId) {
selectedCommunity.value = list.find(item => item.id === props.defaultId) || list[0]
} else {
selectedCommunity.value = list[0]
}
}
}, { immediate: true })
// 选择小区
const selectCommunity = (index) => {
pickerIndex.value = [index]
}
// 取消选择
const onCancel = () => {
popupVisible.value = false
// 把选择器的值重置为当前已选的值
pickerIndex.value = currentIndex.value
emit('cancel')
}
// 确认选择
const onConfirm = () => {
const selectedIndex = pickerIndex.value[0]
if (selectedIndex >= 0 && selectedIndex < props.communities.length) {
const selectedItem = props.communities[selectedIndex]
currentIndex.value = pickerIndex.value
emit('update:modelValue', selectedItem.value)
emit('change', selectedItem)
emit('confirm', selectedItem)
}
popupVisible.value = false
}
</script>
<style lang="scss" scoped>
.community-selector {
display: inline-flex;
}
.community-popup {
border-radius: 24rpx 24rpx 0 0 !important;
}
.picker-container {
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
display: flex;
flex-direction: column;
max-height: 70vh;
}
.picker-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 110rpx;
padding: 0 30rpx;
border-bottom: 1px solid #f2f2f2;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.02);
position: relative;
.title {
font-size: 34rpx;
font-weight: 500;
color: #333;
}
.cancel-btn {
font-size: 30rpx;
color: #999;
padding: 15rpx 10rpx;
}
.confirm-btn {
font-size: 30rpx;
color: #3e9bff;
font-weight: 500;
padding: 15rpx 10rpx;
}
}
.community-list {
flex: 1;
height: calc(70vh - 110rpx);
}
.community-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 40rpx;
border-bottom: 1px solid #f6f6f6;
&.active {
background-color: #f8f8f8;
}
.community-item-name {
font-size: 32rpx;
color: #333;
}
.check-icon {
font-size: 40rpx;
color: #3e9bff;
}
}
.safe-area-padding {
height: 80rpx;
}
</style>