diff --git a/src/components/DesignForm/assembly/index.ts b/src/components/DesignForm/assembly/index.ts index a896a3c..1d8c278 100644 --- a/src/components/DesignForm/assembly/index.ts +++ b/src/components/DesignForm/assembly/index.ts @@ -1063,6 +1063,8 @@ export default [ icon: 'sliders', iconFont: 'fa-sliders', control: { + range:[], + multiple: '', }, config: {}, styles: { diff --git a/src/components/DesignForm/formControlPropertiNew.vue b/src/components/DesignForm/formControlPropertiNew.vue index 086cc61..6f4cdfb 100644 --- a/src/components/DesignForm/formControlPropertiNew.vue +++ b/src/components/DesignForm/formControlPropertiNew.vue @@ -13,6 +13,8 @@ import validateInt from "./validateInt"; import { ValidateTextTypes } from "@/components/DesignForm/validateText"; import { ElMessage } from "element-plus"; import { formatNumber } from "@/api/DesignForm/utils"; +import { getOrgTreeList } from "@/api/hr/org/index"; +import { orgInfo } from "@/api/hr/org/type"; import { PublicAtrr, formStruct, @@ -833,6 +835,22 @@ const attrList = computed(() => { vIf: state.isSearch, vShow: ["lowcodeImage"], }, + { + label: "数据范围", + value: config.orgCentent, + path: "config.orgCentent", + type: "orgCentent_range", + vIf: state.isSearch, + vShow: ["orgCentent"], + }, + { + label: "是否多选", + value: config.orgCentent, + path: "config.orgCentent", + type: "orgCentent_Multiple", + vIf: state.isSearch, + vShow: ["orgCentent"], + }, /* { label: "添加时间水印", value: config.lowcodeImage, @@ -2048,6 +2066,8 @@ watch( controlData.value.control.fillRoles.child[0].id = uuidv4() .replaceAll("-", "") .toString(); + }else if(controlData.value.type === "orgCentent"){ + haveOrgTreeInfo() } //实现关联表单设置可选字段中没有当前字段效果 start // console.log(controlData.value.name) @@ -2086,6 +2106,34 @@ watch( //实现关联表单设置可选字段中没有当前字段效果 end } ); + + +const orgTreeProps = { + children: "child", + label: "name", +}; //行政组织树对照值 + + +const orgCententRange = ref(); +function haveOrgTreeInfo() { + //orgTreeLoading.value = true; + getOrgTreeList({ orgid: 309 }) + .then(({ data }) => { + console.log("行政组织树对照值", data); + + + + orgCententRange.value = data; + + console.log(orgCententRange.value) + }) + .finally(() => { + //orgTreeLoading.value = false; + }); +} + + + //轮播图图片上传成功钩子 function carouselImgUploadSuccess( response: any, @@ -4404,6 +4452,30 @@ const aiAgentList = ref([ + + + + + + + + + + + + + + + diff --git a/src/components/DesignForm/public/expand/org.vue b/src/components/DesignForm/public/expand/org.vue index 0a429af..ba3a5fa 100644 --- a/src/components/DesignForm/public/expand/org.vue +++ b/src/components/DesignForm/public/expand/org.vue @@ -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(); 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} 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) { + 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} targetIds - 原始目标ID数组 + * @param {Map} nodeMap - 节点映射表 + * @returns {Array} 仅包含顶层目标ID的数组(无父节点在目标数组内) + */ +function filterChildTargetIds(targetIds: any[], nodeMap: Map) { + 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} topTargetIds - 预处理后的顶层目标ID数组 + * @param {Map} nodeMap - 节点映射表 + * @returns {Set} 需保留的节点ID集合(去重) + */ +function collectKeepIds(topTargetIds: any[], nodeMap: Map) { + 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} keepIds - 需保留的ID集合 + */ +function collectDescendants(childNodes: any[], nodeMap: any, keepIds: Set) { + if (!Array.isArray(childNodes)) return; + + childNodes.forEach(childNode => { + keepIds.add(childNode.id); + collectDescendants(childNode.child, nodeMap, keepIds); // 递归收集子节点的子节点 + }); +} + +/** + * 辅助函数5:递归过滤树形节点,仅保留在keepIds中的节点 + * @param {Array} nodes - 当前层级的节点数组 + * @param {Set} keepIds - 需保留的节点ID集合 + * @returns {Array} 过滤后的节点数组 + */ +function filterTreeNodes(nodes: any[], keepIds: Set) { + 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' + diff --git a/src/widget/org/cont.vue b/src/widget/org/cont.vue index 157864e..b1acc27 100644 --- a/src/widget/org/cont.vue +++ b/src/widget/org/cont.vue @@ -12,21 +12,115 @@ const props = defineProps({ } }) const orgName = ref("") + +/* const orgName = computed(()=>{ + return str.value +}) */ + +//let str = ref("") + //选择行政组织 const pickOrgVal = (val:any) => { if(val != "" && val != null){ - - getgovcont({id:val*1,idstr:val}) - .then(({data}) =>{ - // console.log("选择行政组织-3->",data,data.name) + if(hasComma(val)){ + + let arr = commaStringToNumberArray(val) + let str= '' + arr.forEach((element, index, array) => { + // 判断当前是否是最后一个元素 + const isLast = index === array.length - 1; + str = '' + + let item = element+"" + getgovcont({id:element*1,idstr:item}) + .then(({data}) =>{ + str+=data.name+"," + }) + .finally(()=>{ + if(isLast){ + //alert(1) + str = removeLastChar(str) + orgName.value = str + } + + + }) + + }); + + + }else{ + getgovcont({id:val*1,idstr:val}) + .then(({data}) =>{ + // console.log("选择行政组织-3->",data,data.name) orgName.value = data.name - }) - .finally(()=>{ - - }) + }) + .finally(()=>{}) + } + + + } } + +function removeLastChar(str: string | any[]) { + // 检查输入是否为字符串 + if (typeof str !== 'string') { + throw new Error('输入必须是字符串类型'); + } + + // 处理空字符串情况 + if (str.length === 0) { + return str; // 空字符串返回自身 + } + + // 截取从开始到倒数第二个字符的部分 + return str.slice(0, -1); +} + + +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; + }); +} + + onBeforeMount(() => { pickOrgVal(props.orgid) })