wui-components-v2 1.1.70 → 1.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/api/feishu.ts +20 -20
  2. package/api/index.ts +4 -4
  3. package/api/login.ts +51 -51
  4. package/api/tree.ts +28 -28
  5. package/components/action-popup/action-popup.vue +116 -116
  6. package/components/add-address-list/add-address-list.vue +187 -187
  7. package/components/add-address-page/config.ts +297 -297
  8. package/components/audio-play/audio-play.vue +220 -220
  9. package/components/batch-upload-file/batch-upload-file.vue +75 -75
  10. package/components/card-botom-buttons/card-botom-buttons.vue +279 -279
  11. package/components/demo-card/demo-card.vue +44 -44
  12. package/components/fold-card/fold-card.vue +2 -2
  13. package/components/form-control/form-control.vue +9 -1
  14. package/components/global-loading/GlobalLoading.md +366 -366
  15. package/components/global-loading/global-loading.vue +55 -55
  16. package/components/global-message/GlobalMessage.md +570 -570
  17. package/components/global-toast/GlobalToast.md +261 -261
  18. package/components/global-toast/global-toast.vue +56 -56
  19. package/components/label-value/open-value-more.vue +56 -56
  20. package/components/loading/loading.vue +5 -5
  21. package/components/login-form-box/login-form-box.vue +65 -65
  22. package/components/phone-login-form/phone-login-form.vue +204 -204
  23. package/components/privacy-popup/privacy-popup.vue +191 -191
  24. package/components/scan-input/scan-input.vue +406 -406
  25. package/components/select-col-picker/select-col-picker.vue +168 -168
  26. package/components/tab-search/tab-search.vue +73 -73
  27. package/components/tree-select/components/TreeNode.vue +200 -200
  28. package/components/tree-select/components/index.vue +471 -471
  29. package/components/tree-select/components/tree-select.vue +56 -56
  30. package/components/tree-select/index.vue +93 -93
  31. package/components/user-choose/user-choose.vue +169 -151
  32. package/components/video-play/video-play.vue +56 -56
  33. package/components/wui-auto-update-component/wui-auto-update-component.vue +132 -132
  34. package/components/wui-default/wui-default.vue +49 -49
  35. package/components/wui-details-page/wui-details-page.vue +147 -147
  36. package/components/wui-edit-page/wui-edit-page-copy.vue +757 -757
  37. package/components/wui-edit-page/wui-edit-page.vue +793 -793
  38. package/components/wui-login/wui-login.vue +43 -43
  39. package/components/wui-login1/wui-login.vue +41 -41
  40. package/components/wui-menus/wui-menus-top.vue +137 -137
  41. package/components/wui-menus/wui-menus-top1.vue +8 -8
  42. package/components/wui-menus1/components/banner-carousel.vue +320 -320
  43. package/components/wui-menus1/components/quick-panel.vue +55 -55
  44. package/components/wui-menus1/components/search-bar.vue +49 -49
  45. package/components/wui-menus1/components/section-menus.vue +208 -208
  46. package/components/wui-menus1/wui-menus-top.vue +718 -718
  47. package/components/wui-menus1/wui-menus-top1.vue +398 -398
  48. package/components/wui-notify-info/notify-handle.vue +53 -53
  49. package/components/wui-scan-binding-sensor/wui-scan-binding-sensor.vue +75 -75
  50. package/components/wui-search-history-babbar/components/SearchBar.vue +70 -70
  51. package/components/wui-select-list/wui-select-list.vue +1 -9
  52. package/components/wui-select-popup/wui-select-popup.vue +1 -0
  53. package/components/wui-tree-page/wui-tree-page.vue +297 -297
  54. package/components/wui-update-component/wui-update-component.vue +221 -221
  55. package/composables/types/theme.ts +65 -65
  56. package/composables/types/user.ts +5 -5
  57. package/composables/useColorGenerator.ts +221 -221
  58. package/composables/useCompanyFieldFilter.ts +26 -24
  59. package/composables/useEnumes.ts +43 -43
  60. package/composables/useEventBus.ts +37 -37
  61. package/composables/useGlobalLoading.ts +47 -47
  62. package/composables/useGlobalMessage.ts +53 -53
  63. package/composables/useGlobalToast.ts +63 -63
  64. package/composables/useLocale.ts +54 -54
  65. package/composables/useManualTheme.ts +179 -179
  66. package/composables/useSectionMenus.ts +120 -120
  67. package/composables/useTabbar.ts +50 -50
  68. package/composables/useTheme.ts +74 -74
  69. package/composables/useUser.ts +24 -24
  70. package/index.d.ts +90 -90
  71. package/package.json +24 -24
  72. package/store/manualThemeStore.ts +143 -143
  73. package/store/persist.ts +41 -41
  74. package/store/themeStore.ts +74 -74
  75. package/store/userStore.ts +22 -22
  76. package/styles/dark-mode.scss +498 -498
  77. package/type.ts +154 -154
  78. package/utils/control-tree.ts +353 -353
  79. package/utils/eventBus.ts +36 -36
  80. package/utils/idata-scan.ts +99 -99
  81. package/utils/index.ts +979 -979
  82. package/utils/rsaEncrypt.ts +10 -10
@@ -1,471 +1,471 @@
1
- <script setup lang="ts">
2
- import { computed, defineEmits, defineOptions, defineProps, onMounted, ref, toRaw } from 'vue'
3
- import { useManualTheme } from '../../../composables/useManualTheme'
4
- import { treeConfigApi, treeDataApi, treeNodeDataApi } from '../../../api/tree'
5
- import { pageConfig } from '../../../api/page'
6
- import loadingCom from '../../loading/loading.vue'
7
- import TreeSelect from './tree-select.vue'
8
-
9
- defineOptions({
10
- name: 'TreeSelectIndex',
11
- })
12
- const props = defineProps<{
13
- sourceId: string
14
- selectMode: SelectMode
15
- }>()
16
- const emit = defineEmits(['handleConfirm', 'handleClose'])
17
- // 树形节点数据结构定义
18
- interface TreeNode {
19
- id: string
20
- name: string
21
- children?: TreeNode[]
22
- [key: string]: any // 允许其他自定义属性
23
- }
24
-
25
- const { currentThemeColor } = useManualTheme()
26
- // 选择模式类型
27
- type SelectMode = 'single' | 'multiple'
28
-
29
- // 示例树形数据
30
- // const treeData: TreeNode[] = [
31
- // {
32
- // id: '1',
33
- // name: '产品管理',
34
- // children: [
35
- // {
36
- // id: '1-1',
37
- // name: '产品列表',
38
- // children: [
39
- // { id: '1-1-1', name: '产品查询' },
40
- // { id: '1-1-2', name: '产品新增' },
41
- // { id: '1-1-3', name: '产品编辑' },
42
- // { id: '1-1-4', name: '产品删除' },
43
- // ],
44
- // },
45
- // {
46
- // id: '1-2',
47
- // name: '产品分类',
48
- // children: [
49
- // { id: '1-2-1', name: '分类管理' },
50
- // { id: '1-2-2', name: '分类排序' },
51
- // ],
52
- // },
53
- // ],
54
- // },
55
- // {
56
- // id: '2',
57
- // name: '订单管理',
58
- // children: [
59
- // { id: '2-1', name: '订单列表' },
60
- // { id: '2-2', name: '订单详情' },
61
- // { id: '2-3', name: '订单导出' },
62
- // ],
63
- // },
64
- // {
65
- // id: '3',
66
- // name: '用户管理',
67
- // children: [
68
- // {
69
- // id: '3-1',
70
- // name: '用户列表',
71
- // children: [
72
- // { id: '3-1-1', name: '用户查询' },
73
- // { id: '3-1-2', name: '用户编辑' },
74
- // ],
75
- // },
76
- // { id: '3-2', name: '角色管理' },
77
- // ],
78
- // },
79
- // { id: '4', name: '系统设置' },
80
- // ]
81
- const treeData = ref<TreeNode[]>([])
82
-
83
- // 选择模式
84
- const selectMode = ref<SelectMode>(props.selectMode)
85
-
86
- // 选中的节点ID
87
- const selectedKeys = ref<string[]>([])
88
- // 选中的节点
89
- const selectedNode = ref<TreeNode[]>([])
90
-
91
- // 搜索关键词
92
- const searchKeyword = ref('')
93
-
94
- // 所有节点的引用,用于滚动定位
95
- const nodeRefs = ref<Record<string, HTMLElement | null>>({})
96
-
97
- // 处理选择模式变更
98
- // function handleModeChange(mode: SelectMode) {
99
- // // 切换模式时清空选择
100
- // selectedKeys.value = []
101
- // }
102
- const treeConfigs = reactive({
103
- branchRatmplId: '',
104
- branchTitle: '',
105
- id: '',
106
- rootGtmplId: '',
107
- primaryColumnId1: '',
108
- primaryColumnId2: '',
109
- leafRatmplId: '',
110
- })
111
-
112
- const loading = ref(false)
113
- // 获取配置
114
- async function treeConfig1() {
115
- try {
116
- const res = await treeConfigApi(props.sourceId)
117
- treeConfigs.branchRatmplId = res.ttmplConfig.branchRatmplId
118
- treeConfigs.branchTitle = res.ttmplConfig.branchTitle
119
- treeConfigs.id = res.ttmplConfig.id
120
- treeConfigs.rootGtmplId = res.ttmplConfig.rootGtmplId
121
- treeConfigs.leafRatmplId = res.ttmplConfig.leafRatmplId
122
- treeConfig2(treeConfigs.branchRatmplId)
123
- }
124
- catch (error) {
125
- console.log('error:', error)
126
- // TODO handle the exception
127
- }
128
- }
129
-
130
- // 获取配置2
131
- async function treeConfig2(branchRatmplId: string) {
132
- try {
133
- const res1 = await pageConfig(branchRatmplId)
134
- treeConfigs.primaryColumnId1 = res1.ltmplConfig.primaryColumn.sourceId
135
- getTreeTopData(treeConfigs.id)
136
- }
137
- catch (error) {
138
- console.log('error:', error)
139
- // TODO handle the exception
140
- }
141
- }
142
-
143
- // 根据id找到树节点
144
- async function findTreeNodeandAppend(id: string, arr: TreeNode[], appendData: any) {
145
- for (let index = 0; index < arr.length; index++) {
146
- const element = arr[index]
147
- if (element.id === id) {
148
- element.children = appendData.map((item: any) => {
149
- return {
150
- id: item.code,
151
- name: item.fieldMap[treeConfigs.primaryColumnId1],
152
- children: [],
153
- }
154
- })
155
- }
156
- else {
157
- if (element.children)
158
- findTreeNodeandAppend(id, element.children, appendData)
159
- }
160
- }
161
- }
162
-
163
- // 获取树顶层数据
164
- async function getTreeTopData(id: string) {
165
- try {
166
- loading.value = true
167
- const res = await treeDataApi(id)
168
- treeData.value = res.entities.map((item: any) => {
169
- return {
170
- id: item.code,
171
- name: item.fieldMap[treeConfigs.primaryColumnId1],
172
- children: [],
173
- }
174
- })
175
- }
176
- catch (error) {
177
- console.log('error:', error)
178
- // TODO handle the exception
179
- }
180
- finally {
181
- loading.value = false
182
- }
183
- }
184
-
185
- // 获取树节点数据
186
- async function getTreeNodeData(node: TreeNode) {
187
- try {
188
- const res = await treeNodeDataApi(treeConfigs.branchRatmplId, node.id)
189
- if (!res.entities.length)
190
- return
191
- // 追加节点
192
- findTreeNodeandAppend(node.id, treeData.value, res.entities)
193
- }
194
- catch (error) {
195
- console.log('error:', error)
196
- }
197
- }
198
-
199
- function handleToggleExpand(node: TreeNode) {
200
- getTreeNodeData(node)
201
- }
202
-
203
- onMounted(() => {
204
- treeConfig1()
205
- })
206
-
207
- // 处理选择变更
208
- function handleSelectChange(keys: string[], node: TreeNode) {
209
- selectedNode.value = [node]
210
- selectedKeys.value = keys
211
- }
212
-
213
- // 处理标签关闭
214
- function handleTagClose(key: string) {
215
- selectedKeys.value = selectedKeys.value.filter(k => k !== key)
216
- }
217
-
218
- // 根据ID获取节点名称
219
- function getNodeNameByKey(key: string): string {
220
- const findNode = (nodes: TreeNode[]): TreeNode | null => {
221
- for (const node of nodes) {
222
- if (node.id === key) {
223
- return node
224
- }
225
- if (node.children && node.children.length > 0) {
226
- const found = findNode(node.children)
227
- if (found)
228
- return found
229
- }
230
- }
231
- return null
232
- }
233
-
234
- const node = findNode(treeData.value)
235
- return node ? node.name : ''
236
- }
237
-
238
- // 处理搜索
239
- function handleSearch(keyword?: string) {
240
- // 搜索逻辑在filteredTreeData计算属性中处理
241
- if (keyword !== undefined) {
242
- searchKeyword.value = keyword
243
- }
244
- }
245
-
246
- // 过滤后的树形数据
247
- const filteredTreeData = computed<TreeNode[]>(() => {
248
- if (!searchKeyword.value) {
249
- return treeData.value
250
- }
251
-
252
- const keyword = searchKeyword.value.toLowerCase()
253
-
254
- // 过滤节点的递归函数
255
- const filterNodes = (nodes: TreeNode[]): TreeNode[] => {
256
- return nodes
257
- .map((node) => {
258
- // 复制节点,避免修改原数据
259
- const newNode = { ...node }
260
-
261
- // 如果有子节点,递归过滤
262
- if (newNode.children && newNode.children.length > 0) {
263
- newNode.children = filterNodes(newNode.children)
264
- }
265
-
266
- // 节点名称包含关键词,或者有符合条件的子节点,则保留该节点
267
- const nodeNameMatch = newNode.name.toLowerCase().includes(keyword)
268
- const hasMatchingChildren = newNode.children && newNode.children.length > 0
269
-
270
- return nodeNameMatch || hasMatchingChildren ? newNode : null
271
- })
272
- .filter(Boolean) as TreeNode[]
273
- }
274
-
275
- return filterNodes(treeData.value)
276
- })
277
-
278
- // 记录节点引用
279
- function setNodeRef(id: string, el: HTMLElement | null) {
280
- if (el) {
281
- nodeRefs.value[id] = el
282
- }
283
- }
284
-
285
- // 滚动到指定节点
286
- function scrollToNode(id: string) {
287
- const node = nodeRefs.value[id]
288
- if (node) {
289
- node.scrollIntoView({ behavior: 'smooth', block: 'center' })
290
- // 高亮效果
291
- node.classList.add('node-highlight')
292
- setTimeout(() => {
293
- node.classList.remove('node-highlight')
294
- }, 2000)
295
- }
296
- }
297
-
298
- // 页面加载时展开有子节点的节点
299
- onMounted(() => {
300
- // 可以在这里添加默认展开逻辑
301
- })
302
-
303
- // 确定
304
- function handleConfirm() {
305
- emit('handleConfirm', toRaw(selectedKeys.value), toRaw(selectedNode.value), treeConfigs.leafRatmplId)
306
- }
307
-
308
- // 取消
309
- function handleClose() {
310
- emit('handleClose')
311
- }
312
- </script>
313
-
314
- <template>
315
- <view class="tree-select-page">
316
- <!-- 选择模式切换 -->
317
- <!-- <view class="mode-switch-container">
318
- <view class="mode-label">
319
- 选择模式:
320
- </view>
321
- <wd-radio-group
322
- v-model="selectMode"
323
- direction="horizontal"
324
- @change="handleModeChange"
325
- >
326
- <wd-radio name="single" label="单选" />
327
- <wd-radio name="multiple" label="多选" />
328
- </wd-radio-group>
329
- </view> -->
330
-
331
- <!-- 搜索框 -->
332
- <view class="search-container">
333
- <wd-search
334
- v-model="searchKeyword"
335
- placeholder-class="splaceholderClass"
336
- placeholder="搜索节点..."
337
- clearable
338
- @cancel="handleClose"
339
- @input="handleSearch"
340
- @clear="handleSearch"
341
- />
342
- </view>
343
- <!-- 已选结果 -->
344
- <view v-if="selectedKeys.length > 0" class="selected-result">
345
- <view class="result-title">
346
- 已选 {{ selectedKeys.length }} 项:
347
- </view>
348
- <view class="result-tags">
349
- <wd-tag
350
- v-for="key in selectedKeys"
351
- :key="key"
352
- :bg-color="currentThemeColor.primary"
353
- color="#fff"
354
- closable
355
- round
356
- @close="handleTagClose(key)"
357
- @click="scrollToNode(key)"
358
- >
359
- {{ getNodeNameByKey(key) }}
360
- </wd-tag>
361
- </view>
362
- </view>
363
-
364
- <!-- 树形选择区域 -->
365
- <loadingCom v-if="loading" />
366
- <view v-else class="tree-container">
367
- <tree-select
368
- :set-node-ref="setNodeRef"
369
- :tree-data="filteredTreeData"
370
- :select-mode="selectMode"
371
- :selected-keys="selectedKeys"
372
- @update:selected-keys="handleSelectChange"
373
- @toggle-expand="handleToggleExpand"
374
- />
375
-
376
- <!-- 空状态 -->
377
- <view v-if="filteredTreeData.length === 0" class="empty-state">
378
- <wd-icon name="search" size="48" color="#ccc" />
379
- <view class="empty-text">
380
- 没有找到匹配的节点
381
- </view>
382
- </view>
383
- </view>
384
- <view class="h-100px" />
385
- <!-- 底部按钮 -->
386
- <view class="fixed bottom-0 left-0 right-0 box-border w-100% flex p-2 bg-white dark:bg-[var(--wot-dark-background)]">
387
- <wd-button
388
- custom-class="flex-1"
389
- type="primary"
390
- @click="handleConfirm"
391
- >
392
- 确定
393
- </wd-button>
394
- </view>
395
- </view>
396
- </template>
397
-
398
- <style scoped>
399
- .mode-switch-container {
400
- padding: 16px;
401
- /* background-color: #fff; */
402
- margin-bottom: 10px;
403
- display: flex;
404
- align-items: center;
405
- }
406
-
407
- .mode-label {
408
- font-size: 14px;
409
- color: #666;
410
- margin-right: 12px;
411
- }
412
-
413
- .tree-container {
414
- /* background-color: #fff; */
415
- padding: 10px 0;
416
- min-height: 300px;
417
- /* max-height: calc(100vh - 220px); */
418
- }
419
-
420
- .empty-state {
421
- display: flex;
422
- flex-direction: column;
423
- align-items: center;
424
- justify-content: center;
425
- padding: 40px 0;
426
- color: #999;
427
- }
428
-
429
- .empty-text {
430
- margin-top: 16px;
431
- font-size: 14px;
432
- }
433
-
434
- .selected-result {
435
- /* background-color: #fff; */
436
- padding: 16px;
437
- }
438
-
439
- .result-title {
440
- text-align: left;
441
- font-size: 14px;
442
- color: #666;
443
- margin-bottom: 12px;
444
- }
445
-
446
- .result-tags {
447
- display: flex;
448
- flex-wrap: wrap;
449
- gap: 8px;
450
- }
451
-
452
- /* 节点高亮动画 */
453
- .node-highlight {
454
- animation: highlight 2s ease;
455
- }
456
- :deep(.wd-tag__close){
457
- line-height: 0.8;
458
- color:#fff;
459
- }
460
- :deep(.uni-input-input){
461
- text-align: left;
462
- }
463
- .splaceholderClass{
464
- text-align: left;
465
- }
466
-
467
- @keyframes highlight {
468
- 0% { background-color: rgba(64, 158, 255, 0.2); }
469
- 100% { background-color: transparent; }
470
- }
471
- </style>
1
+ <script setup lang="ts">
2
+ import { computed, defineEmits, defineOptions, defineProps, onMounted, ref, toRaw } from 'vue'
3
+ import { useManualTheme } from '../../../composables/useManualTheme'
4
+ import { treeConfigApi, treeDataApi, treeNodeDataApi } from '../../../api/tree'
5
+ import { pageConfig } from '../../../api/page'
6
+ import loadingCom from '../../loading/loading.vue'
7
+ import TreeSelect from './tree-select.vue'
8
+
9
+ defineOptions({
10
+ name: 'TreeSelectIndex',
11
+ })
12
+ const props = defineProps<{
13
+ sourceId: string
14
+ selectMode: SelectMode
15
+ }>()
16
+ const emit = defineEmits(['handleConfirm', 'handleClose'])
17
+ // 树形节点数据结构定义
18
+ interface TreeNode {
19
+ id: string
20
+ name: string
21
+ children?: TreeNode[]
22
+ [key: string]: any // 允许其他自定义属性
23
+ }
24
+
25
+ const { currentThemeColor } = useManualTheme()
26
+ // 选择模式类型
27
+ type SelectMode = 'single' | 'multiple'
28
+
29
+ // 示例树形数据
30
+ // const treeData: TreeNode[] = [
31
+ // {
32
+ // id: '1',
33
+ // name: '产品管理',
34
+ // children: [
35
+ // {
36
+ // id: '1-1',
37
+ // name: '产品列表',
38
+ // children: [
39
+ // { id: '1-1-1', name: '产品查询' },
40
+ // { id: '1-1-2', name: '产品新增' },
41
+ // { id: '1-1-3', name: '产品编辑' },
42
+ // { id: '1-1-4', name: '产品删除' },
43
+ // ],
44
+ // },
45
+ // {
46
+ // id: '1-2',
47
+ // name: '产品分类',
48
+ // children: [
49
+ // { id: '1-2-1', name: '分类管理' },
50
+ // { id: '1-2-2', name: '分类排序' },
51
+ // ],
52
+ // },
53
+ // ],
54
+ // },
55
+ // {
56
+ // id: '2',
57
+ // name: '订单管理',
58
+ // children: [
59
+ // { id: '2-1', name: '订单列表' },
60
+ // { id: '2-2', name: '订单详情' },
61
+ // { id: '2-3', name: '订单导出' },
62
+ // ],
63
+ // },
64
+ // {
65
+ // id: '3',
66
+ // name: '用户管理',
67
+ // children: [
68
+ // {
69
+ // id: '3-1',
70
+ // name: '用户列表',
71
+ // children: [
72
+ // { id: '3-1-1', name: '用户查询' },
73
+ // { id: '3-1-2', name: '用户编辑' },
74
+ // ],
75
+ // },
76
+ // { id: '3-2', name: '角色管理' },
77
+ // ],
78
+ // },
79
+ // { id: '4', name: '系统设置' },
80
+ // ]
81
+ const treeData = ref<TreeNode[]>([])
82
+
83
+ // 选择模式
84
+ const selectMode = ref<SelectMode>(props.selectMode)
85
+
86
+ // 选中的节点ID
87
+ const selectedKeys = ref<string[]>([])
88
+ // 选中的节点
89
+ const selectedNode = ref<TreeNode[]>([])
90
+
91
+ // 搜索关键词
92
+ const searchKeyword = ref('')
93
+
94
+ // 所有节点的引用,用于滚动定位
95
+ const nodeRefs = ref<Record<string, HTMLElement | null>>({})
96
+
97
+ // 处理选择模式变更
98
+ // function handleModeChange(mode: SelectMode) {
99
+ // // 切换模式时清空选择
100
+ // selectedKeys.value = []
101
+ // }
102
+ const treeConfigs = reactive({
103
+ branchRatmplId: '',
104
+ branchTitle: '',
105
+ id: '',
106
+ rootGtmplId: '',
107
+ primaryColumnId1: '',
108
+ primaryColumnId2: '',
109
+ leafRatmplId: '',
110
+ })
111
+
112
+ const loading = ref(false)
113
+ // 获取配置
114
+ async function treeConfig1() {
115
+ try {
116
+ const res = await treeConfigApi(props.sourceId)
117
+ treeConfigs.branchRatmplId = res.ttmplConfig.branchRatmplId
118
+ treeConfigs.branchTitle = res.ttmplConfig.branchTitle
119
+ treeConfigs.id = res.ttmplConfig.id
120
+ treeConfigs.rootGtmplId = res.ttmplConfig.rootGtmplId
121
+ treeConfigs.leafRatmplId = res.ttmplConfig.leafRatmplId
122
+ treeConfig2(treeConfigs.branchRatmplId)
123
+ }
124
+ catch (error) {
125
+ console.log('error:', error)
126
+ // TODO handle the exception
127
+ }
128
+ }
129
+
130
+ // 获取配置2
131
+ async function treeConfig2(branchRatmplId: string) {
132
+ try {
133
+ const res1 = await pageConfig(branchRatmplId)
134
+ treeConfigs.primaryColumnId1 = res1.ltmplConfig.primaryColumn.sourceId
135
+ getTreeTopData(treeConfigs.id)
136
+ }
137
+ catch (error) {
138
+ console.log('error:', error)
139
+ // TODO handle the exception
140
+ }
141
+ }
142
+
143
+ // 根据id找到树节点
144
+ async function findTreeNodeandAppend(id: string, arr: TreeNode[], appendData: any) {
145
+ for (let index = 0; index < arr.length; index++) {
146
+ const element = arr[index]
147
+ if (element.id === id) {
148
+ element.children = appendData.map((item: any) => {
149
+ return {
150
+ id: item.code,
151
+ name: item.fieldMap[treeConfigs.primaryColumnId1],
152
+ children: [],
153
+ }
154
+ })
155
+ }
156
+ else {
157
+ if (element.children)
158
+ findTreeNodeandAppend(id, element.children, appendData)
159
+ }
160
+ }
161
+ }
162
+
163
+ // 获取树顶层数据
164
+ async function getTreeTopData(id: string) {
165
+ try {
166
+ loading.value = true
167
+ const res = await treeDataApi(id)
168
+ treeData.value = res.entities.map((item: any) => {
169
+ return {
170
+ id: item.code,
171
+ name: item.fieldMap[treeConfigs.primaryColumnId1],
172
+ children: [],
173
+ }
174
+ })
175
+ }
176
+ catch (error) {
177
+ console.log('error:', error)
178
+ // TODO handle the exception
179
+ }
180
+ finally {
181
+ loading.value = false
182
+ }
183
+ }
184
+
185
+ // 获取树节点数据
186
+ async function getTreeNodeData(node: TreeNode) {
187
+ try {
188
+ const res = await treeNodeDataApi(treeConfigs.branchRatmplId, node.id)
189
+ if (!res.entities.length)
190
+ return
191
+ // 追加节点
192
+ findTreeNodeandAppend(node.id, treeData.value, res.entities)
193
+ }
194
+ catch (error) {
195
+ console.log('error:', error)
196
+ }
197
+ }
198
+
199
+ function handleToggleExpand(node: TreeNode) {
200
+ getTreeNodeData(node)
201
+ }
202
+
203
+ onMounted(() => {
204
+ treeConfig1()
205
+ })
206
+
207
+ // 处理选择变更
208
+ function handleSelectChange(keys: string[], node: TreeNode) {
209
+ selectedNode.value = [node]
210
+ selectedKeys.value = keys
211
+ }
212
+
213
+ // 处理标签关闭
214
+ function handleTagClose(key: string) {
215
+ selectedKeys.value = selectedKeys.value.filter(k => k !== key)
216
+ }
217
+
218
+ // 根据ID获取节点名称
219
+ function getNodeNameByKey(key: string): string {
220
+ const findNode = (nodes: TreeNode[]): TreeNode | null => {
221
+ for (const node of nodes) {
222
+ if (node.id === key) {
223
+ return node
224
+ }
225
+ if (node.children && node.children.length > 0) {
226
+ const found = findNode(node.children)
227
+ if (found)
228
+ return found
229
+ }
230
+ }
231
+ return null
232
+ }
233
+
234
+ const node = findNode(treeData.value)
235
+ return node ? node.name : ''
236
+ }
237
+
238
+ // 处理搜索
239
+ function handleSearch(keyword?: string) {
240
+ // 搜索逻辑在filteredTreeData计算属性中处理
241
+ if (keyword !== undefined) {
242
+ searchKeyword.value = keyword
243
+ }
244
+ }
245
+
246
+ // 过滤后的树形数据
247
+ const filteredTreeData = computed<TreeNode[]>(() => {
248
+ if (!searchKeyword.value) {
249
+ return treeData.value
250
+ }
251
+
252
+ const keyword = searchKeyword.value.toLowerCase()
253
+
254
+ // 过滤节点的递归函数
255
+ const filterNodes = (nodes: TreeNode[]): TreeNode[] => {
256
+ return nodes
257
+ .map((node) => {
258
+ // 复制节点,避免修改原数据
259
+ const newNode = { ...node }
260
+
261
+ // 如果有子节点,递归过滤
262
+ if (newNode.children && newNode.children.length > 0) {
263
+ newNode.children = filterNodes(newNode.children)
264
+ }
265
+
266
+ // 节点名称包含关键词,或者有符合条件的子节点,则保留该节点
267
+ const nodeNameMatch = newNode.name.toLowerCase().includes(keyword)
268
+ const hasMatchingChildren = newNode.children && newNode.children.length > 0
269
+
270
+ return nodeNameMatch || hasMatchingChildren ? newNode : null
271
+ })
272
+ .filter(Boolean) as TreeNode[]
273
+ }
274
+
275
+ return filterNodes(treeData.value)
276
+ })
277
+
278
+ // 记录节点引用
279
+ function setNodeRef(id: string, el: HTMLElement | null) {
280
+ if (el) {
281
+ nodeRefs.value[id] = el
282
+ }
283
+ }
284
+
285
+ // 滚动到指定节点
286
+ function scrollToNode(id: string) {
287
+ const node = nodeRefs.value[id]
288
+ if (node) {
289
+ node.scrollIntoView({ behavior: 'smooth', block: 'center' })
290
+ // 高亮效果
291
+ node.classList.add('node-highlight')
292
+ setTimeout(() => {
293
+ node.classList.remove('node-highlight')
294
+ }, 2000)
295
+ }
296
+ }
297
+
298
+ // 页面加载时展开有子节点的节点
299
+ onMounted(() => {
300
+ // 可以在这里添加默认展开逻辑
301
+ })
302
+
303
+ // 确定
304
+ function handleConfirm() {
305
+ emit('handleConfirm', toRaw(selectedKeys.value), toRaw(selectedNode.value), treeConfigs.leafRatmplId)
306
+ }
307
+
308
+ // 取消
309
+ function handleClose() {
310
+ emit('handleClose')
311
+ }
312
+ </script>
313
+
314
+ <template>
315
+ <view class="tree-select-page">
316
+ <!-- 选择模式切换 -->
317
+ <!-- <view class="mode-switch-container">
318
+ <view class="mode-label">
319
+ 选择模式:
320
+ </view>
321
+ <wd-radio-group
322
+ v-model="selectMode"
323
+ direction="horizontal"
324
+ @change="handleModeChange"
325
+ >
326
+ <wd-radio name="single" label="单选" />
327
+ <wd-radio name="multiple" label="多选" />
328
+ </wd-radio-group>
329
+ </view> -->
330
+
331
+ <!-- 搜索框 -->
332
+ <view class="search-container">
333
+ <wd-search
334
+ v-model="searchKeyword"
335
+ placeholder-class="splaceholderClass"
336
+ placeholder="搜索节点..."
337
+ clearable
338
+ @cancel="handleClose"
339
+ @input="handleSearch"
340
+ @clear="handleSearch"
341
+ />
342
+ </view>
343
+ <!-- 已选结果 -->
344
+ <view v-if="selectedKeys.length > 0" class="selected-result">
345
+ <view class="result-title">
346
+ 已选 {{ selectedKeys.length }} 项:
347
+ </view>
348
+ <view class="result-tags">
349
+ <wd-tag
350
+ v-for="key in selectedKeys"
351
+ :key="key"
352
+ :bg-color="currentThemeColor.primary"
353
+ color="#fff"
354
+ closable
355
+ round
356
+ @close="handleTagClose(key)"
357
+ @click="scrollToNode(key)"
358
+ >
359
+ {{ getNodeNameByKey(key) }}
360
+ </wd-tag>
361
+ </view>
362
+ </view>
363
+
364
+ <!-- 树形选择区域 -->
365
+ <loadingCom v-if="loading" />
366
+ <view v-else class="tree-container">
367
+ <tree-select
368
+ :set-node-ref="setNodeRef"
369
+ :tree-data="filteredTreeData"
370
+ :select-mode="selectMode"
371
+ :selected-keys="selectedKeys"
372
+ @update:selected-keys="handleSelectChange"
373
+ @toggle-expand="handleToggleExpand"
374
+ />
375
+
376
+ <!-- 空状态 -->
377
+ <view v-if="filteredTreeData.length === 0" class="empty-state">
378
+ <wd-icon name="search" size="48" color="#ccc" />
379
+ <view class="empty-text">
380
+ 没有找到匹配的节点
381
+ </view>
382
+ </view>
383
+ </view>
384
+ <view class="h-100px" />
385
+ <!-- 底部按钮 -->
386
+ <view class="fixed bottom-0 left-0 right-0 box-border w-100% flex p-2 bg-white dark:bg-[var(--wot-dark-background)]">
387
+ <wd-button
388
+ custom-class="flex-1"
389
+ type="primary"
390
+ @click="handleConfirm"
391
+ >
392
+ 确定
393
+ </wd-button>
394
+ </view>
395
+ </view>
396
+ </template>
397
+
398
+ <style scoped>
399
+ .mode-switch-container {
400
+ padding: 16px;
401
+ /* background-color: #fff; */
402
+ margin-bottom: 10px;
403
+ display: flex;
404
+ align-items: center;
405
+ }
406
+
407
+ .mode-label {
408
+ font-size: 14px;
409
+ color: #666;
410
+ margin-right: 12px;
411
+ }
412
+
413
+ .tree-container {
414
+ /* background-color: #fff; */
415
+ padding: 10px 0;
416
+ min-height: 300px;
417
+ /* max-height: calc(100vh - 220px); */
418
+ }
419
+
420
+ .empty-state {
421
+ display: flex;
422
+ flex-direction: column;
423
+ align-items: center;
424
+ justify-content: center;
425
+ padding: 40px 0;
426
+ color: #999;
427
+ }
428
+
429
+ .empty-text {
430
+ margin-top: 16px;
431
+ font-size: 14px;
432
+ }
433
+
434
+ .selected-result {
435
+ /* background-color: #fff; */
436
+ padding: 16px;
437
+ }
438
+
439
+ .result-title {
440
+ text-align: left;
441
+ font-size: 14px;
442
+ color: #666;
443
+ margin-bottom: 12px;
444
+ }
445
+
446
+ .result-tags {
447
+ display: flex;
448
+ flex-wrap: wrap;
449
+ gap: 8px;
450
+ }
451
+
452
+ /* 节点高亮动画 */
453
+ .node-highlight {
454
+ animation: highlight 2s ease;
455
+ }
456
+ :deep(.wd-tag__close){
457
+ line-height: 0.8;
458
+ color:#fff;
459
+ }
460
+ :deep(.uni-input-input){
461
+ text-align: left;
462
+ }
463
+ .splaceholderClass{
464
+ text-align: left;
465
+ }
466
+
467
+ @keyframes highlight {
468
+ 0% { background-color: rgba(64, 158, 255, 0.2); }
469
+ 100% { background-color: transparent; }
470
+ }
471
+ </style>