uniapp uview下拉树选择器

该文章展示了如何在jsuview框架中创建一个TreePick组件,该组件是一个底部弹出的下拉树选择器。组件内部使用了递归树结构(tree.vue)来展示数据,并提供了展开/折叠节点、选择节点等功能。组件监听数据变化,初始化树结构,并在用户确定选择后更新状态。
摘要由CSDN通过智能技术生成

js uview实现下拉树选择器功能
效果:
在这里插入图片描述
TreePick是最后在页面内使用的弹窗组件,tree.vue是递归树

TreePick.vue

<template>
	<view>
		<u-popup :show="open" mode="bottom" round="20" @close="cancel">
			<view class="tree-btn">
				<u-button class="btn" text="取 消" size="mini" type="text" @click="cancel">
				</u-button>
				<span></span>
				<u-button class="btn" text="确 定" size="mini" type="text" @click="submit"></u-button>
			</view>
			<scroll-view style="height: 400px; ">
				<tree :data="data" @handler="handler" v-bind="$attrs" v-on="$listeners" :treeNode.sync="treeNode"
					checkColor="#20A7B2">
				</tree>
			</scroll-view>
		</u-popup>
	</view>
</template>

<script>
	import tree from './tree.vue';
	export default {
		name: 'TreePicker',
		props: {
			data: {
				type: Array,
				default: () => {
					return [];
				}
			},
			showModel: {
				type: Boolean,
				default: false
			},
		},
		data() {
			return {
				treeNode: {},
				originNode: {}
			};
		},

		components: { tree },

		computed: {
			open: {
				get() {
					return this.showModel;
				},
				set(val) {
					this.$emit('update:showModel', val);
				}
			}
		},

		watch: {
			open: {
				handler(val) {
					if (val) {
						this.originNode = JSON.parse(JSON.stringify(this.$store.state.treeNode))
						this.initData(this.data, 0);
						this.getLocation(this.data)
					}
				},
				immediate: true,
				deep: true
			},
		},
		methods: {
			close() {
				this.open = false;
			},
			//初始化树,设置节点层级level
			initData(data, plevel) {
				data.forEach(item => {
					if (item.pid == 0) item.level = 0;
					else item.level = plevel + 1;
					if (item.children && item.children.length) this.initData(item.children, item.level);
				});
				this.treeNode = JSON.stringify(this.treeNode) == "{}" ? data[0] : this.treeNode
			},
			
			handler(data) {
				let { treeNode } = data
				this.treeNode = treeNode;
			},

			//显示当前选中节点,其余折叠
			getLocation(data) {
				let target = this.$store.state.treeNode.ancestors ? this.$store.state.treeNode.ancestors.split(",") : []
				data.forEach(e => {
					if (target.includes(String(e.id))) e.isExpand = true
					else e.isExpand = false
					if (e.children && e.children.length) this.getLocation(e.children)
				})
			},
			submit() {
				this.originNode = this.treeNode
				this.$emit('getTreeNode', this.treeNode);
				this.close()
			},
			cancel() {
				this.$emit('getTreeNode', this.originNode);
				this.close()
			}
		}
	};
</script>
<style lang="scss" scoped>
	.tree-btn {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 5px 0;

		.btn {
			flex: 1;
		}

		span {
			flex-grow: 3;
			font-size: 9px;
			text-align: center;
			color: #000;
		}
	}


	::v-deep .u-button--mini {
		width: 15px;
	}

	::v-deep .u-button__text {
		color: #7BACB0;
		font-size: 9.16px !important;
		font-weight: 500;
	}
</style>

tree.vue

<template>
	<view class=" tree" id="tree">
		<view v-for="(item, index) in data" :key="index" style="padding: 2px 8px;">
			<span class="tree-node" :style="{ 'padding-left': item.level * indent + 'px' }">
				<u-icon name="arrow-right" size="14px" v-if="item.children && !item.isExpand"
					@click="changeExpand(item, index)"></u-icon>
				<u-icon name="arrow-down" size="14px" v-else-if="item.children && item.isExpand"
					@click="changeExpand(item, index)"></u-icon>
				<p class="node-span"
					:style="{ color: node?(item.id == node.id ? checkColor : '#666'):'','margin-left':'8px' }"
					@click="clickNode(item)" :class=" node?(item.id == node.id ? 'selected' : ''):'' ">
					{{ item.name}}
				</p>
			</span>
			<tree v-if="item.children && item.isExpand" :data="item.children" v-bind="$attrs" v-on="$listeners"
				:class="item.isExpand ? 'dh' : ''" :label="label" :checkColor="checkColor" :indent="indent"
				:treeNode="treeNode"></tree>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'tree',
		props: {
			data: {
				type: Array,
				default: () => []
			},
			label: {
				type: String,
				default: () => "name"
			},
			indent: {
				type: Number,
				default: 4
			},
			checkColor: {
				type: String,
				default: "#3c9cff"
			},
			treeNode: {
				type: Object
			}
		},
		data() {
			return {
				windowHeight: 0
			};
		},
		computed: {
			node() {
				return this.$store.state.treeNode;
			}

		},
		methods: {
			changeExpand(item, index) {
				this.$nextTick(() => {
					this.$set(item, 'isExpand', !item.isExpand);
					this.$forceUpdate()
				})

			},
			clickNode(item) {
				this.$store.state.treeNode = item
				this.$emit("handler", {
					treeNode: item
				})
			},
		}
	};
</script>

<style lang="scss" scoped>
	.tree-node {
		font-size: 9.16px;
		display: flex;
		font-weight: 500;
		overflow: hidden;

		.node-span {
			margin: 3px;
		}
	}

	.selected {}

	.tree {
		animation: move 0.5s;
		overflow: hidden;
		padding: 0 10px;
	}

	.dh {
		@keyframes move {
			0% {
				opacity: 0;
				margin-top: -20px;
			}

			100% {
				opacity: 1;
				margin-top: 0px;
			}
		}
	}
</style>

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值