1 changed files with 896 additions and 0 deletions
@ -0,0 +1,896 @@ |
|||||
|
<template> |
||||
|
<!-- {{ props.data?.control.fixedOptions }} --><!-- {{transferConfig}} --><!-- {{ resData }} --><!-- {{ count1 }} --> |
||||
|
<!-- <hr> |
||||
|
<hr> |
||||
|
<hr> --> |
||||
|
<div v-if="dataFinished" class="transfer"><!-- {{props.selectedValue}} --> |
||||
|
<div class="leftArea"><!-- {{ selectedValueCompu }}{{checkedIdList}} --> |
||||
|
<div class="transferName">{{ transferConfig.transferName }}</div> |
||||
|
<div style="padding-left: 15px; padding-right: 25px;margin-top: 10px;margin-bottom: 8px;"> |
||||
|
<ElInput v-model="keyword" placeholder="搜索"> |
||||
|
<template #prefix> |
||||
|
<ElIcon class="el-input__icon"> |
||||
|
<Search /> |
||||
|
</ElIcon> |
||||
|
</template> |
||||
|
</ElInput> |
||||
|
</div> |
||||
|
<div class="leftScroll"> |
||||
|
<ElScrollbar height="100%"> |
||||
|
<ElTree ref="treeRef" node-key="id" empty-text="暂无数据" :data="userList1" :props="treeProps" |
||||
|
show-checkbox highlight-current :default-expand-all="isExpandAll" |
||||
|
:filter-node-method="filterNode" @check="handleCheckList" @check-change="getCheckedList" /> |
||||
|
</ElScrollbar> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="rigthArea"> |
||||
|
<div style="margin-bottom: 8px;"> |
||||
|
<div class="transferName"><span>已选: {{ checkList.length }}</span> |
||||
|
<ElButton link style="float: right;margin-right: 5px;" @click="clearCheckList">清空</ElButton> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
|
</div> |
||||
|
<ElScrollbar height="calc(100% - 50px)"> |
||||
|
<ElTag v-for="user in checkList" :key="user.id" |
||||
|
style="display: block; font-size: 13px; letter-spacing: 1.5px;text-align: center;margin-top: 8px;height: 30px;width: 75%;margin-left:40px;padding-top: 6px;" |
||||
|
closable @close="handleCloseTag(user)"> |
||||
|
{{ |
||||
|
user.label |
||||
|
}}[{{ |
||||
|
user.parent.data.label |
||||
|
}}] |
||||
|
</ElTag> |
||||
|
|
||||
|
</ElScrollbar> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup name="MultiTreeSelector"> |
||||
|
import { ref, watch, onMounted } from 'vue' |
||||
|
|
||||
|
import request from '@/utils/request'; |
||||
|
import { forEach } from 'jszip'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
// eslint-disable-next-line vue/require-default-prop |
||||
|
data: { |
||||
|
type: Object, |
||||
|
default() { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
selectedValue: { |
||||
|
type: Array, |
||||
|
default() { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
const emits = defineEmits(['checkedIdListChanged', 'updateModel','reRenderComponent']); |
||||
|
/* const fixedOptions = ref([]) */ |
||||
|
/* fixedOptions.value = props.data?.control.fixedOptions */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
//const transferConfig = props.data?.config |
||||
|
const transferConfig = computed({ |
||||
|
set(){ |
||||
|
|
||||
|
}, |
||||
|
get(){ |
||||
|
|
||||
|
return props.data?.config |
||||
|
|
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const treeProps = { |
||||
|
value: 'id', |
||||
|
label: 'label', |
||||
|
disabled: 'disabled', |
||||
|
children: 'children' |
||||
|
} |
||||
|
let dataFinished = ref(false) |
||||
|
const treeRef = ref() |
||||
|
const isExpandAll = ref(transferConfig.value.transferDataSource != "数据源") // 是否全展开 |
||||
|
const keyword = ref('') // 搜索关键字 |
||||
|
const checkList = ref([]) // 选中的list |
||||
|
const userList = computed({ |
||||
|
|
||||
|
get(){ |
||||
|
if (transferConfig.value.transferDataSource === "数据源") { |
||||
|
|
||||
|
return [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: [...resData.value] |
||||
|
}] |
||||
|
|
||||
|
}else{ |
||||
|
|
||||
|
return [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: props.data?.control.fixedOptions |
||||
|
}] |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
set(){ |
||||
|
|
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
let userList1 = ref([]); |
||||
|
// |
||||
|
let checkedIdList = ref([]); |
||||
|
let count = 0 |
||||
|
function waitAndReGet() { |
||||
|
/* console.log("waitAndReGet") |
||||
|
console.log(props.selectedValue) |
||||
|
console.log(props.selectedValue.value) */ |
||||
|
setTimeout(() => { |
||||
|
let values = props.selectedValue.map(item => { |
||||
|
// 假设这里需要提取每个元素的特定值,可以根据实际情况进行调整 |
||||
|
return item; |
||||
|
}); |
||||
|
if (props.selectedValue && values) { |
||||
|
|
||||
|
/* console.log("有值") |
||||
|
console.log(props.selectedValue) |
||||
|
console.log(values) */ |
||||
|
//return props.selectedValue.value |
||||
|
checkedIdList.value = values |
||||
|
} else { |
||||
|
if (count < 3) { |
||||
|
count++ |
||||
|
waitAndReGet() |
||||
|
} |
||||
|
|
||||
|
/* console.log("waitAndReGet---else") */ |
||||
|
|
||||
|
} |
||||
|
}, 2000) |
||||
|
} |
||||
|
/* |
||||
|
|
||||
|
|
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
const selectedValueCompu = computed({ |
||||
|
get() { |
||||
|
//console.log("get") |
||||
|
//nextTick(()=>{ |
||||
|
let values = props.selectedValue.map(item => { |
||||
|
// 假设这里需要提取每个元素的特定值,可以根据实际情况进行调整 |
||||
|
return item; |
||||
|
}); |
||||
|
if (props.selectedValue && values) { |
||||
|
/* console.log(props.selectedValue.value) |
||||
|
console.log("computed---if") */ |
||||
|
return values |
||||
|
} else { |
||||
|
waitAndReGet() |
||||
|
/* console.log("computed---else") */ |
||||
|
return [] |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//}) |
||||
|
|
||||
|
}, |
||||
|
set() { } |
||||
|
}) |
||||
|
/* console.log(props.selectedValue) */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
const url = transferConfig.value.apiUrl;/* '/javasys/lowCode/transfer/getOrgAndManTree' */ |
||||
|
let resData = ref([]) |
||||
|
function endsWithGetOrgAndManTree(str) { |
||||
|
// 检查输入是否为字符串 |
||||
|
if (typeof str !== 'string') { |
||||
|
return false; |
||||
|
} |
||||
|
const suffix = 'getOrgAndManTree'; |
||||
|
// 当字符串长度小于后缀长度时,直接返回false |
||||
|
if (str.length < suffix.length) { |
||||
|
return false; |
||||
|
} |
||||
|
// 获取字符串末尾与后缀长度相同的子字符串并比较 |
||||
|
return str.slice(-suffix.length) === suffix; |
||||
|
} |
||||
|
function processTreeData(node) { |
||||
|
if(!endsWithGetOrgAndManTree(url)){ |
||||
|
return node |
||||
|
} |
||||
|
// 检查节点及其所有子孙节点中是否存在id长度大于4的节点 |
||||
|
function hasLongIdNode(node) { |
||||
|
// 检查当前节点id长度 |
||||
|
if (node.id && node.id.length > 4) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 处理子节点(考虑children为null的情况) |
||||
|
if (node.children && Array.isArray(node.children) && node.children.length > 0) { |
||||
|
for (const child of node.children) { |
||||
|
if (hasLongIdNode(child)) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 递归处理节点 |
||||
|
function processNode(node) { |
||||
|
// 如果当前节点及其所有子孙都没有长id,则移除该节点 |
||||
|
if (!hasLongIdNode(node)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
// 复制节点避免修改原对象 |
||||
|
const newNode = {...node}; |
||||
|
|
||||
|
// 处理子节点 |
||||
|
if (newNode.children && Array.isArray(newNode.children) && newNode.children.length > 0) { |
||||
|
// 递归处理每个子节点并过滤掉需要移除的 |
||||
|
newNode.children = newNode.children |
||||
|
.map(child => processNode(child)) |
||||
|
.filter(child => child !== null); |
||||
|
|
||||
|
// 如果所有子节点都被移除,保持children为null |
||||
|
if (newNode.children.length === 0) { |
||||
|
newNode.children = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return newNode; |
||||
|
} |
||||
|
|
||||
|
return processNode(node); |
||||
|
} |
||||
|
|
||||
|
function getDetail() { |
||||
|
//console.log(11111) |
||||
|
if (transferConfig.value.transferDataSource === "数据源") { |
||||
|
return request({ |
||||
|
url: url, |
||||
|
method: transferConfig.value.method, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
if (transferConfig.value.transferDataSource === "数据源") { |
||||
|
getDetail().then(({ data }) => { |
||||
|
checkedIdList.value = props.selectedValue |
||||
|
|
||||
|
const data1 = processTreeData(data) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
//console.log(`获取穿梭框接口数据`); |
||||
|
resData.value = data1.children |
||||
|
// 全选方法2:插入 全选node |
||||
|
userList.value = [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: [...resData.value] |
||||
|
}] |
||||
|
userList1.value = userList.value |
||||
|
if( treeRef.value){ |
||||
|
treeRef.value.setCheckedKeys(checkedIdList.value, true) |
||||
|
} |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
checkedIdList.value.forEach(element => { |
||||
|
const _node = treeRef?.value?.getNode(element) |
||||
|
if (!_node) return |
||||
|
const _checkList = checkList.value |
||||
|
if (_node.checked) { // 勾选时 |
||||
|
if (_node.checked && _node.childNodes.length === 0) { |
||||
|
_checkList.push(_node) |
||||
|
} |
||||
|
// 通过new Set() 实现数据去重 |
||||
|
checkList.value = Array.from(new Set(_checkList)) |
||||
|
// 遍历子级,递归,直到获取最后一级的人员 |
||||
|
if (_node.childNodes.length > 0) { |
||||
|
_node.childNodes.map(item => handleCheckList(item.data)) |
||||
|
} |
||||
|
} else if (!_node.checked) { // 取消勾选 |
||||
|
checkList.value = checkList.value.filter(item => item.checked) |
||||
|
} |
||||
|
}); |
||||
|
}, 50); |
||||
|
|
||||
|
|
||||
|
}); |
||||
|
} |
||||
|
const getCheckedList = () => { |
||||
|
checkedIdList.value = treeRef.value.getCheckedKeys(true) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
let resData1 = ref([ |
||||
|
{ |
||||
|
id: '3', |
||||
|
label: '营销部', |
||||
|
children: [ |
||||
|
{ |
||||
|
id: '3-1', |
||||
|
label: '威震天' |
||||
|
}, |
||||
|
{ |
||||
|
id: '3-2', |
||||
|
label: '嫦娥' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
id: '2', |
||||
|
label: '业务部', |
||||
|
children: [ |
||||
|
{ |
||||
|
id: '2-1-1', |
||||
|
label: '业务部子部1', |
||||
|
children: [ |
||||
|
{ |
||||
|
id: '2-1-0001', |
||||
|
label: '李白' |
||||
|
}, |
||||
|
{ |
||||
|
id: '2-1-0002', |
||||
|
label: '萧峰' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
id: '2-2-1', |
||||
|
label: '业务部子部2', |
||||
|
children: [ |
||||
|
{ |
||||
|
id: '2-2-00006', |
||||
|
label: '武则天' |
||||
|
}, |
||||
|
{ |
||||
|
id: '2-2-00005', |
||||
|
label: '拿破仑' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
id: '4', |
||||
|
label: '软件开发部', |
||||
|
children: [ |
||||
|
{ |
||||
|
id: '5-3', |
||||
|
label: '张辽' |
||||
|
}, |
||||
|
{ |
||||
|
id: '5-9', |
||||
|
label: '吕布' |
||||
|
}, |
||||
|
{ |
||||
|
id: '5-0', |
||||
|
label: '许褚' |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
// 勾选或者取消勾选 |
||||
|
const handleCheckList = (val) => { |
||||
|
//console.log(val) |
||||
|
const valId = val.id; |
||||
|
//console.log(valId) |
||||
|
const _node = treeRef?.value?.getNode(valId) |
||||
|
//console.log(_node) |
||||
|
|
||||
|
|
||||
|
if (!_node) return |
||||
|
const _checkList = checkList.value |
||||
|
//console.log(checkedIdList.value) |
||||
|
if (_node.checked||_node.childNodes.length > 0) { |
||||
|
if (_node.checked && _node.childNodes.length === 0) { |
||||
|
|
||||
|
let count = 0 |
||||
|
checkedIdList.value.forEach(element1 => { |
||||
|
if(element1==valId){ |
||||
|
|
||||
|
count++ |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if(count==1){ |
||||
|
_checkList.push(_node) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
//console.log(_checkList) |
||||
|
|
||||
|
/* checkList.value.forEach(element => { |
||||
|
console.log(element.data.id) |
||||
|
}); */ |
||||
|
|
||||
|
//使用 Map 对象实现真正的按 id 去重 |
||||
|
checkList.value = Array.from(new Map( |
||||
|
_checkList.map(item => [item.data.id, item]) |
||||
|
).values()); |
||||
|
|
||||
|
// 遍历子级,递归,直到获取最后一级的人员 |
||||
|
if (_node.childNodes.length > 0) { |
||||
|
_node.childNodes.map(item => handleCheckList(item.data)) |
||||
|
} |
||||
|
} else if (!_node.checked) { // 取消勾选 |
||||
|
checkList.value = checkList.value.filter(item => item.checked) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 人员树 父级过滤的递归 |
||||
|
* @node 当前节点 |
||||
|
* @result 存结果的数组 |
||||
|
* @key |
||||
|
* */ |
||||
|
const getParentNode = (node, result, key) => { |
||||
|
let isPass = node?.data?.label?.indexOf(key) !== -1 |
||||
|
isPass ? result.push(isPass) : '' |
||||
|
if (!isPass && node.level !== 1 && node.parent) { |
||||
|
return getParentNode(node.parent, result, key) |
||||
|
} |
||||
|
} |
||||
|
/*人员树过滤 |
||||
|
* @value 过滤的关键字 |
||||
|
* @data 被过滤的tree |
||||
|
* @node |
||||
|
* */ |
||||
|
const filterNode = (value, data, node) => { |
||||
|
if (!value) { |
||||
|
data.disabled = false |
||||
|
return true |
||||
|
} else { |
||||
|
//带过滤条件时,所有父级都禁止勾选 |
||||
|
// array.map(k => (!k.disabled) && (_childArr.push(k))) |
||||
|
(data.children) && (data.disabled = true) |
||||
|
} |
||||
|
let _arr = [] |
||||
|
getParentNode(node, _arr, value) |
||||
|
let result = false |
||||
|
_arr.forEach(item => { |
||||
|
result = result || item |
||||
|
}) |
||||
|
return result |
||||
|
} |
||||
|
// 通过tag 取消勾选 |
||||
|
const handleCloseTag = (tag) => { |
||||
|
checkedIdList.value = checkedIdList.value.filter(item => item !== tag.data.id); |
||||
|
|
||||
|
if (!treeRef?.value) return |
||||
|
checkList.value = checkList.value.filter(item => { |
||||
|
treeRef.value.setChecked(tag, false) |
||||
|
if (!item?.data) return |
||||
|
return item.data.id !== tag.data.id |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 清空CheckList |
||||
|
const clearCheckList = () => { |
||||
|
if (!treeRef?.value) return |
||||
|
treeRef.value.setCheckedKeys([]) |
||||
|
while (checkList.value.length > 0) { |
||||
|
checkList.value.pop() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 全选 /全不选 |
||||
|
const handleCheckAll = (array) => { |
||||
|
treeRef.value.setCheckedNodes(array) |
||||
|
array.map(item => { |
||||
|
handleCheckList(item) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 折叠/展开 |
||||
|
const expandAll = () => { |
||||
|
const nodesMap = treeRef.value.store.nodesMap |
||||
|
for (let key in nodesMap) { |
||||
|
nodesMap[key].expanded = isExpandAll.value |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
let watchCount = 0 |
||||
|
|
||||
|
|
||||
|
// 执行tree数据过滤 |
||||
|
watch(keyword, (newVal) => { |
||||
|
|
||||
|
|
||||
|
|
||||
|
//console.log(fuzzyTreeSearch(userList.value,newVal)) |
||||
|
|
||||
|
|
||||
|
userList1.value = fuzzyTreeSearch(userList.value,newVal) |
||||
|
//console.log(userList1.value) |
||||
|
//treeRef.value.data = fuzzyTreeSearch(treeRefValueData,newVal) |
||||
|
watchCount++ |
||||
|
isShowMore() |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
function isShowMore() { |
||||
|
isExpandAll.value = true |
||||
|
let nodes = treeRef.value.store._getAllNodes(); |
||||
|
nodes.forEach(item => { |
||||
|
item.expanded = true; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function fuzzyTreeSearch(treeData, keyword) { |
||||
|
if (!keyword?.trim()) return deepClone(treeData); // 空关键词返回完整树 |
||||
|
|
||||
|
|
||||
|
const processNode = (node) => { |
||||
|
const newNode = deepClone(node); // 深拷贝当前节点 |
||||
|
const isSelfMatch = newNode.label.toLowerCase().includes(keyword.toLowerCase()); |
||||
|
|
||||
|
// 处理子节点(关键修复点) |
||||
|
let hasValidChild = false; |
||||
|
if (newNode.children?.length) { |
||||
|
newNode.children = newNode.children |
||||
|
.map(child => processNode(child)) |
||||
|
.filter(Boolean); // 过滤无效子节点 |
||||
|
hasValidChild = newNode.children.length > 0; |
||||
|
} |
||||
|
|
||||
|
// 保留条件:自身匹配 或 子节点有匹配(修复子节点丢失问题) |
||||
|
if (isSelfMatch || hasValidChild) { |
||||
|
// 自身匹配时保留完整子树(即使子节点未匹配) |
||||
|
if (isSelfMatch && newNode.children?.length === 0) { |
||||
|
newNode.children = deepClone(node.children); // 直接复制原子节点 |
||||
|
} |
||||
|
return newNode; |
||||
|
} |
||||
|
return null; |
||||
|
}; |
||||
|
|
||||
|
return treeData.map(root => processNode(root)).filter(Boolean); |
||||
|
} |
||||
|
|
||||
|
function deepClone(obj) { |
||||
|
if (obj === null || typeof obj !== 'object') return obj; |
||||
|
const clone = Array.isArray(obj) ? [] : {}; |
||||
|
for (const key in obj) { |
||||
|
clone[key] = deepClone(obj[key]); |
||||
|
} |
||||
|
return clone; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
//获取配置接口相应或获取固定选项值 |
||||
|
if (transferConfig.value.transferDataSource === "固定选项") { |
||||
|
setTimeout(() => { |
||||
|
|
||||
|
// 全选方法2:插入 全选node |
||||
|
userList.value = [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: props.data?.control.fixedOptions |
||||
|
}] |
||||
|
userList1.value = userList.value |
||||
|
//console.log(treeRef) |
||||
|
/* console.log(checkedIdList.value) */ |
||||
|
if( treeRef.value){ |
||||
|
treeRef.value.setCheckedKeys(checkedIdList.value, true) |
||||
|
} |
||||
|
setTimeout(() => { |
||||
|
checkedIdList.value.forEach(element => { |
||||
|
const _node = treeRef?.value?.getNode(element) |
||||
|
if (!_node) return |
||||
|
const _checkList = checkList.value |
||||
|
if (_node.checked) { // 勾选时 |
||||
|
if (_node.checked && _node.childNodes.length === 0) { |
||||
|
_checkList.push(_node) |
||||
|
} |
||||
|
// 通过new Set() 实现数据去重 |
||||
|
checkList.value = Array.from(new Set(_checkList)) |
||||
|
// 遍历子级,递归,直到获取最后一级的人员 |
||||
|
if (_node.childNodes.length > 0) { |
||||
|
_node.childNodes.map(item => handleCheckList(item.data)) |
||||
|
} |
||||
|
} else if (!_node.checked) { // 取消勾选 |
||||
|
checkList.value = checkList.value.filter(item => item.checked) |
||||
|
} |
||||
|
}); |
||||
|
}, 50); |
||||
|
}, 50); |
||||
|
dataFinished.value = true |
||||
|
} else { |
||||
|
getDetail |
||||
|
dataFinished.value = true |
||||
|
} |
||||
|
/* setTimeout(()=>{ |
||||
|
console.log("setTimeout") |
||||
|
if(props.data?.config.transferDataSource==="数据源"){ |
||||
|
//emits('reRenderComponent'); |
||||
|
count1.value++ |
||||
|
} |
||||
|
},1500) */ |
||||
|
function arrayEquals(arr1, arr2) { |
||||
|
// 首先检查是否都是数组 |
||||
|
if (!Array.isArray(arr1) || !Array.isArray(arr2)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 检查数组长度是否相同 |
||||
|
if (arr1.length !== arr2.length) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 逐个比较元素 |
||||
|
for (let i = 0; i < arr1.length; i++) { |
||||
|
const item1 = arr1[i]; |
||||
|
const item2 = arr2[i]; |
||||
|
|
||||
|
// 如果两个元素都是数组,则递归比较 |
||||
|
if (Array.isArray(item1) && Array.isArray(item2)) { |
||||
|
if (!arrayEquals(item1, item2)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
// 如果两个元素都是对象,则比较它们的属性 |
||||
|
else if (typeof item1 === 'object' && item1 !== null && |
||||
|
typeof item2 === 'object' && item2 !== null) { |
||||
|
// 简单对象比较(不考虑原型链) |
||||
|
const keys1 = Object.keys(item1); |
||||
|
const keys2 = Object.keys(item2); |
||||
|
|
||||
|
if (keys1.length !== keys2.length) return false; |
||||
|
|
||||
|
for (const key of keys1) { |
||||
|
if (!keys2.includes(key) || !arrayEquals([item1[key]], [item2[key]])) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// 其他类型直接比较 |
||||
|
else { |
||||
|
if (item1 !== item2) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 监听 checkedIdList 的变化 |
||||
|
watch(checkedIdList, (newValue, oldValue) => { |
||||
|
/* console.log(oldValue) |
||||
|
console.log('checkedIdList 发生了变化', newValue); */ |
||||
|
|
||||
|
if(!arrayEquals(newValue,oldValue)){ |
||||
|
//console.log(1) |
||||
|
emits('checkedIdListChanged', newValue); |
||||
|
}else{ |
||||
|
//console.log(2) |
||||
|
} |
||||
|
|
||||
|
//emits('checkedIdListChanged', newValue); |
||||
|
}, { deep: true }); |
||||
|
|
||||
|
let count1 = ref(0); |
||||
|
|
||||
|
|
||||
|
/* onBeforeMount(() => { |
||||
|
console.log('onBeforeMount: 组件即将挂载'); |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
console.log('onMounted: 组件已挂载'); |
||||
|
}); |
||||
|
onBeforeUpdate(() => { |
||||
|
console.log('onBeforeUpdate: 数据即将更新到 DOM'); |
||||
|
|
||||
|
|
||||
|
}); |
||||
|
|
||||
|
onUpdated(() => { |
||||
|
console.log('onUpdated: DOM 已更新'); |
||||
|
if(props.data?.config.transferDataSource==="数据源"){ |
||||
|
console.log("emits('reRenderComponent');") |
||||
|
emits('reRenderComponent'); |
||||
|
} |
||||
|
}); |
||||
|
onBeforeUnmount(() => { |
||||
|
|
||||
|
console.log('onBeforeUnmount: 组件即将卸载,清除定时器'); |
||||
|
}); |
||||
|
onUnmounted(() => { |
||||
|
console.log('onUnmounted: 组件已卸载'); |
||||
|
}); */ |
||||
|
|
||||
|
watch(transferConfig, (newValue, oldValue) => { |
||||
|
|
||||
|
count1.value++ |
||||
|
|
||||
|
//emits('reRenderComponent', newValue); |
||||
|
try { |
||||
|
|
||||
|
count1.value++ |
||||
|
if (newValue.transferDataSource === "数据源") { |
||||
|
emits('reRenderComponent'); |
||||
|
|
||||
|
|
||||
|
getDetail().then(({ data }) => { |
||||
|
alert(2) |
||||
|
const data1 = processTreeData(data) |
||||
|
//console.log(`获取穿梭框接口数据`); |
||||
|
resData.value = data1.children |
||||
|
// 全选方法2:插入 全选node |
||||
|
userList.value = [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: [...resData.value] |
||||
|
}] |
||||
|
userList1.value = userList.value |
||||
|
if( treeRef.value){ |
||||
|
treeRef.value.setCheckedKeys(checkedIdList.value, true) |
||||
|
} |
||||
|
setTimeout(() => { |
||||
|
checkedIdList.value.forEach(element => { |
||||
|
const _node = treeRef?.value?.getNode(element) |
||||
|
if (!_node) return |
||||
|
const _checkList = checkList.value |
||||
|
if (_node.checked) { // 勾选时 |
||||
|
if (_node.checked && _node.childNodes.length === 0) { |
||||
|
_checkList.push(_node) |
||||
|
} |
||||
|
// 通过new Set() 实现数据去重 |
||||
|
checkList.value = Array.from(new Set(_checkList)) |
||||
|
// 遍历子级,递归,直到获取最后一级的人员 |
||||
|
if (_node.childNodes.length > 0) { |
||||
|
_node.childNodes.map(item => handleCheckList(item.data)) |
||||
|
} |
||||
|
} else if (!_node.checked) { // 取消勾选 |
||||
|
checkList.value = checkList.value.filter(item => item.checked) |
||||
|
} |
||||
|
}); |
||||
|
}, 50); |
||||
|
|
||||
|
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error('Error :', error); |
||||
|
// 可以在这里进行一些错误处理,比如设置默认值或其他操作 |
||||
|
//parsedData.value = null; |
||||
|
} |
||||
|
|
||||
|
}, { deep: true }); |
||||
|
|
||||
|
|
||||
|
watch(selectedValueCompu, (newValue, oldValue) => { |
||||
|
//console.log('selectedValueCompu 发生了变化', newValue); |
||||
|
checkedIdList.value = selectedValueCompu.value |
||||
|
//获取配置接口相应或获取固定选项值 |
||||
|
if (transferConfig.value.transferDataSource === "固定选项") { |
||||
|
setTimeout(() => { |
||||
|
|
||||
|
// 全选方法2:插入 全选node |
||||
|
userList.value = [{ |
||||
|
id: '全选', |
||||
|
label: '全选', |
||||
|
children: props.data?.control.fixedOptions |
||||
|
}] |
||||
|
userList1.value = userList.value |
||||
|
//console.log(treeRef) |
||||
|
/* console.log(checkedIdList.value) */ |
||||
|
if( treeRef.value){ |
||||
|
treeRef.value.setCheckedKeys(checkedIdList.value, true) |
||||
|
} |
||||
|
setTimeout(() => { |
||||
|
checkedIdList.value.forEach(element => { |
||||
|
const _node = treeRef?.value?.getNode(element) |
||||
|
if (!_node) return |
||||
|
const _checkList = checkList.value |
||||
|
if (_node.checked) { // 勾选时 |
||||
|
if (_node.checked && _node.childNodes.length === 0) { |
||||
|
_checkList.push(_node) |
||||
|
} |
||||
|
// 通过new Set() 实现数据去重 |
||||
|
checkList.value = Array.from(new Set(_checkList)) |
||||
|
// 遍历子级,递归,直到获取最后一级的人员 |
||||
|
if (_node.childNodes.length > 0) { |
||||
|
_node.childNodes.map(item => handleCheckList(item.data)) |
||||
|
} |
||||
|
} else if (!_node.checked) { // 取消勾选 |
||||
|
checkList.value = checkList.value.filter(item => item.checked) |
||||
|
} |
||||
|
}); |
||||
|
}, 50); |
||||
|
}, 50); |
||||
|
dataFinished.value = true |
||||
|
} else { |
||||
|
getDetail |
||||
|
dataFinished.value = true |
||||
|
} |
||||
|
//emits('updateModel',newValue); |
||||
|
}, { deep: true }); |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
<style scoped> |
||||
|
.transfer { |
||||
|
display: flex; |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
.transferName { |
||||
|
height: 30px; |
||||
|
padding-top: 8px; |
||||
|
background-color: #F5F7FA; |
||||
|
padding-left: 10px; |
||||
|
border-radius: 5px 5px 0 0; |
||||
|
border-bottom: 1px solid gainsboro; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.buttonArea { |
||||
|
|
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.buttonArea2 { |
||||
|
height: 33px; |
||||
|
} |
||||
|
|
||||
|
.leftScroll { |
||||
|
height: 367px; |
||||
|
} |
||||
|
|
||||
|
.leftArea { |
||||
|
border: 1px solid gainsboro; |
||||
|
width: 300px; |
||||
|
border-radius: 5px; |
||||
|
height: 456px; |
||||
|
position: relative; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.rigthArea { |
||||
|
border: 1px solid gainsboro; |
||||
|
width: 300px; |
||||
|
border-radius: 5px; |
||||
|
height: 456px; |
||||
|
background-color: white; |
||||
|
|
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue