|
|
|
@ -11,6 +11,7 @@ const props = withDefaults( |
|
|
|
defineProps<{ |
|
|
|
modelValue?: string; |
|
|
|
disabled?: boolean; |
|
|
|
data?: Object; |
|
|
|
}>(), |
|
|
|
{} |
|
|
|
); |
|
|
|
@ -20,13 +21,33 @@ const emits = defineEmits<{ |
|
|
|
const value = computed({ |
|
|
|
get: () => { |
|
|
|
if (props.modelValue != "" && props.modelValue != undefined) { |
|
|
|
return props.modelValue * 1; |
|
|
|
if(hasComma(props.modelValue)||multiple){ |
|
|
|
return commaStringToNumberArray(props.modelValue) |
|
|
|
}else{ |
|
|
|
return props.modelValue * 1; |
|
|
|
} |
|
|
|
} else { |
|
|
|
return props.modelValue; |
|
|
|
} |
|
|
|
}, |
|
|
|
set: (newVal: any) => { |
|
|
|
emits("update:modelValue", newVal); |
|
|
|
|
|
|
|
//console.log(newVal) |
|
|
|
if(isArray(newVal)){ |
|
|
|
let str = arrayToCommaString(newVal) |
|
|
|
//console.log(2) |
|
|
|
//console.log(str) |
|
|
|
emits("update:modelValue", str); |
|
|
|
}else{ |
|
|
|
//console.log(1) |
|
|
|
//console.log(newVal) |
|
|
|
emits("update:modelValue", newVal); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// let newValJson:criteriaForPeopleList[] = JSON.parse(newVal) |
|
|
|
// if(newValJson.length > 0){ |
|
|
|
// let userAry = new Array |
|
|
|
@ -43,6 +64,62 @@ const value = computed({ |
|
|
|
// } |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
function hasComma(str: any) { |
|
|
|
// 检查输入是否为字符串类型 |
|
|
|
if (typeof str !== 'string') { |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// 使用indexOf检查是否包含逗号 |
|
|
|
// 如果存在逗号,返回true;否则返回false |
|
|
|
return str.indexOf(',') !== -1; |
|
|
|
} |
|
|
|
|
|
|
|
function commaStringToNumberArray(str: string) { |
|
|
|
// 检查输入是否为字符串 |
|
|
|
if (typeof str !== 'string') { |
|
|
|
throw new Error('输入必须是字符串'); |
|
|
|
} |
|
|
|
|
|
|
|
// 处理空字符串的情况 |
|
|
|
if (str.trim() === '') { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
// 按逗号分割字符串,处理可能的空格,并转换为数字 |
|
|
|
return str.split(',').map(item => { |
|
|
|
// 去除前后空格 |
|
|
|
const trimmed = item.trim(); |
|
|
|
// 转换为数字 |
|
|
|
const number = Number(trimmed); |
|
|
|
|
|
|
|
// 检查是否为有效数字 |
|
|
|
if (isNaN(number)) { |
|
|
|
throw new Error(`无法将 "${trimmed}" 转换为有效数字`); |
|
|
|
} |
|
|
|
|
|
|
|
return number; |
|
|
|
}); |
|
|
|
} |
|
|
|
function isArray(variable: any) { |
|
|
|
// 现代浏览器推荐使用的方法 |
|
|
|
if (typeof Array.isArray === 'function') { |
|
|
|
return Array.isArray(variable); |
|
|
|
} else { |
|
|
|
// 兼容旧环境的判断方式 |
|
|
|
return Object.prototype.toString.call(variable) === '[object Array]'; |
|
|
|
} |
|
|
|
} |
|
|
|
function arrayToCommaString(numbers: any[]) { |
|
|
|
// 检查输入是否为数组 |
|
|
|
if (!Array.isArray(numbers)) { |
|
|
|
throw new Error('输入必须是一个数组'); |
|
|
|
} |
|
|
|
|
|
|
|
// 使用join方法将数组元素用逗号连接 |
|
|
|
return numbers.join(','); |
|
|
|
} |
|
|
|
const orgTreeList = ref<orgInfo[]>(); |
|
|
|
const orgTreeLoading = ref(false); //加载行政组织树 |
|
|
|
const orgTreeProps = { |
|
|
|
@ -55,7 +132,21 @@ function haveOrgTreeInfo() { |
|
|
|
getOrgTreeList({ orgid: 309 }) |
|
|
|
.then(({ data }) => { |
|
|
|
console.log("行政组织树对照值", data); |
|
|
|
orgTreeList.value = data; |
|
|
|
|
|
|
|
//liwenxuan 250916 start |
|
|
|
|
|
|
|
// 目标ID数组(需保留的核心节点ID)自动忽略id数组中为其他id子节点的id |
|
|
|
const targetIds = props.data.control.range |
|
|
|
// 执行过滤 |
|
|
|
const filteredTree = filterOrganizationTree(data, targetIds); |
|
|
|
orgTreeList.value = filteredTree |
|
|
|
|
|
|
|
//liwenxuan 250916 end |
|
|
|
|
|
|
|
|
|
|
|
//orgTreeList.value = data; |
|
|
|
|
|
|
|
|
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
orgTreeLoading.value = false; |
|
|
|
@ -72,6 +163,145 @@ onMounted(() => { |
|
|
|
haveOrgTreeInfo(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 过滤组织架构树:仅保留「顶层目标ID」节点、其所有祖先节点和子孙节点 |
|
|
|
* (顶层目标ID:目标数组中无其他ID是其祖先的ID,子节点ID会被自动忽略) |
|
|
|
* @param {Array} originalTree - 原始组织架构树形结构(顶层为数组) |
|
|
|
* @param {Array<number>} targetIds - 目标节点ID数组(可能包含子节点ID) |
|
|
|
* @returns {Array} 过滤后的组织架构树 |
|
|
|
*/ |
|
|
|
function filterOrganizationTree(originalTree: orgInfo[], targetIds: any) { |
|
|
|
// 1. 构建「节点ID→节点」映射表,方便快速查找父/子节点 |
|
|
|
const nodeMap = new Map(); |
|
|
|
buildNodeMap(originalTree, nodeMap); |
|
|
|
|
|
|
|
// 2. 预处理目标ID:过滤掉「是其他目标ID子节点」的ID,仅保留顶层目标ID |
|
|
|
const topTargetIds = filterChildTargetIds(targetIds, nodeMap); |
|
|
|
if (topTargetIds.length === 0) { |
|
|
|
console.warn("警告:所有目标ID无效或均为其他目标ID的子节点,返回空树"); |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 收集所有需保留的节点ID(顶层目标ID + 所有祖先ID + 所有子孙ID) |
|
|
|
const keepIds = collectKeepIds(topTargetIds, nodeMap); |
|
|
|
|
|
|
|
// 4. 递归过滤原始树,仅保留需保留的节点 |
|
|
|
const filteredTree = filterTreeNodes(originalTree, keepIds); |
|
|
|
|
|
|
|
return filteredTree; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助函数1:遍历树形结构,构建「节点ID→节点」映射表 |
|
|
|
* @param {Array} nodes - 当前层级的节点数组(顶层/子节点数组) |
|
|
|
* @param {Map} nodeMap - 待构建的节点映射表 |
|
|
|
*/ |
|
|
|
function buildNodeMap(nodes: any[], nodeMap: Map<any, any>) { |
|
|
|
if (!nodes || !Array.isArray(nodes)) return; |
|
|
|
|
|
|
|
nodes.forEach(node => { |
|
|
|
nodeMap.set(node.id, node); |
|
|
|
// 递归处理子节点(兼容child为null的情况) |
|
|
|
buildNodeMap(Array.isArray(node.child) ? node.child : [], nodeMap); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助函数2:预处理目标ID数组,过滤掉「是其他目标ID子节点」的ID |
|
|
|
* @param {Array<number>} targetIds - 原始目标ID数组 |
|
|
|
* @param {Map} nodeMap - 节点映射表 |
|
|
|
* @returns {Array<number>} 仅包含顶层目标ID的数组(无父节点在目标数组内) |
|
|
|
*/ |
|
|
|
function filterChildTargetIds(targetIds: any[], nodeMap: Map<any, any>) { |
|
|
|
if (!targetIds || !Array.isArray(targetIds)) return []; |
|
|
|
|
|
|
|
// 先过滤掉不存在于映射表中的无效ID |
|
|
|
const validTargetIds = targetIds.filter(id => nodeMap.has(id)); |
|
|
|
if (validTargetIds.length <= 1) return validTargetIds; // 仅1个有效ID,直接返回 |
|
|
|
|
|
|
|
// 过滤逻辑:若ID的祖先链中存在其他有效目标ID,则视为子节点ID,需排除 |
|
|
|
return validTargetIds.filter(currentId => { |
|
|
|
let parentId = nodeMap.get(currentId).superior; |
|
|
|
// 向上追溯所有祖先,判断是否有祖先在有效目标ID中 |
|
|
|
while (parentId !== undefined && nodeMap.has(parentId)) { |
|
|
|
if (validTargetIds.includes(parentId)) { |
|
|
|
console.log(`提示:ID ${currentId} 是目标ID ${parentId} 的子节点,已忽略`); |
|
|
|
return false; // 是其他目标ID的子节点,排除 |
|
|
|
} |
|
|
|
parentId = nodeMap.get(parentId).superior; // 继续向上追溯 |
|
|
|
} |
|
|
|
return true; // 无祖先在目标数组内,保留为顶层目标ID |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助函数3:收集所有需保留的节点ID(顶层目标ID + 所有祖先ID + 所有子孙ID) |
|
|
|
* @param {Array<number>} topTargetIds - 预处理后的顶层目标ID数组 |
|
|
|
* @param {Map} nodeMap - 节点映射表 |
|
|
|
* @returns {Set<number>} 需保留的节点ID集合(去重) |
|
|
|
*/ |
|
|
|
function collectKeepIds(topTargetIds: any[], nodeMap: Map<any, any>) { |
|
|
|
const keepIds = new Set(); |
|
|
|
|
|
|
|
topTargetIds.forEach((targetId: unknown) => { |
|
|
|
const targetNode = nodeMap.get(targetId); |
|
|
|
|
|
|
|
// 1. 保留顶层目标节点自身 |
|
|
|
keepIds.add(targetId); |
|
|
|
|
|
|
|
// 2. 向上追溯:保留所有祖先节点(直到顶层节点) |
|
|
|
let parentId = targetNode.superior; |
|
|
|
while (parentId !== undefined && nodeMap.has(parentId)) { |
|
|
|
keepIds.add(parentId); |
|
|
|
parentId = nodeMap.get(parentId).superior; |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 向下递归:保留所有子孙节点(子→孙→曾孙...) |
|
|
|
collectDescendants(targetNode.child, nodeMap, keepIds); |
|
|
|
}); |
|
|
|
|
|
|
|
return keepIds; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助函数4:递归收集某个节点的所有子孙节点ID |
|
|
|
* @param {Array} childNodes - 当前节点的子节点数组 |
|
|
|
* @param {Map} nodeMap - 节点映射表 |
|
|
|
* @param {Set<number>} keepIds - 需保留的ID集合 |
|
|
|
*/ |
|
|
|
function collectDescendants(childNodes: any[], nodeMap: any, keepIds: Set<unknown>) { |
|
|
|
if (!Array.isArray(childNodes)) return; |
|
|
|
|
|
|
|
childNodes.forEach(childNode => { |
|
|
|
keepIds.add(childNode.id); |
|
|
|
collectDescendants(childNode.child, nodeMap, keepIds); // 递归收集子节点的子节点 |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 辅助函数5:递归过滤树形节点,仅保留在keepIds中的节点 |
|
|
|
* @param {Array} nodes - 当前层级的节点数组 |
|
|
|
* @param {Set<number>} keepIds - 需保留的节点ID集合 |
|
|
|
* @returns {Array} 过滤后的节点数组 |
|
|
|
*/ |
|
|
|
function filterTreeNodes(nodes: any[], keepIds: Set<unknown>) { |
|
|
|
if (!Array.isArray(nodes) || !keepIds) return []; |
|
|
|
|
|
|
|
return nodes.reduce((filtered, node) => { |
|
|
|
if (keepIds.has(node.id)) { |
|
|
|
// 深拷贝节点(避免修改原始数据),并递归过滤子节点 |
|
|
|
const clonedNode = { ...node }; |
|
|
|
clonedNode.child = filterTreeNodes(Array.isArray(clonedNode.child) ? clonedNode.child : [], keepIds); |
|
|
|
filtered.push(clonedNode); |
|
|
|
} |
|
|
|
return filtered; |
|
|
|
}, []); |
|
|
|
} |
|
|
|
|
|
|
|
const multiple = props.data.control.multiple=='1' |
|
|
|
|
|
|
|
</script> |
|
|
|
<template> |
|
|
|
<el-tree-select |
|
|
|
@ -81,9 +311,11 @@ onMounted(() => { |
|
|
|
node-key="id" |
|
|
|
:props="orgTreeProps" |
|
|
|
:data="orgTreeList" |
|
|
|
check-strictly |
|
|
|
:render-after-expand="false" |
|
|
|
clearable |
|
|
|
:show-checkbox="multiple" |
|
|
|
:multiple="multiple" |
|
|
|
:check-strictly="!multiple" |
|
|
|
/> |
|
|
|
<div></div> |
|
|
|
</template> |
|
|
|
|