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 @@