【uniapp 、小程序】渐进式树形结构组件(支持回显)

效果图(右箭头用的uviewplus的u-icon ,可自行修改)
此组件只包含画框部分
在这里插入图片描述

传入的数组格式如下(一个label、一个value、一个children组成的树形数组),回显只需将此数组以及目标id传入即可,
微信小程序的话隐藏再显示一次组件就通过refs调用一次组件的getScollWidthForMpWeixin方法即可
在这里插入图片描述

源代码如下:

<template>
	<view class="next-scroll-box">
		<scroll-view class="next-scroll-title" scroll-x="true" :scroll-left="scrollViewWidth" scroll-with-animation>
			<view class="next-scroll-title-item-box" v-for="(i, e) in tabList" @click="checkTab(e)" :key="e">
				<view
					v-if="tabId >= e"
					:id="'next-' + e"
					:class="['next-scroll-title-item', tabId == e ? ' next-scroll-title-item-true' : '']">
					{{ checkList[e] ? checkList[e][labelKey] : i.title }}
					<view style="margin-left: 20rpx" v-if="tabId > e">
						<u-icon size="14" bold color="#6D809D" name="arrow-right"></u-icon>
					</view>
				</view>
			</view>
		</scroll-view>
		<scroll-view
			class="next-scroll-view_H"
			scroll-y="true"
			:scroll-into-view="scrollIntoView"
			scroll-with-animation>
			<view
				class="next-scroll-view-grid-box"
				v-if="checkBox && checkBox.length && checkBox[tabId] && checkBox[tabId].length">
				<view
					:id="`next-scroll-view-item-` + index"
					v-for="(item, index) in checkBox[tabId]"
					:key="index"
					@click="check(index)"
					:class="
						checkList && checkList[tabId] && checkList[tabId][labelKey] == item[labelKey]
							? 'next-scroll-view-item-true'
							: 'next-scroll-view-item'
					">
					{{ item[labelKey] || '' }}
				</view>
			</view>
			<view class="next-scroll-view-noBox" v-else>
				<view class="text">暂无数据</view>
			</view>
		</scroll-view>
	</view>
</template>

<script>
	export default {
		props: {
			value: {
				type: String,
				default: '',
			},
			options: {
				type: Array,
				default: () => {
					return []
				},
			},
			valueKey: {
				type: String,
				default: 'value',
			},
			labelKey: {
				type: String,
				default: 'label',
			},
			childrenKey: {
				type: String,
				default: 'children',
			},
			defaultValue: {
				// 默认值
				type: [Number, String],
				default: '',
			},
		},
		data() {
			return {
				checkBox: [],
				tabId: 0,
				checkList: [],
				checkListModel: [],
				id: 0,
				tabList: [
					{
						title: '请选择',
						id: 0,
					},
				],
				scrollViewWidth: 0,
				elWidth: 0,
				yIndex: 0,
				scrollIntoView: '',
			}
		},
		created() {},
		options: {
			styleIsolation: 'shared', // 解除样式隔离
		},
		mounted() {
			this.init()
			console.log('----------插件挂载完毕------------')
		},
		watch: {
			defaultValue: {
				immediate: true,
				handler(newVal) {
					if ((newVal || newVal == '0') && this.options.length) {
						this.handleDefaultSelected(newVal, this.options)
					}
				},
			},
		},
		computed: {
			_value() {
				return (this.checkListModel || []).map((item) => item[this.valueKey]).join(',')
			},
		},
		methods: {
			init() {
				if (!this.defaultValue && this.defaultValue != 0) {
					this.id = 0
					this.tabId = 0
					this.checkBox = []
					this.checkList = []
				}
				//初始化求出滚动的宽度
				let view = uni.createSelectorQuery().in(this).select('.next-scroll-title')
				view.boundingClientRect((rect) => {
					this.scrollViewWidth = Math.round(rect.width)
				}).exec()
				this.getData()
			},
			// 微信特调方法,初始设置scrollViewWidth
			getScollWidthForMpWeixin() {
				let view = uni.createSelectorQuery().in(this).select('.next-scroll-title')
				view.boundingClientRect((rect) => {
					this.scrollViewWidth = Math.round(rect.width)
				}).exec()

				let view2 = uni
					.createSelectorQuery()
					.in(this)
					.select('#next-' + this.tabId)
				view2
					.boundingClientRect((rect) => {
						this.elWidth = Math.round(rect.width)
					})
					.exec()

				if (this.scrollIntoView) {
					this.scrollIntoView = 'next-scroll-view-item-0'
				}

				setTimeout(() => {
					this.scrollViewWidth = this.scrollViewWidth + this.elWidth
				})

				setTimeout(() => {
					this.scrollIntoView = `next-scroll-view-item-` + this.yIndex
				}, 100)
			},
			async check(index) {
				this.$set(this.checkList, this.id, this.checkBox[this.id][index])
				const children = this.checkBox[this.id][index][this.childrenKey]
				let n = 0
				if (children && children.length) {
					n = this.id + 1
				} else {
					n = this.id
				}

				let arr = []
				for (let i = 0; i <= n; i++) {
					arr.push({
						title: '请选择',
						id: i,
					})
				}
				this.tabList = arr
				if (this.id < this.tabList.length - 1) this.id = this.id + 1
				await this.getData()
				let view = uni
					.createSelectorQuery()
					.in(this)
					.select('#next-' + this.tabId)
				view.boundingClientRect((rect) => {
					this.elWidth = Math.round(rect.width)
				}).exec()
				setTimeout(() => {
					this.scrollViewWidth = this.scrollViewWidth + this.elWidth
				})
				if (this.tabId < this.tabList.length - 1) this.tabId = this.tabId + 1
			},
			checkTab(e) {
				if (e == this.id) return
				this.id = e
				this.tabId = e
				this.checkList = this.checkList.splice(0, e)
				this.scrollIntoView = 'next-scroll-view-item-0'
				this.$emit('confirm', {
					flag: false,
				})
			},
			getResult(event) {
				if (event == 'confirm') {
					if (this.checkList.length != this.tabList.length) return
					let result = this.checkList
					this.checkListModel = result
					// #ifdef VUE2
					this.$emit('input', this._value)
					// #endif
					// #ifdef VUE3
					this.$emit('update:value', this._value)
					// #endif
					// flag为true 即已经选到了最终节点
					this.$emit('confirm', {
						value: result,
						flag: true,
					})
				}
			},
			//使用本地假数据进行加载
			async getData() {
				if (this.checkList.length === this.tabList.length) {
					this.getResult('confirm')
					// console.log('tabid------>', this.tabId)
					// console.log('id----->', this.id)
					// console.log('checkbox----->', this.checkBox)
					// console.log('checkList----->', this.checkList)
					// console.log('tabList----->', this.tabList)
					return
				}
				// 此处的flag为false是告诉父级并没有选到最终的节点
				this.$emit('confirm', {
					flag: false,
				})
				let list = []
				if (this.checkList.length) {
					var id = this.checkList[this.id - 1][this.valueKey]
					const item = this.checkBox[this.id - 1].find((item) => {
						return item[this.valueKey] == id
					})
					;(item[this.childrenKey] ? item[this.childrenKey] : []).map((e) => {
						list.push(e)
					})
					this.$set(this.checkBox, this.id, list)
				} else {
					this.options.map((e) => {
						list.push(e)
					})
					this.$set(this.checkBox, this.id, list)
				}
			},

			// 回显函数   传入value(要保证唯一)  以及总的数组
			handleDefaultSelected(code, handlerArr) {
				if (!code || !handlerArr || !Array.isArray(handlerArr) || !handlerArr.length) return
				let that = this
				function findNodeById(code, arr, path = []) {
					for (let i = 0; i < arr.length; i++) {
						if (arr[i][that.valueKey] == code) {
							return [...path, arr[i]]
						}
						if (arr[i][that.childrenKey]) {
							let result = findNodeById(code, arr[i][that.childrenKey], [...path, arr[i]])
							if (result) {
								return result
							}
						}
					}
					return null
				}
				const checkList = findNodeById(code, handlerArr)
				if (checkList === null) return
				let checkBox = [handlerArr]
				const tabList = checkList.map((item, index) => {
					if (item[that.childrenKey] && item[that.childrenKey].length > 0 && checkList.length > index + 1)
						checkBox.push(item[that.childrenKey])
					return { title: '请选择', id: index }
				})
				const id = tabList.length - 1
				const tabId = tabList.length - 1

				const yIndex = checkBox[checkBox.length - 1]?.findIndex(
					(it) => it[that.valueKey] == checkList[checkList.length - 1][that.valueKey]
				)

				that.checkList = checkList
				that.checkBox = checkBox
				that.id = id
				that.tabId = tabId
				that.tabList = tabList

				that.yIndex = yIndex
				setTimeout(() => {
					that.scrollIntoView = `next-scroll-view-item-` + yIndex
				}, 100)

				// console.log('checkList--->', checkList)
				// console.log('checkBox--->', checkBox)
				// console.log('id--->', id)
				// console.log('tabId--->', tabId)
				// console.log('tabList--->', tabList)
			},
		},
	}
</script>

<style>
	/deep/ ::-webkit-scrollbar {
		width: 0;
		height: 0;
		color: transparent;
		display: none;
	}
</style>

<style lang="scss" scoped>
	.next-scroll-box {
		width: 100%;
		height: 100%;
		background: #ffff;
		border-radius: 24rpx 24rpx 0 0;
	}

	.next-scroll-title {
		white-space: nowrap;
		width: 100%;
		height: 88rpx;
		line-height: 88rpx;
		background-color: #fafafa;
		padding: 0 20rpx;
	}

	.next-scroll-view_H {
		white-space: nowrap;
		width: 100%;
		height: 400rpx;
		line-height: 100rpx;
		background-color: #ffffff;
	}

	.next-scroll-title-item {
		position: relative;
		display: flex;
		align-items: center;
	}

	.next-scroll-title-item-box {
		display: inline-block;
		margin: 0 10rpx;
		font-size: 28rpx;
		color: #333333;
	}

	.next-scroll-title-item-true {
		font-size: 28rpx;
		font-weight: 700;
		color: #45afff;
	}

	.next-scroll-view-grid-box {
		width: calc(100% - 20rpx);
		margin: 10rpx;
		padding-bottom: 10rpx;
	}

	.next-scroll-view-noBox {
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;

		.text {
			width: 100%;
			color: #333333;
			text-align: center;
			font-size: 28rpx;
		}
	}

	.next-scroll-view-item {
		padding: 0rpx 24rpx;
		text-align: left;
		border-radius: 6rpx;
		background: #fff;
		color: #333333;
		font-size: 28rpx;
		margin: 12rpx 4rpx;
		height: 66rpx;
		line-height: 66rpx;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.next-scroll-view-item-true {
		padding: 0rpx 24rpx;
		text-align: left;
		border-radius: 6rpx;
		color: #45afff;
		font-size: 28rpx;
		margin: 12rpx 4rpx;
		height: 66rpx;
		line-height: 66rpx;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
</style>


  • 32
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1.在页面中引入layui的css和js文件: ``` <link rel="stylesheet" href="/layui/css/layui.css"> <script src="/layui/layui.js"></script> ``` 2.创建一个div作为树形组件的容器: ``` <div id="tree"></div> ``` 3.定义树形组件的数据: ``` var treeData = [{ "id": 1, "name": "节点1", "children": [{ "id": 11, "name": "节点1-1" }, { "id": 12, "name": "节点1-2" }] }, { "id": 2, "name": "节点2", "children": [{ "id": 21, "name": "节点2-1" }, { "id": 22, "name": "节点2-2" }] }]; ``` 4.使用layui.tree组件创建树形结构: ``` layui.use(['tree'], function(){ var tree = layui.tree; tree.render({ elem: '#tree', data: treeData, id: 'treeId', showCheckbox: true, accordion: true, checkArr: [1, 11] //默认勾选节点1和节点1-1 }); }); ``` 其中,id为树形组件的唯一标识,showCheckbox表示是否显示复选框,accordion表示是否开启手风琴模式(只展开一个节点),checkArr表示默认勾选的节点的id数组。 5.获取勾选的节点: ``` var checkedData = layui.tree.getChecked('treeId'); ``` 其中,treeId为树形组件的id。 6.将勾选的节点id数组转换成树形组件需要的格式: ``` var checkedIds = []; for (var i = 0; i < checkedData.length; i++) { checkedIds.push(checkedData[i].id); } var checkedArr = layui.tree.ConvertToArr('treeId', checkedIds); ``` 7.将勾选的节点回显到树形组件中: ``` layui.use(['tree'], function(){ var tree = layui.tree; tree.render({ elem: '#tree', data: treeData, id: 'treeId', showCheckbox: true, accordion: true, checkArr: checkedArr //勾选之前已经勾选的节点 }); }); ``` 完整代码如下: ``` <link rel="stylesheet" href="/layui/css/layui.css"> <script src="/layui/layui.js"></script> <div id="tree"></div> <script> var treeData = [{ "id": 1, "name": "节点1", "children": [{ "id": 11, "name": "节点1-1" }, { "id": 12, "name": "节点1-2" }] }, { "id": 2, "name": "节点2", "children": [{ "id": 21, "name": "节点2-1" }, { "id": 22, "name": "节点2-2" }] }]; layui.use(['tree'], function(){ var tree = layui.tree; tree.render({ elem: '#tree', data: treeData, id: 'treeId', showCheckbox: true, accordion: true, checkArr: [1, 11] //默认勾选节点1和节点1-1 }); var checkedData = layui.tree.getChecked('treeId'); var checkedIds = []; for (var i = 0; i < checkedData.length; i++) { checkedIds.push(checkedData[i].id); } var checkedArr = layui.tree.ConvertToArr('treeId', checkedIds); tree.render({ elem: '#tree', data: treeData, id: 'treeId', showCheckbox: true, accordion: true, checkArr: checkedArr //勾选之前已经勾选的节点 }); }); </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值