uniapp 微信小程序 懒加载多级城市联动

本文详细描述了如何在uniapp微信小程序中使用懒加载策略实现一个多级联动的城市选择器组件,包括模板结构、数据处理和组件交互逻辑,以提高性能和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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'
   /**
    * dataList 数据
    * label 要显示的字段
    * rangeKey id 唯一值
    * promptText 显示的文字
    */

   /**
    * 最后一层,通过父级组件调用load(row,[]) 第二个参数为空,自动结束递归
    */

   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()
   }
   /**
    * 点击项,获取子项
    * @param {Object} item
    */
   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) {
		//获取城市数据 cityList
		 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')
	}

### 实现微信小程序多级分类功能 在 `uni-app` 中实现微信小程序多级分类功能通常涉及数据结构设计、页面布局以及交互逻辑处理。以下是具体实现方案: #### 数据准备与获取 为了展示多级分类,首先需要准备好相应的分类数据。这些数据可以从本地静态文件加载或者通过网络请求从服务器端获取。 ```javascript // 假设这是从服务端返回的数据格式 const categories = [ { id: 1, name: '电子产品', children: [ {id: 2, name: '手机'}, {id: 3, name: '电脑'} ] }, { id: 4, name: '生活用品', children: [ {id: 5, name: '家居'}, {id: 6, name: '厨房用具'} ] } ]; ``` #### 页面构建 可以采用列表形式来显示顶级类目,在点击某个项时展开其子类别。这里推荐使用 `picker-view` 或者自定义组件来完成这一操作[^1]。 ##### 使用 picker-view 构建多级联动选择器 `<picker-view>` 是一种适合用来做多列滚动的选择框控件,非常适合用来制作多级菜单。 ```html <picker-view indicator-style="height: 50px;" :value=" pickerViewValue " @change="bindChange"> <picker-view-column> <!-- 渲染一级栏目 --> <div v-for="(item,index) in topCategories" :key="index">{{ item.name }}</div> </picker-view-column> <picker-view-column v-if="currentSecondLevel.length>0"> <!-- 渲染二级栏目 --> <div v-for="(subItem,i) in currentSecondLevel" :key="i">{{ subItem.name }}</div> </picker-view-column> </picker-view> ``` ##### 自定义组件法 如果希望有更灵活的设计,则可以选择创建自己的 Vue 组件来进行渲染。这种方式允许完全控制样式和行为。 ```vue <!-- Category.vue --> <template> <view class="category-container"> <scroll-view scroll-y style="height: calc(100vh - var(--window-top));"> <block v-for="(cat,idx) in categoryList" :key="idx"> <text>{{ cat.name }}</text> <ul v-show="expandedIndexes.includes(idx)"> <li v-for="(child,cIdx) in cat.children" :key="cIdx">{{ child.name }}</li> </ul> </block> </scroll-view> </view> </template> <script> export default { data(){ return{ expandedIndexes:[], // 记录哪些索引被展开了 categoryList:[] // 类别列表 }; }, methods:{ toggleExpand(index){ const idx=this.expandedIndexes.indexOf(index); if (idx>-1)this.expandedIndexes.splice(idx,1);else this.expandedIndexes.push(index); } } }; </script> ``` #### 动态更新视图 当用户选择了不同的选项卡或者是进行了其他类型的互动之后,应该及时刷新界面以反映最新的状态变化。这可以通过监听事件并相应调整内部的状态变量来达成目的。 #### 路由管理 对于较为复杂的场景下可能还需要考虑不同级别的路由切换问题。此时可借助于 `uni.navigateTo()` 方法配合路径参数传递机制来实现在各个层级之间的平滑过渡[^3]。 ```javascript uni.navigateTo({ url:'/pages/categoryDetail/index?id=' + selectedCategoryId }); ``` 以上就是在 `uni-app` 中实现微信小程序多级分类的一些基本思路和技术要点。实际应用过程中可以根据业务需求进一步优化和完善细节部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值