vue实现自定义树形穿梭框功能

时间:2024-03-01 10:00:33
<template> <div class="treeTransfer"> <!-- 左边 --> <div class="leftTree"> <div class="list"> <div class="left_lowline"> <div class="leftcheck_con"> <el-checkbox v-model="checkedLeft" :disabled="leftTreeData.length < 1" label="" size="large" style="margin-right: 12px" @change="leftAllCheck" /> <p class="left_title">{{ props.title[0] }}</p> </div> <div> {{ leftOperation.length }}/{{ leftAllselectIdarry.length }} </div> </div> <el-tree ref="leftTreeRef" :data="leftTreeData" show-checkbox node-key="id" :props="props.defaultProps" v-slot="{ node, data }" accordion :check-strictly="true" @check="onCheckLeft" default-expand-all> <div> {{ data.label }} </div> </el-tree> </div> </div> <!-- 中间按钮 --> <div class="btnDiv"> <div class="mg10"> <el-button @click="toRight()" icon="ele-Right" type="primary" circle :disabled="leftOperation.length < 1"/> </div> <div class="mg10"> <el-button @click="toLeft()" icon="ele-Back" type="primary" circle :disabled="rightOperation.length < 1"/> </div> </div> <!-- 右边 --> <div class="rightTree"> <div class="list"> <div class="left_lowline"> <div class="leftcheck_con"> <el-checkbox v-model="checkedRight" :disabled="rightTreeData.length < 1" label="" size="large" style="margin-right: 12px" @change="rightAllCheck" /> <p class="left_title">{{ props.title[1] }}</p> </div> <div> {{ rightOperation.length }}/{{ rightAllselectIdarry.length }} </div> </div> <el-tree ref="rightTreeRef" :data="rightTreeData" show-checkbox node-key="id" :props="props.defaultProps" v-slot="{ node, data }" accordion :check-strictly="true" @check="onCheckRight" default-expand-all> <div> {{ data.label }} </div> </el-tree> </div> </div> </div> </template> <script setup lang="ts"> import { ref, onMounted, watch, nextTick } from 'vue'; import lodash from 'lodash-es' const props = defineProps(['fromData', 'toData', 'defaultProps', 'title', 'visible']); const checkedLeft = ref(false) const checkedRight = ref(false) const leftOperation = ref<any[]>([]) const rightOperation = ref<any[]>([]) // 定义emit const emits = defineEmits(['addStaffchange']); const leftTreeRef = ref(); const rightTreeRef = ref(); // 左侧数据 const leftTreeData = ref([] as any); // 右侧数据 const rightTreeData = ref([] as any); // 左侧可以选中id集合 const leftAllselectIdarry = ref([] as any) // 右侧可以选中id集合 const rightAllselectIdarry = ref([] as any) watch( props, newVal => { leftTreeData.value = lodash.cloneDeep(newVal.fromData) rightTreeData.value = lodash.cloneDeep(newVal.toData) // 获取左侧的全选中的id leftAllselectIdarry.value = getAllIds(leftTreeData.value, []) if (newVal.visible) { checkedLeft.value = false checkedRight.value = false leftOperation.value = [] rightOperation.value = [] nextTick(() => { leftTreeRef?.value.setCheckedKeys([]) }) } }, { immediate: true } ) defineExpose({ leftTreeData, rightTreeData }) onMounted(() => { }) // 去右边 const toRight = async () => { const leftTree = leftTreeRef.value; if (!leftTree) { return } const leftNodes = leftTree.getCheckedNodes(false, false) const checkedKeys = leftTree.getCheckedKeys(false) const rightTree = rightTreeRef.value const newArr = rightTreeData.value.concat(leftNodes) let obj = {}; let peon = newArr.reduce((cur,next) => { obj[next['id']] ? "" : obj[next['id']] = true && cur.push(next); return cur; },[]) //设置cur默认类型为数组,并且初始值为空的数组 const getnewleftArry = peon.map(item => { return { id: item.id, label: item.label, pid: item.pid, children: [], } }) rightTreeData.value = getnewleftArry leftOperation.value = leftTreeRef.value?.getCheckedKeys(false) emits('addStaffchange', checkedKeys) setTimeout(() => { rightTree?.setCheckedNodes(leftNodes); rightOperation.value = rightTreeRef.value?.getCheckedKeys(false) rightAllcheckChange() }, 500) }; // 去左边 const toLeft = async () => { const rightTree = rightTreeRef.value if (!rightTree) { return } const checkedKeys = rightTree.getCheckedKeys(false) for(var i=0; i<rightTreeData.value.length;i++){ if(checkedKeys.includes(rightTreeData.value[i].id)){ rightTreeData.value.splice(i,1) i-=1 } } rightOperation.value = rightTree?.getCheckedKeys(false) if (rightTreeData.value && rightTreeData.value.length === 0) { checkedRight.value = false } emits('addStaffchange', getAllIds(rightTreeData.value, [])) }; //左侧选中 const onCheckLeft = () => { leftOperation.value = leftTreeRef.value?.getCheckedKeys(false) if (leftOperation.value.length === leftAllselectIdarry.value.length) { checkedLeft.value = true } else { checkedLeft.value = false } } // 右侧选中 const onCheckRight = () => { rightOperation.value = rightTreeRef.value?.getCheckedKeys(false) rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).length rightAllcheckChange() } // 右侧是否全选获取 function rightAllcheckChange () { rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).length if (rightOperation.value.length === rightAllselectIdarry.value.length) { checkedRight.value = true } else { checkedRight.value = false } return checkedRight.value } // 左侧全选中值 const leftAllCheck = () => { if (checkedLeft.value) { leftTreeRef.value.setCheckedKeys(getAllIds(leftTreeData.value, [])) checkedLeft.value = true; } else { leftTreeRef.value.setCheckedKeys([]) checkedLeft.value = false } leftOperation.value = leftTreeRef.value?.getCheckedKeys(false) } // 右侧全选中值 const rightAllCheck = () => { if (checkedRight.value) { rightTreeRef.value.setCheckedKeys(getAllIds(rightTreeData.value, [])) checkedRight.value = true } else { rightTreeRef.value.setCheckedKeys([]) checkedRight.value = false } rightOperation.value = rightTreeRef.value?.getCheckedKeys(false) } // 递归获取所有id数据 function getAllIds(tree, result) { //遍历树获取id数组 for (const i in tree) { if (!tree[i].disabled) { result.push(tree[i].id); // 遍历项目满足条件后的操作 } if (tree[i].children) { // 存在子节点就递归 getAllIds(tree[i].children, result); } } return result; } </script> <style scoped lang="scss"> .treeTransfer { display: flex; justify-content: center; .el-tree { overflow: auto; max-height: 360px; } .leftTree { border: 1px solid #ebeef5; width: 40%; height: calc(100% - 60px); overflow: auto; } .left_lowline { display: flex; align-items: center; justify-content: space-between; background: #f5f7fa; padding: 0 23px 0 10px; .leftcheck_con { display: flex; align-items: center; } } .btnDiv { width: 20%; height: calc(100% - 60px); text-align: center; margin: auto 0; line-height: 40px; position: relative; } .rightTree { width: 40%; height: calc(100% - 60px); } } </style>