|
|
|
@ -5,22 +5,23 @@ |
|
|
|
--> |
|
|
|
<script lang='ts' setup> |
|
|
|
import { getOrgTreeList } from '@/api/hr/org/index' |
|
|
|
import { computed } from 'vue'; |
|
|
|
const props = withDefaults( |
|
|
|
defineProps<{ |
|
|
|
defineProps<{ |
|
|
|
data: FormList |
|
|
|
tablekey: any |
|
|
|
numrun?: number |
|
|
|
modelValue?: any // 子表和弹性布局时时有传 |
|
|
|
tProp?: string // 子表时的form-item的prop值,用于子表校验用 |
|
|
|
types?:number |
|
|
|
}>(), |
|
|
|
{} |
|
|
|
types?: number |
|
|
|
}>(), |
|
|
|
{} |
|
|
|
) |
|
|
|
const emits = defineEmits<{ |
|
|
|
(e: 'update:modelValue', numVal: any): void |
|
|
|
(e: 'update:modelValue', numVal: any): void |
|
|
|
}>() |
|
|
|
|
|
|
|
const value = computed({ |
|
|
|
/* const value = computed({ |
|
|
|
get: () => { |
|
|
|
if (props.modelValue != "" && props.modelValue != undefined){ |
|
|
|
return props.modelValue*1 |
|
|
|
@ -29,27 +30,280 @@ const value = computed({ |
|
|
|
} |
|
|
|
}, |
|
|
|
set: (newVal: any) => { |
|
|
|
console.log(newVal) |
|
|
|
emits('update:modelValue', newVal) |
|
|
|
}, |
|
|
|
}); */ |
|
|
|
|
|
|
|
|
|
|
|
const value = computed({ |
|
|
|
get: () => { |
|
|
|
if (props.modelValue != "" && props.modelValue != undefined) { |
|
|
|
if (hasComma(props.modelValue) || multiple) { |
|
|
|
return commaStringToNumberArray(props.modelValue) |
|
|
|
} else { |
|
|
|
return props.modelValue * 1; |
|
|
|
} |
|
|
|
} else { |
|
|
|
return props.modelValue; |
|
|
|
} |
|
|
|
}, |
|
|
|
set: (newVal: any) => { |
|
|
|
|
|
|
|
//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); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
const orgTreeList = ref<orgInfo[]>(); |
|
|
|
|
|
|
|
|
|
|
|
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(','); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 过滤组织架构树:仅保留「顶层目标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' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const orgTreeList = ref<orgInfo[]>(); |
|
|
|
const orgTreeLoading = ref(false); //加载行政组织树 |
|
|
|
const orgTreeProps ={ |
|
|
|
children: 'child', |
|
|
|
label: 'name', |
|
|
|
const orgTreeProps = { |
|
|
|
children: 'child', |
|
|
|
label: 'name', |
|
|
|
} //行政组织树对照值 |
|
|
|
/** |
|
|
|
@ 作者: 秦东 |
|
|
|
@ 时间: 2024-12-05 16:23:01 |
|
|
|
@ 功能: 获取行政组织 |
|
|
|
*/ |
|
|
|
function haveOrgTreeInfo(){ |
|
|
|
function haveOrgTreeInfo() { |
|
|
|
orgTreeLoading.value = true; |
|
|
|
getOrgTreeList({"orgid": 309}) |
|
|
|
.then(({ data })=>{ |
|
|
|
orgTreeList.value = data |
|
|
|
}).finally(()=>{orgTreeLoading.value = false;}) |
|
|
|
getOrgTreeList({ "orgid": 309 }) |
|
|
|
.then(({ data }) => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//liwenxuan 250916 start |
|
|
|
|
|
|
|
// 目标ID数组(需保留的核心节点ID)自动忽略id数组中为其他id子节点的id |
|
|
|
const targetIds = props.data.control.range |
|
|
|
//console.log(targetIds) |
|
|
|
if (targetIds && targetIds.length > 0) { |
|
|
|
// 执行过滤 |
|
|
|
const filteredTree = filterOrganizationTree(data, targetIds); |
|
|
|
orgTreeList.value = filteredTree |
|
|
|
} else { |
|
|
|
orgTreeList.value = data; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//liwenxuan 250916 end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//orgTreeList.value = data |
|
|
|
}).finally(() => { orgTreeLoading.value = false; }) |
|
|
|
} |
|
|
|
onBeforeMount(() => { |
|
|
|
haveOrgTreeInfo(); |
|
|
|
@ -58,30 +312,30 @@ onBeforeMount(() => { |
|
|
|
|
|
|
|
|
|
|
|
interface TreeNodeInfo { |
|
|
|
value: number; |
|
|
|
label: string; |
|
|
|
value: number; |
|
|
|
label: string; |
|
|
|
} |
|
|
|
|
|
|
|
function treeData(node: orgInfo[],title: TreeNodeInfo[]){ |
|
|
|
|
|
|
|
function treeData(node: orgInfo[], title: TreeNodeInfo[]) { |
|
|
|
|
|
|
|
// let title = new Array |
|
|
|
if(Array.isArray(node)){ |
|
|
|
node.forEach((item:TreeNode) => { |
|
|
|
if(Array.isArray(title)){ |
|
|
|
if (Array.isArray(node)) { |
|
|
|
node.forEach((item: TreeNode) => { |
|
|
|
if (Array.isArray(title)) { |
|
|
|
title.push({ |
|
|
|
value:item.id, |
|
|
|
label:item.name |
|
|
|
value: item.id, |
|
|
|
label: item.name |
|
|
|
}); |
|
|
|
}else{ |
|
|
|
title =[{ |
|
|
|
value:item.id, |
|
|
|
label:item.name |
|
|
|
} else { |
|
|
|
title = [{ |
|
|
|
value: item.id, |
|
|
|
label: item.name |
|
|
|
}]; |
|
|
|
} |
|
|
|
if (item.child) { |
|
|
|
|
|
|
|
if(Array.isArray(item.child)){ |
|
|
|
treeData(item.child,title) |
|
|
|
if (Array.isArray(item.child)) { |
|
|
|
treeData(item.child, title) |
|
|
|
// for (const child of item.children) { |
|
|
|
// treeData(child,title) |
|
|
|
// } |
|
|
|
@ -89,58 +343,96 @@ function treeData(node: orgInfo[],title: TreeNodeInfo[]){ |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return title; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ 作者: 秦东 |
|
|
|
@ 时间: 2025-06-10 11:13:06 |
|
|
|
@ 功能: 输出结果 |
|
|
|
*/ |
|
|
|
const valPrint = (val:any,orgList:orgInfo[]) => { |
|
|
|
const valPrint = (val: any, orgList: orgInfo[]) => { |
|
|
|
let titlaAry = new Array |
|
|
|
// console.log(val,"=======>orgList------------>",orgList) |
|
|
|
// if(Array.isArray(orgList)){ |
|
|
|
// console.log(val,"=======>orgList-------2----->",orgList) |
|
|
|
// orgList.forEach((item: any) => { |
|
|
|
// console.log(val,"=======>orgList-----3------->",item.id) |
|
|
|
// if(item.id==val){ |
|
|
|
// console.log(val,"=======>orgList----1-------->",orgList) |
|
|
|
// title = item.name |
|
|
|
// } |
|
|
|
// }) |
|
|
|
// } |
|
|
|
if(val != "" && val != undefined){ |
|
|
|
let orgAry = treeData(orgList) |
|
|
|
console.log(val,"=======>orgList------------>",orgAry) |
|
|
|
if(Array.isArray(orgAry)){ |
|
|
|
orgAry.forEach((item:orgInfo) => { |
|
|
|
if(item.value == val){ |
|
|
|
titlaAry.push(item.label) |
|
|
|
if(isArray(val)){ |
|
|
|
val.forEach((ele: any) => { |
|
|
|
|
|
|
|
|
|
|
|
if (ele != "" && ele != undefined) { |
|
|
|
let orgAry = treeData(orgList) |
|
|
|
//console.log(val, "=======>orgList------------>", orgAry) |
|
|
|
if (Array.isArray(orgAry)) { |
|
|
|
//alert(1) |
|
|
|
orgAry.forEach((item: orgInfo) => { |
|
|
|
//console.log(item.value) |
|
|
|
if (item.value == ele) { |
|
|
|
titlaAry.push(item.label) |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
}else{ |
|
|
|
//console.log(orgList) |
|
|
|
// |
|
|
|
// console.log(val,"=======>orgList------------>",orgList) |
|
|
|
// if(Array.isArray(orgList)){ |
|
|
|
// console.log(val,"=======>orgList-------2----->",orgList) |
|
|
|
// orgList.forEach((item: any) => { |
|
|
|
// console.log(val,"=======>orgList-----3------->",item.id) |
|
|
|
// if(item.id==val){ |
|
|
|
// console.log(val,"=======>orgList----1-------->",orgList) |
|
|
|
// title = item.name |
|
|
|
// } |
|
|
|
// }) |
|
|
|
// } |
|
|
|
if (val != "" && val != undefined) { |
|
|
|
let orgAry = treeData(orgList) |
|
|
|
console.log(val, "=======>orgList------------>", orgAry) |
|
|
|
if (Array.isArray(orgAry)) { |
|
|
|
//alert(1) |
|
|
|
orgAry.forEach((item: orgInfo) => { |
|
|
|
console.log(item.value) |
|
|
|
if (item.value == val) { |
|
|
|
titlaAry.push(item.label) |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
return titlaAry.join("、") |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</script> |
|
|
|
<template> |
|
|
|
<el-tree-select |
|
|
|
v-if="props.types!=3" |
|
|
|
v-bind="$props" |
|
|
|
v-model="value" |
|
|
|
v-loading="orgTreeLoading" |
|
|
|
<template><div></div> |
|
|
|
<el-tree-select |
|
|
|
v-if="props.types != 3" |
|
|
|
v-bind="$props" |
|
|
|
v-model="value" |
|
|
|
|
|
|
|
node-key="id" |
|
|
|
:props="orgTreeProps" |
|
|
|
:data="orgTreeList" |
|
|
|
check-strictly |
|
|
|
:render-after-expand="false" |
|
|
|
:data="orgTreeList" |
|
|
|
:render-after-expand="false" |
|
|
|
|
|
|
|
:show-checkbox="multiple" |
|
|
|
:multiple="multiple" |
|
|
|
:check-strictly="!multiple" |
|
|
|
collapse-tags |
|
|
|
collapse-tags-tooltip |
|
|
|
:max-collapse-tags="2" |
|
|
|
/> |
|
|
|
<el-text v-else class="wordColor">{{valPrint(value,orgTreeList)}}</el-text> |
|
|
|
<el-text v-else class="wordColor">{{ valPrint(value, orgTreeList) }}</el-text> |
|
|
|
</template> |
|
|
|
<style lang='scss' scoped> |
|
|
|
.wordColor{ |
|
|
|
color:#000000; |
|
|
|
<style lang='scss' scoped> |
|
|
|
.wordColor { |
|
|
|
color: #000000; |
|
|
|
} |
|
|
|
</style> |