uniapp 微信小程序 懒加载多级城市联动:
子组件
<template>
<view class="data-picker">
<view class="slot">
<slot>
</slot>
</view>
<view class="data-picker-cont" @tap="handleShowPicker">
<text v-if="!data.activeName" class="pl">{{props.promptText}}</text>
<text v-if="data.activeName">{{data.activeName}}</text>
<uni-icons type="right" size="18" color="#222"></uni-icons>
</view>
</view>
<view class="data-picker-mark" v-if="data.show" @tap="handleClosePicker">
<view class="data-picker-mark-cont" @tap.stop.prevent="">
<view class="data-picker-mark-cont-header">
<view></view>
<view class="data-picker-mark-cont-header-title">请选择{{props.promptText}}</view>
<view class="data-picker-mark-cont-header-close" @tap="handleClosePicker">
<uni-icons type="closeempty" size="20" color="#858585"></uni-icons>
</view>
</view>
<view class="data-picker-mark-cont-tab">
<view v-for="(item,index) in data.tab" :key="index" @tap="handleTab(item)"
class="data-picker-mark-cont-tab-item "
:class="{'data-picker-mark-cont-tab-item-active':item.level==data.activeLevel}">{{item.label}}
</view>
</view>
<view class="data-picker-mark-cont-wrap">
<view class="data-picker-mark-cont-wrap-item" v-for="(item,index) in compAcitveLevelList" :key="index"
@tap="handleAdd(item)">
{{item[props.label]}}
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
onMounted,
reactive,
computed,
watch
} from 'vue'
let props = defineProps(['dataList', 'label', 'rangeKey', 'promptText'])
let emit = defineEmits(['load', 'nodeClick'])
let data = reactive({
show: false,
wrapList: props.dataList,
tab: [{
level: 1,
label: '请选择',
value: '',
data: []
}],
activeLevel: 1,
})
const watchList = watch(() => props.dataList, (value) => {
if (value && value.length > 0) {
data.tab[0].data = value
data.tab[0].data.forEach(item => item.level = 1)
watchList()
}
}, {
deep: true
})
let compAcitveLevelList = computed(() => {
if (data.activeLevel == 1) {
return props.dataList
} else {
let item = data.tab.find(item => item.level == data.activeLevel)
if (item && item.data) {
return item.data
} else {
return []
}
}
})
onMounted(() => {
data.wrapList = props.dataList
})
function load(parentObj, rows) {
let nextLevel = parentObj.level + 1
rows.forEach(item => {
item.level = nextLevel
item.parentId = parentObj[props.rangeKey]
})
let index = parentObj.level - 1
data.tab[index].label = parentObj.text
data.tab[index].value = parentObj.value
if (rows.length <= 0) {
nodeClick()
return false
}
let is = data.tab.find(item => item.label == '请选择')
if (!is) {
data.tab.push({
level: nextLevel,
label: '请选择',
data: rows
})
} else {
let item = data.tab.find(item => item.level == nextLevel)
item.data = []
item.data = rows
}
data.activeLevel = nextLevel
}
function nodeClick() {
let e = {}
e.value = data.tab.map(item => item.value)
e.label = data.tab.map(item => item.label)
data.activeName = e.label.join('-')
emit('nodeClick', e)
handleClosePicker()
}
function handleAdd(item) {
data.tab = data.tab.filter(items => items.level <= item.level)
emit('load', item)
}
function handleTab(item) {
data.activeLevel = item.level
}
function handleClosePicker() {
data.show = false
}
function handleShowPicker() {
data.show = true
}
defineExpose({
load
})
</script>
<style scoped lang="less">
.data-picker {
width: 100%;
height: 100%;
&-cont {
width: 100%;
height: 100%;
display: none;
justify-content: space-between;
align-items: center;
.pl {
color: #858585;
font-size: 28upx;
}
}
&-mark {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, .3);
z-index: 9;
&-cont {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: auto;
max-height: 70vh;
padding: 0 30upx;
box-sizing: border-box;
background-color: #fff;
border-radius: 20upx 20upx 0 0;
z-index: 10;
&-header {
height: 100upx;
display: flex;
justify-content: space-between;
align-items: center;
&-title {
font-size: 30upx;
color: #222;
}
}
&-tab {
width: 100%;
height: 70upx;
border-bottom: 1upx solid #ebebeb;
overflow-x: auto;
white-space: nowrap;
&-item {
width: 100px;
display: inline-block;
height: 100%;
line-height: 70upx;
font-size: 30upx;
color: #222;
text-align: center;
margin-right: 20upx;
&-active {
position: relative;
}
&-active:after {
position: absolute;
content: '';
bottom: 0;
left: 0;
width: 100%;
height: 4upx;
background-color: #3653ff;
}
}
}
&-wrap {
height: 60vh;
overflow: auto;
&-item {
font-size: 28upx;
color: #222;
height: 70upx;
line-height: 70upx;
}
}
}
}
}
.slot:empty+.data-picker-cont {
display: flex !important;
}
</style>
父组件
<dataPicker style="width:100%;height:100%" ref="dataPickerRef"
:dataList="data.cityList"
:label="'text'"
:rangeKey="'value'"
:promptText="'服务地区'"
@load="load"
@nodeClick="nodeClick"></dataPicker>
function load(row) {
dataPickerRef.value.load(row, cityList)
}
function nodeClick(e){
console.log(e,'e')
data.province_code = e.value[0]
data.city_code = e.value[1]
data.area_code = e.value[2]
data.province_name = e.label[0]
data.city_name = e.label[1]
data.area_name = e.label[2]
console.log(data,'data')
}