From a5f313fd00f0081061629763631391f6509ac56f Mon Sep 17 00:00:00 2001 From: xuyuxiang Date: Tue, 3 Mar 2026 00:15:41 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=9B=B4=E6=96=B0=E3=80=91=E6=9C=BA?= =?UTF-8?q?=E6=9E=84=E5=A4=A7=E6=95=B0=E6=8D=AE=E4=BC=98=E5=8C=96=EF=BC=88?= =?UTF-8?q?=E5=BE=85=E5=AE=8C=E5=96=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/XnOrgSelector/index.vue | 8 +- .../src/components/XnPageSelect/README.md | 7 +- .../src/components/XnPageSelect/index.vue | 5 +- .../components/XnPositionSelector/index.vue | 8 +- .../src/components/XnRoleSelector/index.vue | 10 +- .../src/components/XnUserSelector/index.vue | 8 +- snowy-admin-web/src/views/biz/dict/form.vue | 8 +- snowy-admin-web/src/views/biz/dict/index.vue | 7 +- .../src/views/biz/org/copyForm.vue | 7 +- snowy-admin-web/src/views/biz/org/form.vue | 8 +- snowy-admin-web/src/views/biz/org/index.vue | 28 ++-- .../src/views/biz/position/form.vue | 8 +- .../src/views/biz/position/index.vue | 28 ++-- snowy-admin-web/src/views/biz/user/form.vue | 156 +++++++++++------- snowy-admin-web/src/views/biz/user/index.vue | 29 ++-- .../src/views/dev/dict/category/form.vue | 7 +- snowy-admin-web/src/views/gen/basic.vue | 7 +- .../src/views/mobile/resource/menu/form.vue | 7 +- .../src/views/sys/org/copyForm.vue | 7 +- snowy-admin-web/src/views/sys/org/form.vue | 8 +- snowy-admin-web/src/views/sys/org/index.vue | 28 ++-- .../src/views/sys/position/form.vue | 8 +- .../src/views/sys/position/index.vue | 28 ++-- snowy-admin-web/src/views/sys/role/form.vue | 8 +- snowy-admin-web/src/views/sys/role/index.vue | 7 +- snowy-admin-web/src/views/sys/user/form.vue | 156 +++++++++++------- snowy-admin-web/src/views/sys/user/index.vue | 30 ++-- 27 files changed, 309 insertions(+), 317 deletions(-) diff --git a/snowy-admin-web/src/components/XnOrgSelector/index.vue b/snowy-admin-web/src/components/XnOrgSelector/index.vue index 4e582300..755bcc37 100644 --- a/snowy-admin-web/src/components/XnOrgSelector/index.vue +++ b/snowy-admin-web/src/components/XnOrgSelector/index.vue @@ -34,6 +34,7 @@ @@ -243,6 +240,7 @@ const slots = useSlots() // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 获取机构树数据 const treeData = ref() // 默认展开二级树的节点id diff --git a/snowy-admin-web/src/components/XnPageSelect/README.md b/snowy-admin-web/src/components/XnPageSelect/README.md index 7c45cf2d..19fabd70 100644 --- a/snowy-admin-web/src/components/XnPageSelect/README.md +++ b/snowy-admin-web/src/components/XnPageSelect/README.md @@ -27,11 +27,7 @@ XnPageSelect 分页下拉选择器 tree-default-expand-all :tree-data="treeData" :tree-default-expanded-keys="treeDefaultExpandedKeys" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" @change="selePositionData(formData.orgId, 0)" > @@ -56,6 +52,7 @@ import bizUserApi from '@/api/biz/bizUserApi' // 表单数据 const formData = ref({}) const xnPositionPageSelectRef = ref() +const treeFieldNames = { children: 'children', title: 'name', key: 'id' } // 机构选择后查询对应的职位 const selePositionData = (orgId, type) => { diff --git a/snowy-admin-web/src/components/XnPageSelect/index.vue b/snowy-admin-web/src/components/XnPageSelect/index.vue index 204c1f90..8c6f421e 100644 --- a/snowy-admin-web/src/components/XnPageSelect/index.vue +++ b/snowy-admin-web/src/components/XnPageSelect/index.vue @@ -5,7 +5,7 @@ v-if="props.showSearch" v-model:value="modelValue" :options="options" - :field-names="{ label: 'name', value: 'id' }" + :field-names="selectFieldNames" class="xn-wd" :placeholder="props.placeholder" :allow-clear="props.allowClear" @@ -20,7 +20,7 @@ v-else v-model:value="modelValue" :options="options" - :field-names="{ label: 'name', value: 'id' }" + :field-names="selectFieldNames" class="xn-wd" :placeholder="props.placeholder" :allow-clear="props.allowClear" @@ -40,6 +40,7 @@ const initParams = ref({}) const options = ref([]) const spinning = ref(false) + const selectFieldNames = { label: 'name', value: 'id' } const emit = defineEmits({ change: null, 'update:value': null, search: null }) const props = defineProps({ value: { diff --git a/snowy-admin-web/src/components/XnPositionSelector/index.vue b/snowy-admin-web/src/components/XnPositionSelector/index.vue index 3a7f7724..cf5fb52d 100644 --- a/snowy-admin-web/src/components/XnPositionSelector/index.vue +++ b/snowy-admin-web/src/components/XnPositionSelector/index.vue @@ -34,6 +34,7 @@ @@ -239,6 +236,7 @@ const slots = useSlots() // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 获取职位树数据 const treeData = ref() // 默认展开二级树的节点id diff --git a/snowy-admin-web/src/components/XnRoleSelector/index.vue b/snowy-admin-web/src/components/XnRoleSelector/index.vue index 4f5a18c8..97ca7fca 100644 --- a/snowy-admin-web/src/components/XnRoleSelector/index.vue +++ b/snowy-admin-web/src/components/XnRoleSelector/index.vue @@ -34,6 +34,7 @@ { - if (typeof props.orgTreeApi !== 'function' || typeof props.rolePageApi !== 'function') { + if ((typeof props.orgTreeApi !== 'function' && typeof props.orgTreeLazyApi !== 'function') || typeof props.rolePageApi !== 'function') { message.warning('未配置角色选择器API') return } diff --git a/snowy-admin-web/src/components/XnUserSelector/index.vue b/snowy-admin-web/src/components/XnUserSelector/index.vue index d6d7a2f0..76722ce1 100644 --- a/snowy-admin-web/src/components/XnUserSelector/index.vue +++ b/snowy-admin-web/src/components/XnUserSelector/index.vue @@ -41,6 +41,7 @@ @@ -253,6 +250,7 @@ const selectedTableListLoading = ref(false) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 获取机构树数据 const treeData = ref() // 默认展开二级树的节点id diff --git a/snowy-admin-web/src/views/biz/dict/form.vue b/snowy-admin-web/src/views/biz/dict/form.vue index 90f16771..a9dbed64 100644 --- a/snowy-admin-web/src/views/biz/dict/form.vue +++ b/snowy-admin-web/src/views/biz/dict/form.vue @@ -11,11 +11,7 @@ placeholder="请选择上级字典" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" treeLine /> @@ -52,7 +48,7 @@ const treeData = ref([]) // 默认展开的节点(顶级) const defaultExpandedKeys = ref([0]) - const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 打开抽屉 const onOpen = (record, parentId) => { visible.value = true diff --git a/snowy-admin-web/src/views/biz/dict/index.vue b/snowy-admin-web/src/views/biz/dict/index.vue index 35ac1937..ab3b7e0f 100644 --- a/snowy-admin-web/src/views/biz/dict/index.vue +++ b/snowy-admin-web/src/views/biz/dict/index.vue @@ -22,11 +22,7 @@ placeholder="请选择上级字典" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" selectable="false" tree-line /> @@ -119,6 +115,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'dictLabel', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false } // 表格查询 返回 Promise 对象 diff --git a/snowy-admin-web/src/views/biz/org/copyForm.vue b/snowy-admin-web/src/views/biz/org/copyForm.vue index ed5473d6..0f78cdca 100644 --- a/snowy-admin-web/src/views/biz/org/copyForm.vue +++ b/snowy-admin-web/src/views/biz/org/copyForm.vue @@ -10,11 +10,7 @@ allow-clear tree-default-expand-all :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" tree-line /> @@ -48,6 +44,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 选中的ID列表 const ids = ref([]) diff --git a/snowy-admin-web/src/views/biz/org/form.vue b/snowy-admin-web/src/views/biz/org/form.vue index e2dbc08f..f21e2cbf 100644 --- a/snowy-admin-web/src/views/biz/org/form.vue +++ b/snowy-admin-web/src/views/biz/org/form.vue @@ -15,11 +15,7 @@ placeholder="请选择上级机构" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" tree-line :load-data="onLoadData" /> @@ -71,7 +67,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) - const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 在树中递归查找节点 const findNodeInTree = (nodes, id) => { diff --git a/snowy-admin-web/src/views/biz/org/index.vue b/snowy-admin-web/src/views/biz/org/index.vue index 992897ca..31ec4236 100644 --- a/snowy-admin-web/src/views/biz/org/index.vue +++ b/snowy-admin-web/src/views/biz/org/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ placeholder="请选择上级机构" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -184,6 +181,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 树容器高度自适应 const treeContainerRef = ref(null) const treeHeight = ref(0) diff --git a/snowy-admin-web/src/views/biz/position/form.vue b/snowy-admin-web/src/views/biz/position/form.vue index ce48e928..827b9756 100644 --- a/snowy-admin-web/src/views/biz/position/form.vue +++ b/snowy-admin-web/src/views/biz/position/form.vue @@ -15,11 +15,7 @@ placeholder="请选择组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" tree-line :load-data="onLoadData" > @@ -63,7 +59,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) - + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 在树中递归查找节点 const findNodeInTree = (nodes, id) => { if (!nodes) return false diff --git a/snowy-admin-web/src/views/biz/position/index.vue b/snowy-admin-web/src/views/biz/position/index.vue index 6522ee45..dec361f2 100644 --- a/snowy-admin-web/src/views/biz/position/index.vue +++ b/snowy-admin-web/src/views/biz/position/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ placeholder="请选择机构" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -175,6 +172,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 树容器高度自适应 const treeContainerRef = ref(null) const treeHeight = ref(0) diff --git a/snowy-admin-web/src/views/biz/user/form.vue b/snowy-admin-web/src/views/biz/user/form.vue index a9a18553..b818b74c 100644 --- a/snowy-admin-web/src/views/biz/user/form.vue +++ b/snowy-admin-web/src/views/biz/user/form.vue @@ -45,6 +45,7 @@ + + + + + + @@ -122,6 +128,7 @@ :name="['positionJson', index, 'orgId']" :rules="{ required: true, message: '请选择机构' }" > + + @@ -142,6 +150,7 @@ :name="['positionJson', index, 'positionId']" :rules="{ required: true, message: '请选择岗位' }" > + + + + @@ -308,8 +320,10 @@ const activeTabsKey = ref('1') const emit = defineEmits({ successful: null }) const formLoading = ref(false) + const treeLoading = ref(false) const treeData = ref([]) const treeDefaultExpandedKeys = ref([]) + const childTreeExpandedKeys = ref([]) // 分页select组件dom定义 const xnPositionPageSelectRef = ref() const xnUserPageSelectRef = ref() @@ -319,33 +333,12 @@ const formData = ref({}) const treeFieldNames = { children: 'children', label: 'name', key: 'id', value: 'id' } - // 在树中递归查找节点 - const findNodeInTree = (nodes, id) => { - if (!nodes) return false - for (const node of nodes) { - if (node.id === id) return true - if (node.children && findNodeInTree(node.children, id)) return true - } - return false - } - // 确保选中的机构节点在树中可回显名称 - const ensureOrgsInTree = (orgIds) => { - const missingIds = orgIds.filter((id) => id && !findNodeInTree(treeData.value, id)) - if (missingIds.length === 0) return - userCenterApi.userCenterGetOrgListByIdList({ idList: missingIds }).then((data) => { - if (data && data.length > 0) { - data.forEach((org) => { - treeData.value.push({ ...org, isLeaf: true }) - }) - treeData.value = [...treeData.value] - } - }) - } - // 树加载Promise,用于协调回显时序 - let treeLoadPromise = null + // 是否为编辑模式(编辑时加载全量树,新增时懒加载) + const isEditMode = ref(false) // 打开抽屉 const onOpen = (record, orgId) => { visible.value = true + isEditMode.value = !!record formData.value = { gender: '男', positionJson: [] @@ -357,25 +350,42 @@ selePositionData(orgId) }) } - if (record) { - convertFormData(record) - } nextTick(() => { - // 机构选择器数据 - treeLoadPromise = bizUserApi.userOrgTreeLazySelector().then((res) => { - if (res !== null) { - treeData.value = res.map((item) => { - return { - ...item, - isLeaf: item.isLeaf === undefined ? false : item.isLeaf + if (isEditMode.value) { + // 编辑模式:加载全量树 + 详情,等都完成后展开到选中节点 + treeLoading.value = true + const treePromise = bizUserApi.userOrgTreeLazySelector({ searchKey: '' }).then((res) => { + if (res !== null) { + treeData.value = res + // 只有一个根节点时才自动展开 + if (treeData.value.length === 1) { + treeDefaultExpandedKeys.value.push(treeData.value[0].id) } - }) - // 只有一个根节点时才自动展开 - if (treeData.value.length === 1) { - treeDefaultExpandedKeys.value.push(treeData.value[0].id) } - } - }) + }) + const detailPromise = convertFormData(record) + Promise.all([treePromise, detailPromise]).then(() => { + expandToSelectedOrgs() + }).finally(() => { + treeLoading.value = false + }) + } else { + // 新增模式:懒加载树 + bizUserApi.userOrgTreeLazySelector().then((res) => { + if (res !== null) { + treeData.value = res.map((item) => { + return { + ...item, + isLeaf: item.isLeaf === undefined ? false : item.isLeaf + } + }) + // 只有一个根节点时才自动展开 + if (treeData.value.length === 1) { + treeDefaultExpandedKeys.value.push(treeData.value[0].id) + } + } + }) + } }) } // 懒加载子节点 @@ -405,15 +415,51 @@ const onClose = () => { treeData.value = [] treeDefaultExpandedKeys.value = [] + childTreeExpandedKeys.value = [] visible.value = false } + // 在全量树中查找目标节点的所有祖先ID(用于展开树到选中节点) + const collectAncestorKeys = (nodes, targetId, path = []) => { + if (!nodes) return null + for (const node of nodes) { + if (node.id === targetId) return path + if (node.children) { + const found = collectAncestorKeys(node.children, targetId, [...path, node.id]) + if (found) return found + } + } + return null + } + // 展开树到所有选中的机构节点 + const expandToSelectedOrgs = () => { + // 主选择机构:只展开主orgId的祖先 + if (formData.value.orgId) { + const ancestors = collectAncestorKeys(treeData.value, formData.value.orgId) + if (ancestors) { + ancestors.forEach((id) => { + if (!treeDefaultExpandedKeys.value.includes(id)) { + treeDefaultExpandedKeys.value.push(id) + } + }) + } + } + // 任职信息:每行独立展开 + if (formData.value.positionJson) { + formData.value.positionJson.forEach((item, index) => { + if (item.orgId) { + const ancestors = collectAncestorKeys(treeData.value, item.orgId) + childTreeExpandedKeys.value[index] = ancestors ? [...ancestors] : [] + } + }) + } + } // 回显数据 const convertFormData = (record) => { const param = { id: record.id } // 查询详情 - const detailPromise = bizUserApi.userDetail(param).then((data) => { + return bizUserApi.userDetail(param).then((data) => { if (data.positionJson) { // 替换表单中的格式与后端查到的 data.positionJson = JSON.parse(data.positionJson) @@ -428,24 +474,6 @@ }) } selePositionData(formData.value.orgId) - return data - }) - // 等待树和详情都加载完成后,确保机构节点可回显 - nextTick(() => { - if (treeLoadPromise) { - Promise.all([treeLoadPromise, detailPromise]).then(([_, detail]) => { - const orgIds = [detail.orgId] - if (detail.positionJson) { - const positions = typeof detail.positionJson === 'string' - ? JSON.parse(detail.positionJson) - : detail.positionJson - positions.forEach((item) => { - if (item.orgId) orgIds.push(item.orgId) - }) - } - ensureOrgsInTree(orgIds) - }) - } }) } // 默认要校验的 @@ -518,10 +546,12 @@ positionId: undefined, directorId: undefined }) + childTreeExpandedKeys.value.push([]) } // 删减行 const delDomains = (index) => { formData.value.positionJson.splice(index, 1) + childTreeExpandedKeys.value.splice(index, 1) } // 子表行内选择机构 const childOrgSelect = async (data, type, index) => { diff --git a/snowy-admin-web/src/views/biz/user/index.vue b/snowy-admin-web/src/views/biz/user/index.vue index 1eb93fd2..a1a87a29 100644 --- a/snowy-admin-web/src/views/biz/user/index.vue +++ b/snowy-admin-web/src/views/biz/user/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ placeholder="请选择所属机构" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -173,7 +170,6 @@
@@ -69,6 +65,7 @@ const treeData = ref([]) // 默认展开的节点(顶级) const defaultExpandedKeys = ref([0]) + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 定义字典颜色 const dictColorList = [ 'default', diff --git a/snowy-admin-web/src/views/gen/basic.vue b/snowy-admin-web/src/views/gen/basic.vue index ca065238..c26f4183 100644 --- a/snowy-admin-web/src/views/gen/basic.vue +++ b/snowy-admin-web/src/views/gen/basic.vue @@ -66,11 +66,7 @@ allow-clear tree-default-expand-all :tree-data="menuTreeData" - :field-names="{ - children: 'children', - label: 'title', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" tree-line /> @@ -192,6 +188,7 @@ const tableList = ref([]) const tableColumns = ref([]) const menuTreeData = ref([]) + const treeFieldNames = { children: 'children', label: 'title', value: 'id' } const submitLoading = ref(false) const moduleOptions = ref() const generateTypeOptions = ref([ diff --git a/snowy-admin-web/src/views/mobile/resource/menu/form.vue b/snowy-admin-web/src/views/mobile/resource/menu/form.vue index 76aa3d9b..f23385cd 100644 --- a/snowy-admin-web/src/views/mobile/resource/menu/form.vue +++ b/snowy-admin-web/src/views/mobile/resource/menu/form.vue @@ -19,11 +19,7 @@ allow-clear tree-default-expand-all :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'title', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" tree-line @change="parentChange(formData.parentId)" @@ -111,6 +107,7 @@ // 默认展开的节点(顶级) const defaultExpandedKeys = ref([0]) const treeData = ref([]) + const treeFieldNames = { children: 'children', label: 'title', value: 'id' } const formData = ref({}) // 类别 const moduleId = ref('') diff --git a/snowy-admin-web/src/views/sys/org/copyForm.vue b/snowy-admin-web/src/views/sys/org/copyForm.vue index 9d7a5483..59907d3b 100644 --- a/snowy-admin-web/src/views/sys/org/copyForm.vue +++ b/snowy-admin-web/src/views/sys/org/copyForm.vue @@ -10,11 +10,7 @@ allow-clear tree-default-expand-all :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" tree-line /> @@ -48,6 +44,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 选中的ID列表 const ids = ref([]) diff --git a/snowy-admin-web/src/views/sys/org/form.vue b/snowy-admin-web/src/views/sys/org/form.vue index 190b75ad..e94a858e 100644 --- a/snowy-admin-web/src/views/sys/org/form.vue +++ b/snowy-admin-web/src/views/sys/org/form.vue @@ -15,11 +15,7 @@ placeholder="请选择上级组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" tree-line :load-data="onLoadData" /> @@ -71,7 +67,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) - + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 在树中递归查找节点 const findNodeInTree = (nodes, id) => { if (!nodes) return false diff --git a/snowy-admin-web/src/views/sys/org/index.vue b/snowy-admin-web/src/views/sys/org/index.vue index c7eae3d6..b62ac00e 100644 --- a/snowy-admin-web/src/views/sys/org/index.vue +++ b/snowy-admin-web/src/views/sys/org/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ placeholder="请选择上级组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -181,6 +178,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 树容器高度自适应 const treeContainerRef = ref(null) const treeHeight = ref(0) diff --git a/snowy-admin-web/src/views/sys/position/form.vue b/snowy-admin-web/src/views/sys/position/form.vue index 2f5eda0e..f7e86e99 100644 --- a/snowy-admin-web/src/views/sys/position/form.vue +++ b/snowy-admin-web/src/views/sys/position/form.vue @@ -15,11 +15,7 @@ placeholder="请选择组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" tree-line :load-data="onLoadData" > @@ -62,7 +58,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) - + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 在树中递归查找节点 const findNodeInTree = (nodes, id) => { if (!nodes) return false diff --git a/snowy-admin-web/src/views/sys/position/index.vue b/snowy-admin-web/src/views/sys/position/index.vue index eafc06b8..8ad78096 100644 --- a/snowy-admin-web/src/views/sys/position/index.vue +++ b/snowy-admin-web/src/views/sys/position/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ placeholder="请选择组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -172,6 +169,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 树容器高度自适应 const treeContainerRef = ref(null) const treeHeight = ref(0) diff --git a/snowy-admin-web/src/views/sys/role/form.vue b/snowy-admin-web/src/views/sys/role/form.vue index 91febc25..4507168c 100644 --- a/snowy-admin-web/src/views/sys/role/form.vue +++ b/snowy-admin-web/src/views/sys/role/form.vue @@ -30,11 +30,7 @@ allow-clear tree-default-expand-all :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeFieldNames" selectable="false" tree-line /> @@ -65,7 +61,7 @@ // 定义机构元素 const treeData = ref([]) const submitLoading = ref(false) - const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeFieldNames = { children: 'children', label: 'name', value: 'id' } // 打开抽屉 const onOpen = (record, category, orgId) => { visible.value = true diff --git a/snowy-admin-web/src/views/sys/role/index.vue b/snowy-admin-web/src/views/sys/role/index.vue index 83cbd908..8a3f919b 100644 --- a/snowy-admin-web/src/views/sys/role/index.vue +++ b/snowy-admin-web/src/views/sys/role/index.vue @@ -38,11 +38,7 @@ placeholder="请选择组织" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" @change="onCategoryOrOrgIdSelect" @@ -216,6 +212,7 @@ const treeData = ref([]) // 替换treeNode 中 title,key,children const treeFieldNames = { children: 'children', title: 'name', key: 'id' } + const treeSelectFieldNames = { children: 'children', label: 'name', value: 'id' } // 记录数据 const recordCacheData = ref({}) // 树容器高度自适应 diff --git a/snowy-admin-web/src/views/sys/user/form.vue b/snowy-admin-web/src/views/sys/user/form.vue index 476d7c30..004a1632 100644 --- a/snowy-admin-web/src/views/sys/user/form.vue +++ b/snowy-admin-web/src/views/sys/user/form.vue @@ -45,6 +45,7 @@ + + + + + + @@ -122,6 +128,7 @@ :name="['positionJson', index, 'orgId']" :rules="{ required: true, message: '请选择组织' }" > + + @@ -142,6 +150,7 @@ :name="['positionJson', index, 'positionId']" :rules="{ required: true, message: '请选择职位' }" > + + + + @@ -308,8 +320,10 @@ const activeTabsKey = ref('1') const emit = defineEmits({ successful: null }) const formLoading = ref(false) + const treeLoading = ref(false) const treeData = ref([]) const treeDefaultExpandedKeys = ref([]) + const childTreeExpandedKeys = ref([]) // 分页select组件dom定义 const xnPositionPageSelectRef = ref() const xnUserPageSelectRef = ref() @@ -319,33 +333,12 @@ const formData = ref({}) const treeFieldNames = { children: 'children', label: 'name', key: 'id', value: 'id' } - // 在树中递归查找节点 - const findNodeInTree = (nodes, id) => { - if (!nodes) return false - for (const node of nodes) { - if (node.id === id) return true - if (node.children && findNodeInTree(node.children, id)) return true - } - return false - } - // 确保选中的机构节点在树中可回显名称 - const ensureOrgsInTree = (orgIds) => { - const missingIds = orgIds.filter((id) => id && !findNodeInTree(treeData.value, id)) - if (missingIds.length === 0) return - userCenterApi.userCenterGetOrgListByIdList({ idList: missingIds }).then((data) => { - if (data && data.length > 0) { - data.forEach((org) => { - treeData.value.push({ ...org, isLeaf: true }) - }) - treeData.value = [...treeData.value] - } - }) - } - // 树加载Promise,用于协调回显时序 - let treeLoadPromise = null + // 是否为编辑模式(编辑时加载全量树,新增时懒加载) + const isEditMode = ref(false) // 打开抽屉 const onOpen = (record, orgId) => { visible.value = true + isEditMode.value = !!record formData.value = { gender: '男', positionJson: [] @@ -357,25 +350,42 @@ selePositionData(orgId) }) } - if (record) { - convertFormData(record) - } nextTick(() => { - // 机构选择器数据 - treeLoadPromise = userApi.userOrgTreeLazySelector().then((res) => { - if (res !== null) { - treeData.value = res.map((item) => { - return { - ...item, - isLeaf: item.isLeaf === undefined ? false : item.isLeaf + if (isEditMode.value) { + // 编辑模式:加载全量树 + 详情,等都完成后展开到选中节点 + treeLoading.value = true + const treePromise = userApi.userOrgTreeLazySelector({ searchKey: '' }).then((res) => { + if (res !== null) { + treeData.value = res + // 只有一个根节点时才自动展开 + if (treeData.value.length === 1) { + treeDefaultExpandedKeys.value.push(treeData.value[0].id) } - }) - // 只有一个根节点时才自动展开 - if (treeData.value.length === 1) { - treeDefaultExpandedKeys.value.push(treeData.value[0].id) } - } - }) + }) + const detailPromise = convertFormData(record) + Promise.all([treePromise, detailPromise]).then(() => { + expandToSelectedOrgs() + }).finally(() => { + treeLoading.value = false + }) + } else { + // 新增模式:懒加载树 + userApi.userOrgTreeLazySelector().then((res) => { + if (res !== null) { + treeData.value = res.map((item) => { + return { + ...item, + isLeaf: item.isLeaf === undefined ? false : item.isLeaf + } + }) + // 只有一个根节点时才自动展开 + if (treeData.value.length === 1) { + treeDefaultExpandedKeys.value.push(treeData.value[0].id) + } + } + }) + } }) } // 懒加载子节点 @@ -405,15 +415,51 @@ const onClose = () => { treeData.value = [] treeDefaultExpandedKeys.value = [] + childTreeExpandedKeys.value = [] visible.value = false } + // 在全量树中查找目标节点的所有祖先ID(用于展开树到选中节点) + const collectAncestorKeys = (nodes, targetId, path = []) => { + if (!nodes) return null + for (const node of nodes) { + if (node.id === targetId) return path + if (node.children) { + const found = collectAncestorKeys(node.children, targetId, [...path, node.id]) + if (found) return found + } + } + return null + } + // 展开树到所有选中的机构节点 + const expandToSelectedOrgs = () => { + // 主选择组织:只展开主orgId的祖先 + if (formData.value.orgId) { + const ancestors = collectAncestorKeys(treeData.value, formData.value.orgId) + if (ancestors) { + ancestors.forEach((id) => { + if (!treeDefaultExpandedKeys.value.includes(id)) { + treeDefaultExpandedKeys.value.push(id) + } + }) + } + } + // 任职信息:每行独立展开 + if (formData.value.positionJson) { + formData.value.positionJson.forEach((item, index) => { + if (item.orgId) { + const ancestors = collectAncestorKeys(treeData.value, item.orgId) + childTreeExpandedKeys.value[index] = ancestors ? [...ancestors] : [] + } + }) + } + } // 回显数据 const convertFormData = (record) => { const param = { id: record.id } // 查询详情 - const detailPromise = userApi.userDetail(param).then((data) => { + return userApi.userDetail(param).then((data) => { if (data.positionJson) { // 替换表单中的格式与后端查到的 data.positionJson = JSON.parse(data.positionJson) @@ -428,24 +474,6 @@ }) } selePositionData(formData.value.orgId) - return data - }) - // 等待树和详情都加载完成后,确保机构节点可回显 - nextTick(() => { - if (treeLoadPromise) { - Promise.all([treeLoadPromise, detailPromise]).then(([_, detail]) => { - const orgIds = [detail.orgId] - if (detail.positionJson) { - const positions = typeof detail.positionJson === 'string' - ? JSON.parse(detail.positionJson) - : detail.positionJson - positions.forEach((item) => { - if (item.orgId) orgIds.push(item.orgId) - }) - } - ensureOrgsInTree(orgIds) - }) - } }) } @@ -519,10 +547,12 @@ positionId: undefined, directorId: undefined }) + childTreeExpandedKeys.value.push([]) } // 删减行 const delDomains = (index) => { formData.value.positionJson.splice(index, 1) + childTreeExpandedKeys.value.splice(index, 1) } // 子表行内选择机构 diff --git a/snowy-admin-web/src/views/sys/user/index.vue b/snowy-admin-web/src/views/sys/user/index.vue index 5bbbafad..74de1b7f 100644 --- a/snowy-admin-web/src/views/sys/user/index.vue +++ b/snowy-admin-web/src/views/sys/user/index.vue @@ -12,16 +12,17 @@ />
- + + +
@@ -38,11 +39,7 @@ :placeholder="$t('user.placeholderUserOrg')" allow-clear :tree-data="treeData" - :field-names="{ - children: 'children', - label: 'name', - value: 'id' - }" + :field-names="treeSelectFieldNames" tree-line :load-data="onLoadData" /> @@ -187,7 +184,7 @@