根组件使用thorUI的级联选择器
效果
在这里插入图片描述在这里插入图片描述

<template>
	<view class="tui-cascade-selection">
		<scroll-view scroll-x scroll-with-animation :scroll-into-view="scrollViewId"
			:style="{ backgroundColor: headerBgColor }" class="tui-bottom-line"
			:class="{ 'tui-btm-none': !headerLine }">
			<view class="tui-selection-header" :style="{ height: tabsHeight, backgroundColor: backgroundColor }">
				<view class="tui-header-item" :class="{ 'tui-font-bold': idx === currentTab && bold }"
					:style="{ color: idx === currentTab ? activeColor : color, fontSize: size + 'rpx' }"
					:id="`id_${idx}`" @tap.stop="swichNav" :data-current="idx" v-for="(item, idx) in selectedArr"
					:key="idx">
					{{ item.text }}
					<view class="tui-active-line" :style="{ backgroundColor: lineColor }"
						v-if="idx === currentTab && showLine"></view>
				</view>
			</view>
		</scroll-view>
		<swiper class="tui-selection-list" :current="defTab" duration="300" @change="switchTab"
			:style="{ height: height, backgroundColor: backgroundColor }">
			<swiper-item v-for="(item, index) in selectedArr" :key="index">
				<scroll-view scroll-y :scroll-into-view="item.scrollViewId" class="tui-selection-item"
					:style="{ height: height }">
					<view class="tui-first-item" :style="{ height: firstItemTop }"></view>
					<view class="tui-selection-cell" :style="{ padding: padding, backgroundColor: backgroundColor }"
						:id="`id_${subIndex}`" v-for="(subItem, subIndex) in item.list" :key="subIndex"
						@tap.stop="change(index, subIndex, subItem)">
						<icon type="success_no_circle" v-if="item.index === subIndex" :color="checkMarkColor"
							:size="checkMarkSize" class="tui-icon-success"></icon>
						<image :src="subItem.src" v-if="subItem.src" class="tui-cell-img"
							:style="{ width: imgWidth, height: imgHeight, borderRadius: radius }"></image>
						<view class="tui-cell-title"
							:class="{ 'tui-font-bold': item.index === subIndex && textBold, 'tui-flex-shrink': nowrap }"
							:style="{ color: item.index === subIndex ? textActiveColor : textColor, fontSize: textSize + 'rpx' }">
							{{ subItem.text }}
						</view>
						<view class="tui-cell-sub_title" :style="{ color: subTextColor, fontSize: subTextSize + 'rpx' }"
							v-if="subItem.subText">{{ subItem.subText }}</view>
					</view>
				</scroll-view>
			</swiper-item>
		</swiper>
	</view>
</template>

<script>
	export default {
		name: 'tuiCascadeSelection',
		emits: ['change', 'complete', 'update'],
		props: {
			/**
				 * 如果下一级是请求返回,则为第一级数据,否则所有数据
				 * 数据格式
				  [{
					  src: "",
					  text: "",
					  subText: "",
					  value: 0,
					  children:[{
						  text: "",
						  subText: "",
						  value: 0,
						  children:[]
				   }]
				  }]
				 * */
			itemList: {
				type: Array,
				default: () => {
					return [];
				}
			},
			/*
			   初始化默认选中数据
			   [{
				text: "",//选中text
				subText: '',//选中subText
				value: '',//选中value
				src: '', //选中src,没有则传空或不传
				index: 0, //选中数据在当前layer索引
				list: [{src: "", text: "", subText: "", value: 101}] //当前layer下所有数据集合
			  }];
			    
			   */
			defaultItemList: {
				type: Array,
				default () {
					return []
				}
			},
			defaultKey: {
				type: String,
				default: 'text'
			},
			//是否显示header底部细线
			headerLine: {
				type: Boolean,
				default: true
			},
			//header背景颜色
			headerBgColor: {
				type: String,
				default: '#FFFFFF'
			},
			//顶部标签栏高度
			tabsHeight: {
				type: String,
				default: '88rpx'
			},
			//默认显示文字
			text: {
				type: String,
				default: '请选择'
			},
			//tabs 文字大小
			size: {
				type: Number,
				default: 28
			},
			//tabs 文字颜色
			color: {
				type: String,
				default: '#555'
			},
			//选中颜色
			activeColor: {
				type: String,
				default: '#5677fc'
			},
			//选中后文字加粗
			bold: {
				type: Boolean,
				default: true
			},
			//选中后是否显示底部线条
			showLine: {
				type: Boolean,
				default: true
			},
			//线条颜色
			lineColor: {
				type: String,
				default: '#5677fc'
			},
			//icon 大小
			checkMarkSize: {
				type: Number,
				default: 15
			},
			//icon 颜色
			checkMarkColor: {
				type: String,
				default: '#5677fc'
			},
			//item 图片宽度
			imgWidth: {
				type: String,
				default: '40rpx'
			},
			//item 图片高度
			imgHeight: {
				type: String,
				default: '40rpx'
			},
			//图片圆角
			radius: {
				type: String,
				default: '50%'
			},
			//item text颜色
			textColor: {
				type: String,
				default: '#333'
			},
			textActiveColor: {
				type: String,
				default: '#333'
			},
			//选中后字体是否加粗
			textBold: {
				type: Boolean,
				default: true
			},
			//item text字体大小
			textSize: {
				type: Number,
				default: 28
			},
			//text 是否不换行
			nowrap: {
				type: Boolean,
				default: false
			},
			//item subText颜色
			subTextColor: {
				type: String,
				default: '#999'
			},
			//item subText字体大小
			subTextSize: {
				type: Number,
				default: 24
			},
			// item padding
			padding: {
				type: String,
				default: '20rpx 30rpx'
			},
			//占位高度,第一条数据距离顶部距离
			firstItemTop: {
				type: String,
				default: '20rpx'
			},
			//swiper 高度
			height: {
				type: String,
				default: '300px'
			},
			//item  swiper 内容部分背景颜色
			backgroundColor: {
				type: String,
				default: '#FFFFFF'
			},
			//子集数据是否请求返回(默认false,一次性返回所有数据)
			request: {
				type: Boolean,
				default: false
			},
			//子级数据(当有改变时,默认当前选中项新增子级数据,request=true时生效)
			receiveData: {
				type: Array,
				default: () => {
					return [];
				}
			},
			//改变值则重置数据
			reset: {
				type: [Number, String],
				default: 0
			}
		},
		watch: {
			itemList(val) {
				this.initData(val, -1);
			},
			receiveData(val) {
				this.subLevelData(val, this.currentTab);
			},
			reset() {
				this.initData(this.itemList, -1);
			},
			defaultItemList(val) {
				this.setDefaultData(val)
			}
		},
		created() {
			this.setDefaultData(this.defaultItemList)
		},
		data() {
			return {
				currentTab: 0,
				defTab: 0,
				//tab栏scrollview滚动的位置
				scrollViewId: 'id__1',
				selectedArr: [],
				valueText: ''
			};
		},
		methods: {
			setDefaultData(val) {
				let defaultItemList = JSON.parse(JSON.stringify(val || []));
				if (defaultItemList.length > 0) {
					if ((typeof defaultItemList[0] === 'string' || typeof defaultItemList[0] === 'number') && !this
						.request) {
						let subi = -1
						let selectedArr = []
						for (let j = 0, len = defaultItemList.length; j < len; j++) {
							let item = defaultItemList[j]
							let list = []
							let obj = {}
							if (j === 0) {
								list = this.getItemList(-1)
							} else {
								list = this.getItemList(j - 1, subi, selectedArr)
							}
							subi = this.getDefaultIndex(list, item)
							if (subi !== -1) {
								obj = list[subi]
								selectedArr.push({
									text: obj.text || this.text,
									value: obj.value || '',
									src: obj.src || '',
									subText: obj.subText || '',
									index: subi,
									scrollViewId: `id_${subi}`,
									list: list
								})
							}

							if (subi === -1) break;
						}
						this.selectedArr = selectedArr;
						this.defTab = this.currentTab;
						this.$nextTick(() => {
							setTimeout(() => {
								this.currentTab = selectedArr.length - 1;
								this.defTab = this.currentTab;
								this.checkCor();
							}, 20)
						});
					} else {
						defaultItemList.map(item => {
							item.scrollViewId = `id_${item.index}`;
						});
						this.selectedArr = defaultItemList;
						this.defTab = this.currentTab;
						this.$nextTick(() => {
							setTimeout(() => {
								this.currentTab = defaultItemList.length - 1;
								this.defTab = this.currentTab;
								this.checkCor();
							}, 20)
						});
					}

				} else {
					this.initData(this.itemList, -1);
				}
			},
			getDefaultIndex(arr, val) {
				if (!arr || arr.length === 0 || val === undefined) return -1;
				let index = -1;
				let key = this.defaultKey || 'text'
				for (let i = 0, len = arr.length; i < len; i++) {
					if (arr[i][key] == val) {
						index = i;
						break;
					}
				}
				return index;
			},
			initData(data, layer) {
				if (!data || data.length === 0) return;
				if (this.request) {
					//第一级数据
					this.subLevelData(data, layer);
				} else {
					let selectedValue = this.selectedValue || {};
					if (selectedValue.type) {
						this.setDefaultData(selectedValue);
					} else {
						this.subLevelData(this.getItemList(layer, -1), layer);
					}
				}
			},
			removeChildren(data) {
				let list = data.map(item => {
					delete item['children'];
					return item;
				});
				return list;
			},
			getItemList(layer, index, selectedArr) {
				let list = [];
				let arr = JSON.parse(JSON.stringify(this.itemList));
				selectedArr = selectedArr || this.selectedArr
				if (layer == -1) {
					list = this.removeChildren(arr);
				} else {
					let value = selectedArr[0].index;
					value = value === undefined || value == -1 ? index : value;
					if (arr[value] && arr[value].children) {
						list = arr[value].children;
					}
					if (layer > 0) {
						for (let i = 1; i < layer + 1; i++) {
							let val = layer === i ? index : selectedArr[i].index;
							list = val === -1 ? [] : (list[val].children || []);
							if (list.length === 0) break;
						}
					}
					list = this.removeChildren(list);
				}
				return list;
			},
			//滚动切换
			switchTab: function(e) {
				this.currentTab = e.detail.current;
				this.checkCor();
			},
			//点击标题切换当
			swichNav: function(e) {
				let cur = e.currentTarget.dataset.current;
				if (this.currentTab != cur) {
					this.defTab = this.currentTab;
					setTimeout(() => {
						this.currentTab = cur;
						this.defTab = this.currentTab;
					}, 20)
				}
			},
			checkCor: function() {
				let item = this.selectedArr[this.currentTab];
				item.scrollViewId = 'id__1';
				this.$nextTick(() => {
					setTimeout(() => {
						let val = item.index < 2 ? 0 : Number(item.index - 2);
						item.scrollViewId = `id_${val}`;
					}, 20);
				});

				if (this.currentTab > 1) {
					this.scrollViewId = `id_${this.currentTab - 1}`;
				} else {
					this.scrollViewId = `id_0`;
				}
			},
			change(index, subIndex, subItem) {
				let item = this.selectedArr[index];
				if (item.index == subIndex) {
					this.$emit('update', this.valueText);
				}
				item.index = subIndex;
				item.text = subItem.text;
				item.value = subItem.value;
				item.subText = subItem.subText || '';
				item.src = subItem.src || '';
				this.$emit('change', {
					layer: index,
					subIndex: subIndex, //layer=> Array index
					...subItem
				});

				if (!this.request) {
					let data = this.getItemList(index, subIndex);
					this.subLevelData(data, index);
				}
			},
			//新增子级数据时处理
			subLevelData(data, layer) {
				if (!data || data.length === 0) {
					if (layer == -1) return;
					//完成选择
					let arr = this.selectedArr;
					if (layer < arr.length - 1) {
						let newArr = arr.slice(0, layer + 1);
						this.selectedArr = newArr;
					}
					let result = JSON.parse(JSON.stringify(this.selectedArr));
					let lastItem = result[result.length - 1] || {};
					let text = '';
					result.map(item => {
						text += item.text+'/';
						delete item['list'];
						//delete item['index'];
						delete item['scrollViewId'];
						return item;
					});
					this.$emit('complete', {
						result: result,
						value: lastItem.value,
						text: text.replace(/[&\/]$/,''),
						subText: lastItem.subText,
						src: lastItem.src
					});
				} else {
					//重置数据( >layer层级)


					let item = [{
						text: this.text,
						subText: '',
						value: '',
						src: '',
						index: -1,
						scrollViewId: 'id__1',
						list: data
					}];
					if (layer == -1) {
						this.selectedArr = item;
					} else {
						let retainArr = this.selectedArr.slice(0, layer + 1) || [];
						this.selectedArr = retainArr.concat(item);
					}

					let current = this.selectedArr.length - 1;
					if (current >= this.currentTab) {
						this.defTab = this.currentTab
					}
					this.$nextTick(() => {
						setTimeout(() => {
							this.defTab = current;
							this.currentTab = current;
							this.scrollViewId = `id_${this.currentTab > 1?this.currentTab - 1:0}`;
						}, 50)

					});
					let result = JSON.parse(JSON.stringify(this.selectedArr));
					let lastItem = result[result.length>2?result.length - 2:0] || {};
					let text = '';
					result.map(item => {
						text += item.text+'/';
						delete item['list'];
						//delete item['index'];
						delete item['scrollViewId'];
						return item;
					});
					this.valueText = text.replace('/请选择/', '')
					this.$emit('update', {
						value: lastItem.value,
						text: this.valueText
					});
				}
			},
			clear() {
				this.initData(this.itemList, -1);
			}
		}
	};
</script>

<style scoped>
	.tui-cascade-selection {
		width: 100%;
		box-sizing: border-box;
	}

	.tui-selection-header {
		width: 100%;
		display: flex;
		align-items: center;
		position: relative;
		box-sizing: border-box;
	}

	.tui-bottom-line {
		position: relative;
	}

	.tui-bottom-line::after {
		width: 100%;
		content: '';
		position: absolute;
		border-bottom: 1rpx solid #eaeef1;
		-webkit-transform: scaleY(0.5) translateZ(0);
		transform: scaleY(0.5) translateZ(0);
		transform-origin: 0 100%;
		bottom: 0;
		right: 0;
		left: 0;
	}

	.tui-btm-none::after {
		border-bottom: 0 !important;
	}

	.tui-header-item {
		max-width: 240rpx;
		padding: 15rpx 30rpx;
		box-sizing: border-box;
		flex-shrink: 0;
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
		position: relative;
	}

	.tui-font-bold {
		font-weight: bold;
	}

	.tui-active-line {
		width: 60rpx;
		height: 6rpx;
		border-radius: 4rpx;
		position: absolute;
		bottom: 0;
		right: 0;
		left: 50%;
		transform: translateX(-50%);
	}

	.tui-selection-cell {
		width: 100%;
		box-sizing: border-box;
		display: flex;
		align-items: center;
	}

	.tui-icon-success {
		margin-right: 12rpx;
	}

	.tui-cell-img {
		margin-right: 12rpx;
		flex-shrink: 0;
	}

	.tui-cell-title {
		word-break: break-all;
	}

	.tui-flex-shrink {
		flex-shrink: 0;
	}

	.tui-font-bold {
		font-weight: bold;
	}

	.tui-cell-sub_title {
		margin-left: 20rpx;
		word-break: break-all;
	}

	.tui-first-item {
		width: 100%;
	}
</style>

<template>
	<view class="uni-data-tree">
		<view class="uni-data-tree-input" @click="handleInput">
			<slot :options="options" :data="gridValue" :error="errorMessage">
				<view class="input-value" :class="{'input-value-border': border}">
					<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text>
					<view v-else-if="loading && !isOpened" class="selected-area">
						<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more>
					</view>
					<scroll-view v-else-if="gridValue" class="selected-area" scroll-x="true">
						<view class="selected-list">
							<view class="selected-item" >
								<text>{{gridText}}</text>
							</view>
						</view>
					</scroll-view>
					<text v-else class="selected-area placeholder">{{placeholder}}</text>
					<view v-if="clearIcon && !readonly && gridValue" class="icon-clear" @click.stop="clear">
						<uni-icons type="clear" color="#e1e1e1" size="14"></uni-icons>
					</view>
					<view class="arrow-area" v-if="(!clearIcon || !gridValue) && !readonly ">
						<view class="input-arrow"></view>
					</view>
				</view>
			</slot>
		</view>
		<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view>
		<view class="uni-data-tree-dialog" v-show="isOpened">
			<view class="uni-popper__arrow"></view>
			<view class="dialog-caption">
				<view class="title-area">
					<text class="dialog-title">{{popupTitle}}</text>
				</view>
				<view class="dialog-close" @click="handleClose">
					<view class="dialog-close-plus" data-id="close"></view>
					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
				</view>
			</view>
			<tui-cascade-selection
				ref="gridSelet"
				height="280px"
				:activeColor="color"
				:lineColor="color"
				:checkMarkColor="color"
				:itemList="parentDept"
				request
				:receiveData="receiveData"
				@complete="complete"
				@change="change"
				@update="update"
			></tui-cascade-selection>
		</view>
	</view>
</template>

<script>
	import tuiCascadeSelection from "@/components/thorui/tui-cascade-selection/tui-cascade-selection"
	import {departMent} from "@/api/deptList.js"
	export default {
		name: "grid-cascade-selection",
		data() {
			return {
				parentDept: [],
				receiveData: [],
				isOpened: false,
				gridValue: null,
				gridText: ''
			};
		},
		components: {
			tuiCascadeSelection
		},
		emits: ['popupopened', 'popupclosed', 'input', 'change', 'update:modelValue'],
		created() {
			this.getDeptList(1).then((res)=>{
				this.parentDept = res
			}).catch((err)=>{
				
			})
		},
		props: {
			color: {
				type: String,
				default: '#007aff'
			},
			options: {
				type: [Object, Array],
				default () {
					return {}
				}
			},
			placeholder: {
				type: String,
				default: '请选择'
			},
			popupTitle: {
				type: String,
				default: '请选择'
			},
			heightMobile: {
				type: String,
				default: ''
			},
			readonly: {
				type: Boolean,
				default: false
			},
			clearIcon: {
				type: Boolean,
				default: true
			},
			border: {
				type: Boolean,
				default: true
			}
		},
		methods: {
			clear() {
				this.gridText = ''
				this.gridValue = null
				this.$emit('input', this.gridValue)
				this.$emit('update:modelValue', this.gridValue)
				this.$refs.gridSelet.clear()
			},
			getDeptList(id) {
				return departMent({id:id}).then((res)=>{
					return res.data.list.map((item)=>{
						return {
							text: item.deptName,
							value: item.ID
						}
					})
				}).catch((err)=>{
				})
			},
			change(e) {
				/**
				 *   layer: 0  第几级 index
					 src: '/static/images/basic/color.png'
					 subIndex: 2   //当前层级下选中项index
					 subText: '30人'  //选中项数据
					 text: '高一(3)班'
					 value: 103 //选中项value数据
				 * */
			
				// 模拟请求
				let value = e.value;
				let layer = e.layer;
				if (layer === 3) {
					//实际中以请求数据为准,无下级数据则传空数组
					this.receiveData = [];
				} else {
					uni.showLoading({
						title: '请稍候...'
					});
					this.getDeptList(value).then((res)=>{
						this.receiveData =res.length?res:[]
						uni.hideLoading();
					}).catch((err)=>{
						
					})
				}
			},
			complete(e) {
				this.gridText = e.text
				this.gridValue = e.value
				this.isOpened = false
				this.$emit('input', this.gridValue)
				this.$emit('update:modelValue', this.gridValue)
				this.$emit('popupclosed')
			},
			update(value) {
				this.gridText = value.text
				this.gridValue = value.value
				this.$emit('input', this.gridValue)
				this.$emit('update:modelValue', this.gridValue)
			},
			show() {
				this.isOpened = true
				this.$emit('popupopened')
			},
			hide() {
				this.isOpened = false
				this.$emit('popupclosed')
			},
			handleInput() {
				if (this.readonly) {
					return
				}
				this.show()
			},
			handleClose(e) {
				this.hide()
			}
		}
	}
</script>

<style>
.uni-data-tree {
		position: relative;
		font-size: 14px;
	}

	.error-text {
		color: #DD524D;
	}

	.input-value {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		align-items: center;
		flex-wrap: nowrap;
		font-size: 14px;
		line-height: 38px;
		padding: 0 5px;
		overflow: hidden;
		/* #ifdef APP-NVUE */
		height: 40px;
		/* #endif */
	}

	.input-value-border {
		border: 1px solid #e5e5e5;
		border-radius: 5px;
	}

	.selected-area {
		flex: 1;
		overflow: hidden;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
	}

	.load-more {
		/* #ifndef APP-NVUE */
		margin-right: auto;
		/* #endif */
		/* #ifdef APP-NVUE */
		width: 40px;
		/* #endif */
	}

	.selected-list {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		flex-wrap: nowrap;
		padding: 0 5px;
	}

	.selected-item {
		flex-direction: row;
		padding: 0 1px;
		/* #ifndef APP-NVUE */
		white-space: nowrap;
		/* #endif */
	}

	.placeholder {
		font-size: 24rpx;
		color: #bbbbbb;
	}

	.input-split-line {
		opacity: .5;
	}

	.arrow-area {
		position: relative;
		width: 20px;
		/* #ifndef APP-NVUE */
		margin-bottom: 5px;
		margin-left: auto;
		display: flex;
		/* #endif */
		justify-content: center;
		transform: rotate(-45deg);
		transform-origin: center;
	}

	.input-arrow {
		width: 7px;
		height: 7px;
		border-left: 1px solid #999;
		border-bottom: 1px solid #999;
	}

	.uni-data-tree-cover {
		position: fixed;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, .4);
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
		z-index: 100;
	}

	.uni-data-tree-dialog {
		position: fixed;
		left: 0;
		top: 20%;
		right: 0;
		bottom: 0;
		background-color: #FFFFFF;
		border-top-left-radius: 10px;
		border-top-right-radius: 10px;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
		z-index: 102;
		overflow: hidden;
		/* #ifdef APP-NVUE */
		width: 750rpx;
		/* #endif */
	}

	.dialog-caption {
		position: relative;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		/* border-bottom: 1px solid #f0f0f0; */
	}

	.title-area {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		/* #ifndef APP-NVUE */
		margin: auto;
		/* #endif */
		padding: 0 10px;
	}

	.dialog-title {
		/* font-weight: bold; */
		line-height: 44px;
	}

	.dialog-close {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		align-items: center;
		padding: 0 15px;
	}

	.dialog-close-plus {
		width: 16px;
		height: 2px;
		background-color: #666;
		border-radius: 2px;
		transform: rotate(45deg);
	}

	.dialog-close-rotate {
		position: absolute;
		transform: rotate(-45deg);
	}

	.picker-view {
		flex: 1;
		overflow: hidden;
	}

	/* #ifdef H5 */
	@media all and (min-width: 768px) {
		.uni-data-tree-cover {
			background-color: transparent;
		}

		.uni-data-tree-dialog {
			position: absolute;
			top: 55px;
			height: auto;
			min-height: 400px;
			max-height: 50vh;
			background-color: #fff;
			border: 1px solid #EBEEF5;
			box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
			border-radius: 4px;
			overflow: unset;
		}

		.dialog-caption {
			display: none;
		}

		.icon-clear {
			margin-right: 5px;
		}
	}

	/* #endif */

	/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
	/* #ifndef APP-NVUE */
	.uni-popper__arrow,
	.uni-popper__arrow::after {
		position: absolute;
		display: block;
		width: 0;
		height: 0;
		border-color: transparent;
		border-style: solid;
		border-width: 6px;
	}

	.uni-popper__arrow {
		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
		top: -6px;
		left: 10%;
		margin-right: 3px;
		border-top-width: 0;
		border-bottom-color: #EBEEF5;
	}

	.uni-popper__arrow::after {
		content: " ";
		top: 1px;
		margin-left: -6px;
		border-top-width: 0;
		border-bottom-color: #fff;
	}

	/* #endif */
</style>

使用

<grid-cascade-selection class="right" placeholder="请选择行政区划" popup-title="请选择行政区划"
							v-model="areaId" @change="onchange" @popupopened="onpopupopened"
							@popupclosed="onpopupclosed" style="width: 470rpx;"></grid-cascade-selection>
参数
color选中颜色
optionsuniapp插槽选项
popupTitle弹窗标题
placeholder
onpopupclosed弹窗关闭
popupopened弹窗打开
change节点改变
readonly只读
clearIcon清除按钮
border边框

地区请求写在组件内了,可以按需调整

getDeptList(id) {
	return departMent({id:id}).then((res)=>{
		return res.data.list.map((item)=>{
			return {
				text: item.deptName,
				value: item.ID
			}
		})
	}).catch((err)=>{
	})
},
change(e) {
	/**
		 *   layer: 0  第几级 index
			 src: '/static/images/basic/color.png'
			 subIndex: 2   //当前层级下选中项index
			 subText: '30人'  //选中项数据
			 text: '高一(3)班'
			 value: 103 //选中项value数据
		 * */
	
		// 模拟请求
		let value = e.value;
		let layer = e.layer;
		// 数据层级
		if (layer === 3) {
			//实际中以请求数据为准,无下级数据则传空数组
			this.receiveData = [];
		} else {
			uni.showLoading({
				title: '请稍候...'
			});
			this.getDeptList(value).then((res)=>{
				this.receiveData =res.length?res:[]
				uni.hideLoading();
			}).catch((err)=>{
				
			})
		}
	}

数据需要处理为

	[{
    src: "", //图标地址
    text: "",//主文本
    subText: "",//副文本
    value: 0, //value值 
    children:[{
  	  text: "",//主文本
  	  subText: "",//副文本
  	  value: 0,//value值
  	  children:[] //子级数据 如果数据长度为0则表示没有下一级数据了
     }] //子级数据
}]

互相学习,有问题及时沟通

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐