Skip to content

Commit faa5c9f

Browse files
perf: Lazy loading for third-party platform organization structure
1 parent 0b4a765 commit faa5c9f

6 files changed

Lines changed: 123 additions & 17 deletions

File tree

frontend/src/api/system.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const modelApi = {
2727
query: (id: number) => request.get(`/system/aimodel/${id}`),
2828
setDefault: (id: number) => request.put(`/system/aimodel/default/${id}`),
2929
check: (data: any) => request.fetchStream('/system/aimodel/status', data),
30-
platform: (id: number) => request.get(`/system/platform/org/${id}`),
30+
platform: (id: number, lazy?: number, pid?: string) =>
31+
request.get(`/system/platform/org/${id}`, { params: { lazy, pid } }),
3132
userSync: (data: any) => request.post(`/system/platform/user/sync`, data),
3233
}

frontend/src/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"field_details": "Field Details",
3939
"integration": "Platform integration needs to be enabled.",
4040
"the_existing_user": "If the user already exists, overwrite the existing user.",
41+
"lazy_load": "Lazy Load",
4142
"sync_users": "Sync Users",
4243
"sync_wechat_users": "Sync WeChat Users",
4344
"sync_dingtalk_users": "Sync DingTalk Users",

frontend/src/i18n/ko-KR.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"field_details": "필드 세부 정보",
3939
"integration": "플랫폼 통합을 활성화해야 합니다.",
4040
"the_existing_user": "해당 사용자가 이미 존재하는 경우 기존 사용자를 덮어씁니다.",
41+
"lazy_load": "지연 로딩",
4142
"sync_users": "사용자 동기화",
4243
"sync_wechat_users": "위챗 사용자 동기화",
4344
"sync_dingtalk_users": "딩톡 사용자 동기화",

frontend/src/i18n/zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"field_details": "字段详情",
3939
"integration": "需开启平台对接",
4040
"the_existing_user": "若用户已存在,覆盖旧用户",
41+
"lazy_load": "懒加载",
4142
"sync_users": "同步用户",
4243
"sync_wechat_users": "同步企业微信用户",
4344
"sync_dingtalk_users": "同步钉钉用户",

frontend/src/i18n/zh-TW.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"field_details": "欄位詳情",
3939
"integration": "需開啟平台對接",
4040
"the_existing_user": "若使用者已存在,覆蓋舊使用者",
41+
"lazy_load": "懶加載",
4142
"sync_users": "同步使用者",
4243
"sync_wechat_users": "同步企業微信使用者",
4344
"sync_dingtalk_users": "同步釘釘使用者",

frontend/src/views/system/user/SyncUserDing.vue

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
<template>
2-
<el-dialog
3-
v-model="centerDialogVisible"
4-
:title="$t(dialogTitle)"
5-
modal-class="sync-user_ding"
6-
width="840"
7-
>
2+
<el-dialog v-model="centerDialogVisible" modal-class="sync-user_ding" width="840">
3+
<template #header>
4+
<div class="dialog-header">
5+
<span style="margin-right: 12px">{{ $t(dialogTitle) }}</span>
6+
<el-checkbox v-model="isLazy" class="lazy-checkbox">
7+
{{ $t('sync.lazy_load') }}
8+
</el-checkbox>
9+
</div>
10+
</template>
811
<div v-loading="loading" class="flex border" style="height: 428px; border-radius: 6px">
912
<div class="p-16 border-r">
1013
<el-input
14+
v-if="!isLazy"
1115
v-model="search"
1216
:validate-event="false"
1317
:placeholder="$t('datasource.search')"
@@ -20,19 +24,26 @@
2024
</el-icon>
2125
</template>
2226
</el-input>
23-
<div class="mt-8 max-height_workspace">
27+
<div
28+
class="max-height_workspace"
29+
:class="{ 'mt-8': !isLazy }"
30+
:style="isLazy ? { maxHeight: '100%' } : {}"
31+
>
2432
<el-tree
33+
:key="`${treeKey}-${isLazy}`"
2534
ref="organizationUserRef"
2635
style="max-width: 426px"
2736
class="checkbox-group-block"
28-
:data="organizationUserList"
37+
:data="isLazy ? EMPTY_DATA : organizationUserList"
2938
:filter-node-method="filterNode"
3039
show-checkbox
3140
:default-checked-keys="defaultCheckedKeys"
3241
:props="defaultProps"
3342
node-key="id"
34-
default-expand-all
43+
:default-expand-all="!isLazy"
3544
:expand-on-click-node="false"
45+
:lazy="isLazy"
46+
:load="isLazy ? loadNode : undefined"
3647
@check="handleCheck"
3748
>
3849
<template #default="{ node, data }">
@@ -112,16 +123,15 @@
112123
</template>
113124

114125
<script lang="ts" setup>
115-
import { ref, computed, watch, nextTick } from 'vue'
116126
import { modelApi } from '@/api/system'
117-
import { ElLoading } from 'element-plus-secondary'
118-
import avatar_personal from '@/assets/svg/avatar_personal.svg'
119127
import avatar_organize from '@/assets/svg/avatar_organize.svg'
128+
import avatar_personal from '@/assets/svg/avatar_personal.svg'
120129
import Close from '@/assets/svg/icon_close_outlined_w.svg'
121130
import Search from '@/assets/svg/icon_search-outline_outlined.svg'
122-
import type { CheckboxValueType } from 'element-plus-secondary'
123-
import type { FilterNodeMethodFunction } from 'element-plus-secondary'
131+
import type { CheckboxValueType, FilterNodeMethodFunction } from 'element-plus-secondary'
132+
import { ElLoading } from 'element-plus-secondary'
124133
import { cloneDeep } from 'lodash-es'
134+
import { computed, nextTick, ref, watch } from 'vue'
125135
const checkAll = ref(false)
126136
const existingUser = ref(false)
127137
const isIndeterminate = ref(false)
@@ -134,12 +144,17 @@ const defaultCheckedKeys = ref<any[]>([])
134144
const defaultProps = {
135145
children: 'children',
136146
label: 'name',
147+
isLeaf: (data: any) => data.options?.is_user,
137148
}
138149
let rawTree: any = []
139150
const organizationUserList = ref<any[]>([])
140151
const loading = ref(false)
141152
const centerDialogVisible = ref(false)
142153
const checkTableList = ref([] as any[])
154+
const EMPTY_DATA: any[] = []
155+
const isLazy = ref(true)
156+
const treeKey = ref(0)
157+
const wecomDeptCache = ref(new Map<string, any[]>())
143158
144159
const workspaceWithKeywords = computed(() => {
145160
return workspace.value.filter((ele: any) =>
@@ -185,6 +200,21 @@ watch(search, () => {
185200
})
186201
})
187202
203+
watch(isLazy, async () => {
204+
search.value = ''
205+
checkTableList.value = []
206+
checkedWorkspace.value = []
207+
defaultCheckedKeys.value = []
208+
wecomDeptCache.value.clear()
209+
loading.value = true
210+
const systemWorkspaceList = await modelApi.platform(oid, isLazy.value ? 1 : 0)
211+
organizationUserList.value = isLazy.value
212+
? stripDeptChildren(systemWorkspaceList.tree || [])
213+
: systemWorkspaceList.tree || []
214+
rawTree = cloneDeep(systemWorkspaceList.tree)
215+
loading.value = false
216+
})
217+
188218
const filterNode: FilterNodeMethodFunction = (value: string, data: any) => {
189219
if (!value) return true
190220
return data.name.includes(value)
@@ -230,7 +260,61 @@ const handleCheckedWorkspaceChange = (value: CheckboxValueType[]) => {
230260
}
231261
let oid: any = null
232262
263+
const buildWecomDeptCache = (tree: any[]) => {
264+
const cache = new Map<string, any[]>()
265+
const stack = [...tree]
266+
while (stack.length) {
267+
const node = stack.pop()!
268+
if (!node.options?.is_user && node.children?.length) {
269+
cache.set(
270+
node.id,
271+
node.children.map((c: any) => ({ ...c, children: [] }))
272+
)
273+
stack.push(...node.children)
274+
}
275+
}
276+
wecomDeptCache.value = cache
277+
}
278+
279+
const stripDeptChildren = (nodes: any[]) => {
280+
return nodes.map((node: any) => {
281+
if (!node.options?.is_user) {
282+
const { children, ...rest } = node
283+
void children
284+
return rest
285+
}
286+
return { ...node }
287+
})
288+
}
289+
290+
const loadNode = (node: any, resolve: any) => {
291+
if (node.level === 0) {
292+
modelApi.platform(oid, 1).then((res: any) => {
293+
resolve(stripDeptChildren(res.tree || []))
294+
})
295+
return
296+
}
297+
if (oid === 6) {
298+
const deptChildren = wecomDeptCache.value.get(node.data.id) || []
299+
modelApi.platform(oid, 1, node.data.id).then((res: any) => {
300+
resolve(stripDeptChildren([...(res.tree || []), ...deptChildren]))
301+
nextTick(() => {
302+
organizationUserRef.value?.setChecked(node.data.id, false, true)
303+
})
304+
})
305+
return
306+
}
307+
modelApi.platform(oid, 1, node.data.id).then((res: any) => {
308+
resolve(stripDeptChildren(res.tree || []))
309+
nextTick(() => {
310+
organizationUserRef.value?.setChecked(node.data.id, false, true)
311+
})
312+
})
313+
}
314+
233315
const open = async (id: any, title: any) => {
316+
isLazy.value = true
317+
treeKey.value++
234318
dialogTitle.value = title
235319
loading.value = true
236320
search.value = ''
@@ -240,8 +324,15 @@ const open = async (id: any, title: any) => {
240324
checkAll.value = false
241325
isIndeterminate.value = false
242326
const loadingInstance = ElLoading.service({ fullscreen: true })
243-
const systemWorkspaceList = await modelApi.platform(id)
244-
organizationUserList.value = systemWorkspaceList.tree || []
327+
const systemWorkspaceList = await modelApi.platform(id, isLazy.value ? 1 : 0)
328+
if (isLazy.value && id === 6) {
329+
buildWecomDeptCache(systemWorkspaceList.tree)
330+
organizationUserList.value = stripDeptChildren(systemWorkspaceList.tree || [])
331+
} else {
332+
organizationUserList.value = isLazy.value
333+
? stripDeptChildren(systemWorkspaceList.tree || [])
334+
: systemWorkspaceList.tree || []
335+
}
245336
rawTree = cloneDeep(systemWorkspaceList.tree)
246337
loadingInstance?.close()
247338
loading.value = false
@@ -283,6 +374,16 @@ defineExpose({
283374
</script>
284375
<style lang="less">
285376
.sync-user_ding {
377+
.dialog-header {
378+
display: flex;
379+
align-items: center;
380+
381+
.lazy-checkbox {
382+
font-size: 12px;
383+
color: #8f959e;
384+
}
385+
}
386+
286387
.mb-8 {
287388
margin-bottom: 8px;
288389
}

0 commit comments

Comments
 (0)