Element plus Tree选择组件

一 选择组件

效果:

1.单选

2.多选

<template>
	<el-select
		:disabled="disabled"
		ref="chooseProjectName"
		v-model="selectDept"
		multiple
		collapse-tags
		:placeholder="props.placeholder"
		@focus="handleTestTasks"
		@visible-change="visibleType"
		suffix-icon="MoreFilled"
	>
		<el-option v-for="item in deptList" :key="item.id" :label="item?.name" :value="item?.id" />
	</el-select>
	<el-dialog v-model="selectDeptVisible" append-to-body title="选择" width="1200" draggable>
		<div class="selectPersonnelContent">
			<div class="PersonneTree">
				<div class="header">
					<el-input v-model="searchName" placeholder="请输入施工段" :suffix-icon="Search" clearable maxlength="30" show-word-limit />
				</div>
				<div class="content">
					<el-tree
						ref="treeRef"
						class="tree-line"
						:indent="0"
						:data="treeArray"
						show-checkbox
						check-strictly
						:expand-on-click-node="false"
						:default-expanded-keys="defaultExpandedKeys"
						node-key="id"
						@check="handleCheck"
					>
						<template #default="{ data }">
							<span class="custom-tree-node">
								<span class="tree-icon">
									<SvgIcon name="local-work-area" :size="16" />
								</span>
								<span>{{ data.name }}</span>
							</span>
						</template>
					</el-tree>
				</div>
			</div>
			<div class="PersonneInfo">
				<div class="header">
					<span>已选施工段({{ selectionDept.length }})</span>
					<span @click="clearPersonneInfo">清空</span>
				</div>
				<ul>
					<li v-for="(item, index) in selectionDept" :key="index">
						{{ item?.name }}<el-icon class="icon" @click="delPersonneInfo(item)"><Delete /></el-icon>
					</li>
				</ul>
			</div>
		</div>
		<template #footer>
			<div class="dialog-footer">
				<el-button @click="selectDeptVisible = false">取消</el-button>
				<el-button type="primary" @click="submit"> 确认 </el-button>
			</div>
		</template>
	</el-dialog>
</template>

<script setup lang="ts" name="selectPersonnel">
const selectDept = ref([]) as any;
const chooseProjectName = ref();
const searchName = ref('');
const treeRef = ref();
const deptList = ref([]) as any;
const treeArray = ref([]) as any;
const selectionDept = ref([]) as any;
const selectDeptVisible = ref(false);
const defaultExpandedKeys = ref([]) as any;
import { getProjectTreeListWithoutAuth, getProjectTreeListList } from '../../api/components/selectProject';
import { Search } from '@element-plus/icons-vue';
const emits = defineEmits(['update:modelValue']);

const props = defineProps({
	multiple: {
		type: Boolean,
		default: false,
	},
	modelValue: {
		type: Array,
		default: () => [],
	},
	placeholder: {
		type: String,
		default: '请选择所属施工段',
	},
	disabled: {
		type: Boolean,
		default: false,
	},
	all: {
		required: false,
		type: Boolean,
		default: false,
	},
	type: {
		required: false,
		type: String,
		default: '',
	},
});

// 阻止下拉框  下拉事件
const handleTestTasks = () => {
	chooseProjectName.value.blur();
};
// 删除
const delPersonneInfo = (row: { id: '' }) => {
	treeRef.value.setCheckedKeys(
		selectionDept.value.filter((item: any) => item.id != row.id).map((item: any) => item.id),
		true
	);
	selectionDept.value.forEach((item: { id: '' }, i: number) => {
		if (row?.id == item.id) {
			selectionDept.value.splice(i, 1);
			return;
		}
	});
};
// 清空
const clearPersonneInfo = () => {
	nextTick(() => {
		treeRef.value.setCheckedKeys([], true);
	});
	selectionDept.value = [];
};
// select 回显
watch(
	() => props.modelValue,
	() => {
		// 校验v-model 数据类型
		if (!Array.isArray(props.modelValue)) return;
		deptList.value = props.modelValue;
		selectDept.value = props.modelValue?.map((item: any) => item.id);
	},
	{ deep: true, immediate: true }
);
// 选中状态回显
watch(
	() => selectDeptVisible.value,
	() => {
		if (!selectDeptVisible.value) return;
		nextTick(() => {
			selectionDept.value = props.modelValue;
			treeRef.value!.setCheckedKeys(selectDept.value, true);
			defaultExpandedKeys.value = treeArray.value?.map((item: any) => item.id);
		});
	}
);
const visibleType = () => {
	// 弹框
	nextTick(() => {
		// 不显示下拉框
		chooseProjectName.value.blur();
		selectDeptVisible.value = true;
	});
};
onBeforeMount(async () => {
	getProjectTree();
});
// 获取数据
const getProjectTree = async () => {
	if (props.all) {
		let res = await getProjectTreeListWithoutAuth({ name: searchName.value, isTree: true, type: props.type });
		treeArray.value = res.data;
	} else {
		let res = await getProjectTreeListList({ name: searchName.value, isTree: true, type: props.type });
		treeArray.value = res.data;
	}
};
// 输入查询
let time: any;
watch(
	() => searchName.value,
	() => {
		if (time) {
			clearTimeout(time);
			time = setTimeout(() => {
				getProjectTree();
			}, 500);
		} else {
			time = setTimeout(() => {
				getProjectTree();
			}, 500);
		}
	}
);
// 多选
const handleCheck = (row: any) => {
	if (!props.multiple) {
		//实现单选关键就是这一点:在选中某个项之后,将选中的节点数组只设置成当前选中的节点,保证只有一个选项
		treeRef.value.setCheckedKeys([row.id]);
		deptList.value = treeRef.value!.getCheckedNodes();
		let arr = JSON.parse(JSON.stringify(deptList.value));
		selectionDept.value = arr;
	} else {
		deptList.value = treeRef.value!.getCheckedNodes();
		let arr = JSON.parse(JSON.stringify(deptList.value));
		selectionDept.value = arr;
	}
	nextTick(() => {
		const nodes = treeRef.value.store._getAllNodes();
		for (let i = 0; i < nodes.length; i++) {
			if (row.id == nodes[i].data.id) {
				nodes[i].expanded = true;
				break;
			}
		}
	});
};
// 确认
const submit = () => {
	deptList.value = treeRef.value!.getCheckedNodes();
	selectDept.value = treeRef.value!.getCheckedKeys();
	let arr = JSON.parse(JSON.stringify(deptList.value));
	emits(
		'update:modelValue',
		arr.map((el: any) => {
			delete el.children;
			return el;
		})
	);

	selectDeptVisible.value = false;
};
</script>

<style lang="scss" scoped>
:deep(.el-input__wrapper) {
	box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tooltip__trigger) {
	box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tag__close) {
	display: none;
}
.tree-icon {
	width: 20px;
	height: 20px;
	display: inline-block;
	margin-right: 8px;
}
.selectPersonnelContent {
	width: 100%;
	height: 700px;
	display: flex;
	.PersonneTree {
		flex: 1;
		height: 100%;
		border: 1px solid #eee;
		border-radius: 10px;
		padding: 6px;
		overflow: hidden;
		overflow-y: auto;
		margin-right: 15px;
		.header {
			margin-bottom: 10px;
		}
		.content {
			height: calc(100% - 45px);
			overflow: hidden;
			overflow-y: auto;
		}
	}

	.PersonneInfo {
		padding: 10px;
		width: 350px;
		border: 1px solid #eee;
		padding: 6px;
		border-radius: 10px;
		.header {
			padding: 0 6px;
			height: 30px;
			border-bottom: 1px solid #eee;
			display: flex;
			align-items: center;
			justify-content: space-between;
			span:nth-child(2) {
				color: rgb(183, 108, 108);
				cursor: pointer;
			}
		}
		ul {
			height: calc(100% - 30px);
			overflow: hidden;
			overflow-y: auto;
			li {
				height: 25px;
				display: flex;
				align-items: center;
				justify-content: space-between;
				border: 1px solid #eee;
				border-radius: 6px;
				padding: 0 6px;
				margin: 5px 0;
				.icon {
					color: red;
					cursor: pointer;
				}
			}
			li:hover {
				background-color: #eee;
			}
		}
	}
}
</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值