cnhis-design-vue 3.0.5 → 3.0.8

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.
@@ -0,0 +1,1104 @@
1
+ <template>
2
+ <div class="personnel">
3
+ <n-spin :show="state.spinning">
4
+ <div class="people-tree">
5
+ <!-- 树主体与检索 -->
6
+ <div class="people-tree-item people-tree-left">
7
+ <n-input-group style="margin-bottom: 12px">
8
+ <n-input style="text-align: left;" clearable v-model:value="state.title" :placeholder="searchPlaceholder" />
9
+ <n-button type="primary" @click="searchClick">{{ searchButtonText }}</n-button>
10
+ </n-input-group>
11
+ <n-checkbox v-if="searchTreeVisable" v-model:checked="state.searchChecked" :indeterminate="state.searchIndeterminate" @update:checked="radioChange">全选</n-checkbox>
12
+ <n-checkbox v-else v-model:checked="state.singleChecked" :indeterminate="state.searchIndeterminate" @update:checked="radioChange">全选</n-checkbox>
13
+
14
+ <!-- <div v-show="state.search.status == 3 || (state.search.status == 0 && state.treeData.main && state.treeData.main.length == 0)" class="no-data-tips">
15
+ 查无结果
16
+ </div> -->
17
+ <!-- main tree -->
18
+ <n-tree
19
+ class="main-tree-container"
20
+ v-show="state.search.status == 0"
21
+ v-model:checked-keys="state.checkedKeys.main"
22
+ :expanded-keys="state.expandedKeys"
23
+ checkable
24
+ cascade
25
+ allow-checking-not-loaded
26
+ :selectable="false"
27
+ :data="state.treeData.main"
28
+ :check-strategy="queryMainTreeData ? 'all' : 'child'"
29
+ @update:expanded-keys="onExpand"
30
+ :on-load="onLoadData"
31
+ :render-label="renderLabel"
32
+ ></n-tree>
33
+ <!-- search tree -->
34
+
35
+ <search-tree
36
+ v-bind="$attrs"
37
+ ref="searchTree"
38
+ :visible="searchTreeVisable"
39
+ :formatTreeData="state.treeData.search"
40
+ :searchValue="state.searchValue"
41
+ :allCheckedKeys="state.checkedKeys"
42
+ :defaultExpandedKeys="state.search.defaultExpandedKeys"
43
+ :dataListKeys="state.search.dataListKeys"
44
+ :searchNoMore="state.searchNoMore"
45
+ :loadMorenLoadinng="state.loadMorenLoadinng"
46
+ @addCheckedkeysMain="addCheckedkeysMain"
47
+ @removeCheckedkeysMain="removeCheckedkeysMain"
48
+ @addCheckedkeysOther="addCheckedkeysOther"
49
+ @removeCheckedkeysOther="removeCheckedkeysOther"
50
+ @searchOnloadMore="searchOnloadMore"
51
+ ></search-tree>
52
+ </div>
53
+ <!-- 树已选的数据 -->
54
+ <div class="people-tree-item tags-box">
55
+ <slot v-if="state.defaultShowList && state.defaultShowList.length">
56
+ <div class="tag-item def-item" v-for="(tag, i) in state.defaultShowList" :key="i">
57
+ {{ tag.title || tag[wordbookChild.user_name_obj] }}
58
+ <n-icon v-if="!isDetail" class="tag-close" :component="CloseCircleSharp" @click="closeDefaultTag(tag, i)"></n-icon>
59
+ </div>
60
+ </slot>
61
+
62
+ <div
63
+ class="tag-item"
64
+ :class="{ isRoot: !tag.isLeaf }"
65
+ v-for="tag in checkedTreeItem"
66
+ :key="tag.key"
67
+ >
68
+ {{ tag.title || tag.key }} {{ treeCount(tag.count) }}
69
+ <n-icon v-if="!isDetail" class="tag-close" :component="CloseCircleSharp" @click="closeTag(tag)"></n-icon>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </n-spin>
74
+ </div>
75
+ </template>
76
+
77
+ <script lang="tsx">
78
+ import create from '@/core/create.js';
79
+ export default create({
80
+ name: "SelectPerson"
81
+ })
82
+ </script>
83
+ <script lang="tsx" setup>
84
+ import { reactive, onMounted, computed, ref, nextTick, watch } from "vue"
85
+ import { NButton, NInput, NSpin, NInputGroup, NCheckbox, NTree, NTooltip, NIcon, useMessage } from "naive-ui"
86
+ import { CloseCircleSharp } from '@vicons/ionicons5'
87
+ import type { TreeOption } from 'naive-ui'
88
+ import vexutils from '@/utils/vexutils.js';
89
+ import SearchTree from './search-tree.vue';
90
+ import { filterTree } from './utils/index.js'
91
+
92
+ (window as any).$message = useMessage()
93
+
94
+ const FIXEDKEY = '8982eeb4-0439-4100-9805-8cdd12b2256c-front-end-fixed-key';
95
+ const props = withDefaults(defineProps<{
96
+ visible?: boolean
97
+ isDetail?: boolean
98
+ defaultList?: any[]
99
+ data?: any[]
100
+ searchPlaceholder?: string
101
+ searchButtonText?: string
102
+ wordbook: any
103
+ wordbookChild?: any
104
+ SearchLoadMore?: boolean
105
+ queryMainTreeData?: Function
106
+ queryLoadChildData?: Function
107
+ queryTreeSearch?: Function
108
+ }>(), {
109
+ visible: false,
110
+ isDetail: false,
111
+ defaultList: () => ([]),
112
+ data: () => ([]),
113
+ searchPlaceholder: '输入关键字搜索',
114
+ searchButtonText: '搜索',
115
+ wordbook: () => ({}),
116
+ wordbookChild: () => ({}),
117
+ SearchLoadMore: false
118
+ })
119
+ const searchTree: any = ref(null)
120
+ const state = reactive({
121
+ treeData: {
122
+ main: [] as any[],
123
+ search: [] as any[]
124
+ },
125
+ // 展开的keys
126
+ expandedKeys: [] as any[],
127
+ autoExpandParent: true,
128
+ // 扁平化tree的options Object{Array[Object]}
129
+ dataList: {
130
+ main: [],
131
+ other: []
132
+ },
133
+ // 当前tree选中的keys Object{Array[Number]}
134
+ checkedKeys: {
135
+ main: [] as any[],
136
+ other: [] as any[]
137
+ },
138
+ search: {
139
+ defaultExpandedKeys: [] as any[],
140
+ // 0: 未查询,1:查询中,2:有查询结果,3无查询结果
141
+ status: 0,
142
+ dataListKeys: {
143
+ main: [] as any[],
144
+ other: [] as any[]
145
+ },
146
+ records: 0
147
+ },
148
+ searchValue: "",
149
+ searchPage: 1,
150
+ searchNoMore: false,
151
+ title: "",
152
+ // 选中的所有叶节点(Leaf)keys <Array[Number]>,最终提交接口所需格式
153
+ checkedLeafKeys: [] as any[],
154
+ spinning: false,
155
+ loadMorenLoadinng: false,
156
+
157
+ errorMsg: "",
158
+
159
+ defaultShowList: [] as any[], // 默认选中项
160
+ checkedKeysMain: [],
161
+
162
+ cacheParentIdObj: {},
163
+ singleChecked: false,
164
+ indeterminate: false,
165
+
166
+ searchIndeterminate: false,
167
+ searchChecked: false
168
+ })
169
+
170
+ const emit = defineEmits(['check', 'pubCheckedTreeItem', 'handleOk', 'change', 'submitData', 'update:visible'])
171
+
172
+ const searchTreeVisable = computed(() => state.search.status == 2)
173
+ const dataListMainKeys = computed(() => {
174
+ if (!state.dataList.main.length) return []
175
+ return state.dataList.main.map((i: any) => i.key)
176
+ })
177
+ const dataListOtherKeys = computed(() => {
178
+ if (!state.dataList.other.length) return []
179
+ return state.dataList.other.map((i: any) => i.key)
180
+ })
181
+ // 右侧展示tabs <Array[Object]>
182
+ const checkedTreeItem = computed(() => {
183
+ let otherItems = state.dataList.other.filter((item: any) => {
184
+ return state.checkedKeys.other.includes(item.key);
185
+ });
186
+ let mainItems = state.dataList.main.filter(item => {
187
+ let { isLeaf, key } = item;
188
+ let parentId = getParentId(item);
189
+ let showKeys = [...state.checkedKeys.main, ...otherItems];
190
+ let checked = showKeys.includes(key);
191
+ // 父级全选,子级不展示
192
+ /** String(parentId) 类型不相等 */
193
+ if (isLeaf && parentId && state.checkedKeys.main.some(v => v == parentId)) {
194
+ checked = false;
195
+ }
196
+ return checked;
197
+ });
198
+ let list = uniqArrObj([...mainItems, ...otherItems], "key");
199
+ return list;
200
+ })
201
+ const wordbookConfig = computed(() => {
202
+ const { parent_id_obj, parent_name_obj } = props.wordbook || {};
203
+ const { user_id_obj, user_name_obj } = props.wordbookChild || {};
204
+ return {
205
+ parent_id_obj,
206
+ parent_name_obj,
207
+ user_id_obj,
208
+ user_name_obj
209
+ };
210
+ })
211
+ const getLoadChildData = computed(() => {
212
+ if (typeof props.queryLoadChildData == 'function') {
213
+ return onLoadData;
214
+ }
215
+ return null
216
+ })
217
+
218
+ const renderLabel = ({option}: {option: TreeOption}) => {
219
+ if (option.isLeaf || !option.children) {
220
+ return (
221
+ <NTooltip trigger="hover">
222
+ {{
223
+ default: () => `${option.title} ${option.sub_title || ''}`,
224
+ trigger: () => (
225
+ <span>
226
+ <span class="c-title">{ option.title || option.key }&thinsp; </span>
227
+ <span class="c-sub-title">{ option.sub_title }</span>
228
+ </span>
229
+ )
230
+ }}
231
+ </NTooltip>
232
+ )
233
+ } else {
234
+ return (
235
+ <span class="p-title">
236
+ <span>{ option.title || option.key }</span>
237
+ { treeCount(option.count) }
238
+ </span>
239
+ )
240
+ }
241
+ }
242
+ const initDefault = () => {
243
+ if (getLoadChildData.value) {
244
+ getDefaultList();
245
+ } else {
246
+ nextTick(() => {
247
+ state.checkedKeys.main = [...props.defaultList];
248
+ });
249
+ }
250
+ }
251
+ /** --- 默认值 --- */
252
+ const getDefaultList = () => {
253
+ let copy = JSON.parse(JSON.stringify(props.defaultList));
254
+ if (Array.isArray(copy)) {
255
+ // this.defaultShowObj = {};
256
+
257
+ // const { user_id_obj } = props.wordbookChild || {};
258
+ copy.forEach(v => {
259
+ handleDefMapping(v);
260
+ let key = getuniqKey(v);
261
+ let pId = getParentId(v);
262
+ state.cacheParentIdObj[pId] = pId;
263
+ // this.defaultShowObj[key] = v[user_id_obj];
264
+ });
265
+ state.defaultShowList = copy;
266
+ }
267
+ }
268
+ /**
269
+ * 根据配置映射默认值
270
+ */
271
+ const handleDefMapping = (v: any) => {
272
+ const { parent_id_obj, parent_name_obj, user_id_obj, user_name_obj } = wordbookConfig.value || {};
273
+ // 自定义 key 值
274
+ const { parent_id_obj_key, parent_name_obj_key } = props.wordbook;
275
+ const { user_id_obj_key, user_name_obj_key } = props.wordbookChild;
276
+ v[parent_id_obj] = v[parent_id_obj_key] || v.parent_id;
277
+ v[parent_name_obj] = v[parent_name_obj_key] || v.parent_name;
278
+ v[user_id_obj] = v[user_id_obj_key] || v.user_id;
279
+ v[user_name_obj] = v[user_name_obj_key] || v.user_name;
280
+ }
281
+ const staticDataInit = () => {
282
+ if (!Array.isArray(props.data) || props.data.length == 0) return;
283
+ state.treeData.main = JSON.parse(JSON.stringify(props.data));
284
+ generateDataList(state.treeData.main, state.dataList.main);
285
+ }
286
+ const getcheckedKeys = () => {
287
+ let checkedKeysMain = [] as any[],
288
+ checkedKeysOther = [] as any[];
289
+ let copy = JSON.parse(JSON.stringify(props.defaultList));
290
+ if (Array.isArray(copy)) {
291
+ copy.forEach(v => {
292
+ handleDefMapping(v);
293
+ let key = getuniqKey(v);
294
+ checkedKeysMain.push(key);
295
+ // 回填到 检索的选择,防止回填马上检索
296
+ if (String(key).includes(FIXEDKEY)) {
297
+ checkedKeysOther.push(key);
298
+ }
299
+ });
300
+ }
301
+ return {
302
+ checkedKeysMain,
303
+ checkedKeysOther
304
+ };
305
+ }
306
+ /**
307
+ * 初始化 请求已选的数据
308
+ */
309
+ const loadSelected = async () => {
310
+ try {
311
+ let list = await Promise.all(
312
+ state.treeData.main.map(async v => {
313
+ const { parent_id_obj } = props.wordbook || {};
314
+ if ([v.key, v[parent_id_obj], String(v[parent_id_obj])].includes(state.cacheParentIdObj[v[parent_id_obj]])) {
315
+ await onLoadData(v);
316
+ return v;
317
+ }
318
+ })
319
+ );
320
+ return list;
321
+ } catch (error) {
322
+ return Promise.resolve();
323
+ }
324
+ }
325
+ // 动态获取子节点
326
+ const onLoadData = (option: TreeOption ) => {
327
+ return new Promise<void>(async (resolve: Function) => {
328
+ if (option.children) {
329
+ resolve();
330
+ return;
331
+ }
332
+ option.children = await handleLoadChildData(option.key, option);
333
+
334
+ hanldeFilterDefaultShowList(option.children);
335
+
336
+ nextTick(() => {
337
+ state.treeData.main = [...state.treeData.main];
338
+ });
339
+
340
+ addCheckedKeysChildrenMain(option);
341
+
342
+ generateDataList(option.children || [], state.dataList.main);
343
+ resolve();
344
+ });
345
+ }
346
+ // 查询树结构数据(一级父类)
347
+ const getUsers = async () => {
348
+ try {
349
+ if (typeof props.queryMainTreeData !== 'function') {
350
+ return;
351
+ }
352
+ state.spinning = true;
353
+ const { parent_id_obj } = wordbookConfig.value || {};
354
+ let rows = await props.queryMainTreeData();
355
+ let list = uniqArrObj(rows || [], parent_id_obj);
356
+ state.treeData.main = list;
357
+ generateDataList(state.treeData.main, state.dataList.main);
358
+ // TODO:这里的回填
359
+ await loadSelected();
360
+ let { checkedKeysMain, checkedKeysOther } = getcheckedKeys();
361
+ state.checkedKeys.main.push(...checkedKeysMain);
362
+ state.checkedKeys.other = [...checkedKeysOther];
363
+
364
+ state.treeData.main.forEach((f: any) => {
365
+ if (f && f.children && f.children.length) {
366
+ let flag = f.children.every((c: any) => checkedKeysMain.includes(c.key));
367
+ if (flag && f.key) {
368
+ state.checkedKeys.main.push(f.key);
369
+ }
370
+ }
371
+ });
372
+ state.spinning = false;
373
+ } catch (error) {
374
+ state.spinning = false;
375
+ (window as any).$message.error('查询异常');
376
+ }
377
+ }
378
+ const loadMainChildData = (searchResultTreeData: any[]) => {
379
+ return new Promise(async (resolve: Function) => {
380
+ let promises = searchResultTreeData.map(async item => {
381
+ if (item.users) {
382
+ let mainTreeItem = state.treeData.main.find(mainItem => mainItem.key == item.key);
383
+ if (!mainTreeItem || (mainTreeItem && !mainTreeItem.children)) {
384
+ if (!mainTreeItem) {
385
+ mainTreeItem = { ...item };
386
+ }
387
+ mainTreeItem.children = await handleLoadChildData(item.key, mainTreeItem);
388
+ generateDataList(mainTreeItem.children, state.dataList.main);
389
+ addCheckedKeysChildrenMain(mainTreeItem);
390
+ }
391
+ }
392
+ });
393
+
394
+ await Promise.all(promises);
395
+
396
+ resolve();
397
+ });
398
+ }
399
+ const handleLoadChildData = async (key: string | number | undefined, option: TreeOption) => {
400
+ let rows = props.queryLoadChildData && (await props.queryLoadChildData(key, option));
401
+ let list = rows;
402
+ return list;
403
+ }
404
+ // 暂存实际可选节点arr
405
+ const generateDataList = (data: TreeOption[], dataList: any) => {
406
+ for (let i = 0; i < data.length; i++) {
407
+ const node = data[i];
408
+ dataList.push({
409
+ parentId: getParentId(node),
410
+ ...node
411
+ });
412
+ if (node.children) {
413
+ generateDataList(node.children, dataList);
414
+ }
415
+ }
416
+ }
417
+ const addCheckedKeysChildrenMain = (item: any) => {
418
+ if (!item.key) return;
419
+ if (!state.checkedKeys.main.includes(String(item.key))) return false;
420
+ // 新查询到插入已选中的main treeData 的child key,需要手动push到checkedKeys中
421
+ if (!Array.isArray(item.children)) return false;
422
+ item.children.forEach(({ key }: { key: string | number }) => {
423
+ // 防止重复添加(已处理,不会重复添加,保留代码以防逻辑缺漏)
424
+ if (state.checkedKeys.main.includes(key) || !key) return false;
425
+ state.checkedKeys.main.push(key);
426
+ });
427
+ }
428
+ /**
429
+ * 过滤默认值的右边展示
430
+ */
431
+ const hanldeFilterDefaultShowList = (list: any) => {
432
+ state.defaultShowList = state.defaultShowList.filter(d => list.findIndex((l: any) => getuniqKey(d) == getuniqKey(l)) == -1);
433
+ // this.updateDefaultShowObj(this.defaultShowList);
434
+ }
435
+ /**
436
+ * 检索
437
+ */
438
+ const searchFetch = async (data = {}, config = {} as any) => {
439
+ if (typeof props.queryTreeSearch !== 'function') {
440
+ staticDataSearch();
441
+ state.spinning = false;
442
+ isSearchAllCheck();
443
+ return;
444
+ }
445
+ if (!state.searchValue) {
446
+ state.search.status = 0;
447
+ state.search.defaultExpandedKeys = [];
448
+ state.expandedKeys = [];
449
+ state.spinning = false;
450
+ state.searchNoMore = false;
451
+ return false;
452
+ }
453
+ if (!config.loadMoren) {
454
+ state.search.status = 1;
455
+ } else {
456
+ state.loadMorenLoadinng = true;
457
+ }
458
+
459
+ let rows = await props.queryTreeSearch(state.searchValue, data);
460
+ let searchResultTreeData = rows;
461
+ state.loadMorenLoadinng = false;
462
+ if (!searchResultTreeData || !searchResultTreeData.length) {
463
+ if (state.searchPage == 1) {
464
+ state.search.defaultExpandedKeys = [];
465
+ state.search.status = 3;
466
+ } else {
467
+ state.search.status = 2;
468
+ state.searchNoMore = true;
469
+ }
470
+
471
+ state.spinning = false;
472
+ isSearchAllCheck();
473
+ return false;
474
+ }
475
+
476
+ const { parent_id_obj, parent_name_obj, user_count_obj } = props.wordbook || {};
477
+ const { user_name_obj } = props.wordbookChild || {};
478
+ let pObj = {};
479
+ searchResultTreeData.forEach((item: any) => {
480
+ item[parent_id_obj] &&
481
+ (pObj[item[parent_id_obj]] = {
482
+ key: item[parent_id_obj],
483
+ users: []
484
+ });
485
+
486
+ if (item.users) {
487
+ insertSearchTreeData(item);
488
+ } else {
489
+ insertSearchTreeDataOther(item);
490
+ }
491
+ });
492
+ let pList = [] as any[];
493
+ Object.keys(pObj || {}).forEach(v => {
494
+ if (v) {
495
+ pList.push({ ...pObj[v] });
496
+ }
497
+ });
498
+ let len = state.treeData.search.length || 0;
499
+ // TODO:加载更多
500
+ if (props.SearchLoadMore) {
501
+ state.searchNoMore = false;
502
+ } else {
503
+ state.searchNoMore = true;
504
+ }
505
+ // 防止searchTree选中后切换回mainTree,mainTree checkedKeys状态丢失
506
+ await loadMainChildData(pList);
507
+ generateSearchDataListKeys(searchResultTreeData);
508
+
509
+ hanldeFilterDefaultShowList(searchResultTreeData);
510
+ refreshSearchTree();
511
+ state.search.status = 2;
512
+ state.spinning = false;
513
+
514
+ isSearchAllCheck();
515
+ }
516
+ const refreshSearchTree = () => {
517
+ if (!searchTreeVisable.value) return false;
518
+ searchTree.value.setDefaultCheckedKeys();
519
+ nextTick(() => {
520
+ isSearchAllCheck();
521
+ });
522
+ }
523
+ const generateSearchDataListKeys = (searchResultTreeData: any[]) => {
524
+ searchResultTreeData.forEach((item: any) => {
525
+ if (!dataListMainKeys.value.includes(item.key)) {
526
+ state.search.dataListKeys.other.push(item.key);
527
+ item.children &&
528
+ item.children.forEach((childItem: any) => {
529
+ state.search.dataListKeys.other.push(childItem.key);
530
+ });
531
+ return;
532
+ }
533
+
534
+ state.search.dataListKeys.main.push(item.key);
535
+ item.children &&
536
+ item.children.forEach((childItem: any) => {
537
+ state.search.dataListKeys.main.push(childItem.key);
538
+ });
539
+ });
540
+ }
541
+ const insertSearchTreeData = (item: any) => {
542
+ const { user_count_obj } = props.wordbook || {};
543
+ state.search.defaultExpandedKeys.push(item.key);
544
+
545
+ // 默认值处理
546
+ hanldeFilterDefaultShowList(item.children || []);
547
+
548
+ if (item[user_count_obj] == item.children.length) {
549
+ item.disableCheckbox = false;
550
+ }
551
+ state.treeData.search.push(item);
552
+ }
553
+ const insertSearchTreeDataOther = (item: any) => {
554
+ let formatItem = Object.assign({}, item, { isLeaf: true });
555
+
556
+ state.treeData.search.push(formatItem);
557
+
558
+ if (dataListOtherKeys.value.includes(formatItem.key)) return false;
559
+ generateDataList([formatItem], state.dataList.other);
560
+ }
561
+ const isSearchAllCheck = () => {
562
+ nextTick(() => {
563
+ let skeyArr = state.treeData.search.map((item: any) => item.key);
564
+ let serchCheckedKeys = searchTree.value.checkedKeys;
565
+ state.searchIndeterminate = !!serchCheckedKeys.length && serchCheckedKeys.length < skeyArr.length;
566
+ state.searchChecked = serchCheckedKeys.length === skeyArr.length;
567
+ });
568
+ }
569
+ const staticDataSearch = () => {
570
+ // let expandedKeys = new Set();
571
+ // getStaticSearchParentKey(state.treeData.main, expandedKeys);
572
+ // state.expandedKeys = Array.from(expandedKeys);
573
+ // state.autoExpandParent = true;
574
+
575
+ if (!state.title) {
576
+ state.treeData.main = JSON.parse(JSON.stringify(props.data))
577
+ return
578
+ }
579
+ let copyData = JSON.parse(JSON.stringify(state.treeData.main))
580
+ filterTree({ value: state.title, data: copyData, prop: 'title' })
581
+ const expandedKeys: any[] = [];
582
+ vexutils.searchTree(copyData, (item: any) => {
583
+ if (item.visible) {
584
+ expandedKeys.push(item.key);
585
+ }
586
+ if (item.children?.length) {
587
+ item.children = item.children.filter((child: any) => child.visible);
588
+ }
589
+ });
590
+ state.treeData.main = copyData;
591
+ state.expandedKeys = expandedKeys;
592
+ }
593
+ // const getStaticSearchParentKey = (treeData: any[], expandedKeys: any, orgId?: string) => {
594
+ // const keyword = state.title;
595
+ // for (let i = 0; i < treeData.length; i++) {
596
+ // const user = treeData[i];
597
+ // let { title = "", key, children } = user;
598
+ // if (
599
+ // keyword.length > 0 &&
600
+ // (String(title).indexOf(keyword) !== -1 || String(key).indexOf(keyword) !== -1)
601
+ // ) {
602
+ // expandedKeys.add(key);
603
+ // if (orgId) {
604
+ // expandedKeys.add(orgId);
605
+ // }
606
+ // break;
607
+ // }
608
+ // if (children) {
609
+ // getStaticSearchParentKey(children, expandedKeys, user.key || orgId);
610
+ // }
611
+ // }
612
+ // }
613
+ // 展开数节点
614
+ const onExpand = (expandedKeys: Array<string | number>) => {
615
+ // const latestOpenKey = expandedKeys.find(key => this.expandedKeys.indexOf(key) == -1);
616
+ // this.expandedKeys = latestOpenKey ? [latestOpenKey] : [];
617
+ // this.autoExpandParent = false;
618
+ state.expandedKeys = expandedKeys;
619
+ state.autoExpandParent = false;
620
+ // this.$emit('expand', expandedKeys, ...arg)
621
+ }
622
+ const searchClick = () => {
623
+ state.searchValue = state.title;
624
+
625
+ state.treeData.search = [];
626
+ state.search.dataListKeys.other = [];
627
+ state.search.dataListKeys.main = [];
628
+ state.search.defaultExpandedKeys = [];
629
+
630
+ state.spinning = true;
631
+ state.searchPage = 1;
632
+ searchFetch();
633
+ }
634
+ const isAllCheck = (checkedKeys = [] as Array<string | number>) => {
635
+ if (!checkedKeys || checkedKeys.length == 0) {
636
+ state.indeterminate = false;
637
+ state.singleChecked = false;
638
+ } else {
639
+ let keyArr = state.treeData.main.map((item: any) => item.key);
640
+ let keyArrTemp = keyArr.filter((item: string | number) => checkedKeys.indexOf(item) !== -1);
641
+ state.indeterminate = !(keyArr.length == keyArrTemp.length);
642
+ state.singleChecked = keyArr.length == keyArrTemp.length;
643
+ }
644
+ }
645
+ /* 子组件emit方法 */
646
+ const addCheckedkeysMain = (keys: Array<string | number>) => {
647
+ let checkedKeys = state.checkedKeys.main;
648
+ let filterKey = (keys || {}).filter(Boolean);
649
+ filterKey.forEach(key => {
650
+ //
651
+ checkedKeys.push(key);
652
+ // 如果子级全选,父级也需要选中
653
+ let _getParentKey = getParentKey(key, state.treeData.main);
654
+ if (!_getParentKey) return;
655
+ let parent = state.treeData.main.filter(item => {
656
+ return item.key == _getParentKey;
657
+ });
658
+ let allCheck = (parent[0]?.children || []).every((item: any) => {
659
+ return checkedKeys.includes(item.key);
660
+ });
661
+ allCheck && checkedKeys.push(_getParentKey);
662
+ });
663
+ }
664
+ // 获取匹配搜索节点arr
665
+ const getParentKey = (key: string | number, tree: any[]): string | number => {
666
+ let parentKey;
667
+ for (let i = 0; i < tree.length; i++) {
668
+ const node = tree[i];
669
+ if (node.key == key) {
670
+ parentKey = node.key;
671
+ return parentKey;
672
+ }
673
+
674
+ if (node.children) {
675
+ if (node.children.some((item: any) => item.key == key)) {
676
+ parentKey = node.key;
677
+ } else if (getParentKey(key, node.children)) {
678
+ parentKey = getParentKey(key, node.children);
679
+ }
680
+ }
681
+ }
682
+ return parentKey;
683
+ }
684
+ const radioChange = (checked: boolean) => {
685
+ if (searchTreeVisable.value) {
686
+ state.singleChecked = checked;
687
+ } else {
688
+ state.searchChecked = checked;
689
+ }
690
+ checkAll(checked ? 1 : 2);
691
+ // handleOk()
692
+ }
693
+ // type 1 全选 2 反选(清空)
694
+ const checkAll = (type: string | number) => {
695
+ if (checkAllSearchTree(type)) return false;
696
+ if (type == 1) {
697
+ state.expandedKeys = [];
698
+ state.checkedKeys.main = dataListMainKeys.value;
699
+ } else if (type == 2) {
700
+ state.checkedKeys.main = [];
701
+ state.autoExpandParent = true;
702
+ }
703
+ }
704
+ const checkAllSearchTree = (type: string | number) => {
705
+ if (!searchTreeVisable.value) return false;
706
+
707
+ if (type == 1) {
708
+ // 全选默认不选中父级
709
+ let allSearchkeysMain = state.search.dataListKeys.main.filter(key => {
710
+ return !state.search.defaultExpandedKeys.includes(key);
711
+ });
712
+
713
+ let allSearchkeysOther = state.search.dataListKeys.other;
714
+
715
+ state.checkedKeys.main = vexutils.uniq([...state.checkedKeys.main, ...allSearchkeysMain]);
716
+ state.checkedKeys.other = vexutils.uniq([...state.checkedKeys.other, ...allSearchkeysOther]);
717
+
718
+ // 如果子级全选,父级也需要选中
719
+ state.search.dataListKeys.main.forEach(key => {
720
+ let parentKey = getParentKey(key, state.treeData.main);
721
+ let parent = state.treeData.main.filter(item => {
722
+ return item.key == parentKey;
723
+ });
724
+
725
+ let allCheck = parent[0].children.every((item: any) => {
726
+ return state.checkedKeys.main.includes(item.key);
727
+ });
728
+ allCheck && state.checkedKeys.main.push(parentKey);
729
+ });
730
+ refreshSearchTree();
731
+ } else if (type == 2) {
732
+ searchTree.value.emptyCheckedKeys();
733
+ state.autoExpandParent = true;
734
+ }
735
+ isSearchAllCheck();
736
+ return true;
737
+ }
738
+ const removeCheckedkeysMain = (keys: Array<string | number>) => {
739
+ let checkedKeysMain = vexutils.clone(state.checkedKeys.main, true);
740
+ keys.forEach(key => {
741
+ //
742
+ let index = checkedKeysMain.findIndex((mainKey: string | number) => mainKey == key);
743
+ if (index !== -1) {
744
+ checkedKeysMain.splice(index, 1);
745
+ state.checkedKeys.main = [...checkedKeysMain];
746
+ }
747
+ // 删除父级选中
748
+ let parentKey = getParentKey(key, state.treeData.main);
749
+ let parentIndex = checkedKeysMain.findIndex((mainKey: any) => mainKey == parentKey);
750
+ if (parentIndex !== -1) {
751
+ checkedKeysMain.splice(parentIndex, 1);
752
+ }
753
+ });
754
+
755
+ state.checkedKeys.main = [...checkedKeysMain];
756
+ }
757
+ const addCheckedkeysOther = (keys: any) => {
758
+ state.checkedKeys.other.push(...keys);
759
+ }
760
+ const removeCheckedkeysOther = (keys: any[]) => {
761
+ let checkedKeys = state.checkedKeys.other;
762
+ // let mainCheckedKeys = this.checkedKeys.main;
763
+ keys.forEach((key: any) => {
764
+ let index = checkedKeys.findIndex((otherKey: any) => otherKey == key);
765
+ checkedKeys.splice(index, 1);
766
+ handleDeldefaultShowList(key);
767
+ });
768
+ removeCheckedkeysMain(keys);
769
+ }
770
+ const handleDeldefaultShowList = (key: string) => {
771
+ let len = state?.defaultShowList?.length || 0;
772
+ if (len > 0) {
773
+ for (let i = len - 1; i >= 0; i--) {
774
+ let d = state.defaultShowList[i];
775
+ if (`${d.parent_id}_${d.user_id}` == key) {
776
+ closeDefaultTag(d, i);
777
+ }
778
+ }
779
+ }
780
+ }
781
+ const closeDefaultTag = (tag: any, i: number) => {
782
+ state.defaultShowList.splice(i, 1);
783
+ closeTag({ parentId: getParentId(tag), ...tag });
784
+ }
785
+ /* 右侧点击item[x]按钮 相关 */
786
+ const closeTag = (removedTag: any) => {
787
+ // let { parentId } = removedTag;
788
+
789
+ removedCheckedkeysMain(removedTag);
790
+ removedCheckedkeysOther(removedTag);
791
+ refreshSearchTree();
792
+ }
793
+ const removeCheckedkeysIncludeChildren = (parentKey: any) => {
794
+ let needRemoveChildKeys = state.dataList.main
795
+ .filter((i: { key: any; }) => {
796
+ let parentId = getParentId(i);
797
+ return parentId == parentKey || i.key == parentKey;
798
+ })
799
+ .map((i: { key: any; }) => {
800
+ return i.key;
801
+ });
802
+
803
+ state.checkedKeys.main = state.checkedKeys.main.filter((i: any) => {
804
+ return !needRemoveChildKeys.includes(i);
805
+ });
806
+ }
807
+ const removedCheckedkeysMain = (tag: { key: any }) => {
808
+ let checkedKeys = state.checkedKeys.main;
809
+ let parentId = getParentId(tag);
810
+ for (let i = 0; i < checkedKeys.length; i++) {
811
+ if (tag.key == checkedKeys[i] && tag.key == parentId) {
812
+ removeCheckedkeysIncludeChildren(tag.key);
813
+ return false;
814
+ } else if (tag.key == checkedKeys[i]) {
815
+ checkedKeys.splice(i, 1);
816
+ return false;
817
+ }
818
+ }
819
+ }
820
+ const removedCheckedkeysOther = (tag: { key: any; }) => {
821
+ let checkedKeys = state.checkedKeys.other;
822
+ if (checkedKeys.includes(tag.key)) {
823
+ for (let i = 0; i < checkedKeys.length; i++) {
824
+ if (tag.key == checkedKeys[i]) {
825
+ checkedKeys.splice(i, 1);
826
+ return false;
827
+ }
828
+ }
829
+ } else {
830
+ // 删除科室,删除
831
+ OtherRemoveCheckedkeysIncludeChildren(tag.key);
832
+ }
833
+ }
834
+ const OtherRemoveCheckedkeysIncludeChildren = (parentKey: any) => {
835
+ let needRemoveChildKeys = state.dataList.main
836
+ .filter((i: { key: any; }) => {
837
+ let parentId = getParentId(i);
838
+ return parentId == parentKey || i.key == parentKey;
839
+ })
840
+ .map((i: { key: any; }) => {
841
+ return i.key;
842
+ });
843
+
844
+ state.checkedKeys.other = state.checkedKeys.other.filter((i: any) => {
845
+ return !needRemoveChildKeys.includes(i);
846
+ });
847
+ }
848
+ /* submit 相关 */
849
+ const getCheckedMainChildKeys = () => {
850
+ return new Promise<void>(async resolve => {
851
+ let promises = state.treeData.main.map(async item => {
852
+ // 获取勾选了父类tree, 但未获取children
853
+ // 接口传参,需要的是叶节点的keys
854
+ if (state.checkedKeys.main.includes(item.key) && !item.children) {
855
+ item.children = await handleLoadChildData(item.key, item);
856
+ addCheckedKeysChildrenMain(item);
857
+
858
+ generateDataList(item.children, state.dataList.main);
859
+ }
860
+ });
861
+
862
+ await Promise.all(promises);
863
+
864
+ resolve();
865
+ });
866
+ }
867
+ const getCheckedLeafKeys = () => {
868
+ let mainLeafKeys: any = [];
869
+ let mainLeafDataListKeys = state.dataList.main.filter((item: any) => item.isLeaf).map((item: any) => item.key);
870
+
871
+ state.checkedKeys.main.forEach(key => {
872
+ if (mainLeafDataListKeys.includes(key)) {
873
+ mainLeafKeys.push(key);
874
+ }
875
+ });
876
+
877
+ let keyList = [...mainLeafKeys, ...state.checkedKeys.other];
878
+ let res = [...new Set(keyList)];
879
+ return [...res];
880
+ }
881
+ const handleOk = async (isShowLoading = false) => {
882
+ isShowLoading && (state.spinning = true);
883
+ await getCheckedMainChildKeys();
884
+
885
+ state.checkedLeafKeys = getCheckedLeafKeys();
886
+
887
+ const submitData = handleGetSumitData(state.checkedLeafKeys);
888
+
889
+ // let subList = [...submitData, ...this.defaultShowList];
890
+
891
+ emit('pubCheckedTreeItem', [...checkedTreeItem.value, ...state.defaultShowList]);
892
+ emit('handleOk', state.checkedLeafKeys);
893
+ emit('change', state.checkedLeafKeys, [...submitData, ...state.defaultShowList]);
894
+ emit('submitData', [...submitData, ...state.defaultShowList]);
895
+
896
+ isShowLoading && (state.spinning = false);
897
+ }
898
+ // 获取表单提交的数据
899
+ const handleGetSumitData = (list = [] as any[]) => {
900
+ if (!Array.isArray(list)) return [];
901
+ let resList = [] as any[];
902
+ const { parent_id_obj, parent_name_obj, user_id_obj, user_name_obj } = wordbookConfig.value || {};
903
+ list.forEach(v => {
904
+ // d.key
905
+ let f = state.dataList.main.find(d => getuniqKey(d) == v);
906
+ if (!f) {
907
+ f = state.dataList.other.find(o => getuniqKey(o) == v);
908
+ }
909
+ // [parent_id_obj]: f[parent_id_obj],
910
+ // [parent_name_obj]: f[parent_name_obj],
911
+ // [user_id_obj]: f[user_id_obj],
912
+ // [user_name_obj]: f[user_name_obj]
913
+ if (f) {
914
+ /** 提交映射 */
915
+ // 自定义 key 值
916
+ const { parent_id_obj_key, parent_name_obj_key } = props.wordbook;
917
+ const { user_id_obj_key, user_name_obj_key } = props.wordbookChild;
918
+ resList.push({
919
+ [user_id_obj_key || 'user_id']: f[user_id_obj],
920
+ [user_name_obj_key || 'user_name']: f[user_name_obj],
921
+ [parent_id_obj_key || 'parent_id']: f[parent_id_obj],
922
+ [parent_name_obj_key || 'parent_name']: f[parent_name_obj]
923
+ });
924
+ }
925
+ });
926
+ return resList;
927
+ }
928
+ const searchOnloadMore = () => {
929
+ state.searchPage = state.searchPage + 1;
930
+ searchFetch({ page: state.searchPage }, { loadMoren: true });
931
+ }
932
+ const treeCount = (count: any) => {
933
+ if (!count) return '';
934
+ return `(${count})`;
935
+ }
936
+ const cancel = () => {
937
+ emit("update:visible", false);
938
+ state.spinning = false;
939
+ }
940
+ const hanldeGetValue = (...arg: any[]) => {
941
+ handleOk(...arg);
942
+ }
943
+ const getParentId = (v: any) => {
944
+ const { parent_id_obj } = wordbookConfig.value || {};
945
+ return `${v[parent_id_obj]}`
946
+ }
947
+ // 数组对象去重
948
+ const uniqArrObj = (arr: any[], name: string) => {
949
+ let obj = {};
950
+ return arr.reduce((cur, next) => {
951
+ obj[next[name]] ? '' : (obj[next[name]] = true && cur.push(next));
952
+ return cur;
953
+ }, []);
954
+ }
955
+ /**
956
+ * 拼接key
957
+ */
958
+ const getuniqKey = (v: any) => {
959
+ const { user_id_obj } = wordbookConfig.value || {};
960
+ return `${getParentId(v)}_${v[user_id_obj]}`;
961
+ }
962
+
963
+ watch(
964
+ () => props.defaultList,
965
+ (val: any[]) => {
966
+ if (!val) return;
967
+ initDefault();
968
+ },
969
+ { immediate: true }
970
+ );
971
+ watch(
972
+ () => state.checkedKeys.main,
973
+ (val: any[]) => {
974
+ isAllCheck(val);
975
+ isSearchAllCheck()
976
+ emit("check", val);
977
+ },
978
+ { immediate: true, deep: true }
979
+ );
980
+ watch(
981
+ () => props.data,
982
+ (val: any[]) => {
983
+ staticDataInit()
984
+ },
985
+ { immediate: true }
986
+ );
987
+
988
+ onMounted(() => {
989
+ getUsers()
990
+ })
991
+
992
+ </script>
993
+ <style scoped lang="less">
994
+ @primary-color: #5585f5;
995
+ .people-tree {
996
+ display: flex;
997
+ .people-tree-item {
998
+ width: 50%;
999
+ padding: 22px 12px 0;
1000
+ max-height: 376px;
1001
+ height: 376px;
1002
+ overflow-y: auto;
1003
+ &.people-tree-left {
1004
+ display: flex;
1005
+ flex-direction: column;
1006
+ }
1007
+ &:first-child {
1008
+ border-right: 1px solid #e8e8e8;
1009
+ }
1010
+ .tag-item {
1011
+ &.isRoot {
1012
+ background: rgba(@primary-color, 0.08);
1013
+ border: 1px solid rgba(@primary-color, 0.5);
1014
+ color: @primary-color;
1015
+ .tag-close {
1016
+ color: @primary-color;
1017
+ &:hover {
1018
+ color: rgba(@primary-color, 0.6);
1019
+ }
1020
+ }
1021
+
1022
+ }
1023
+
1024
+ box-sizing: border-box;
1025
+ color: #212121;
1026
+ font-size: 14px;
1027
+ list-style: none;
1028
+ display: inline-flex;
1029
+ align-items: center;
1030
+ height: 24px;
1031
+ line-height: 24px;
1032
+ margin-right: 8px;
1033
+ margin-bottom: 4px;
1034
+ padding: 0 6px;
1035
+ white-space: nowrap;
1036
+ background: rgba(#d5d5d5, 0.2);
1037
+ border: 1px solid rgba(#000000, 0.14);
1038
+ border-radius: 4px;
1039
+ transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
1040
+ .tag-close {
1041
+ font-size: 12px;
1042
+ &:hover {
1043
+ color: rgba(@primary-color, 0.6);
1044
+ }
1045
+ &:active {
1046
+ color: #fff;
1047
+ }
1048
+ display: inline-block;
1049
+ font-size: 14px;
1050
+ margin-left: 3px;
1051
+ color: #969696;
1052
+ font-weight: 700;
1053
+ cursor: pointer;
1054
+ transition: all 0.3s;
1055
+ }
1056
+ }
1057
+
1058
+ .no-data-tips {
1059
+ height: 200px;
1060
+ width: 100%;
1061
+ display: flex;
1062
+ justify-content: center;
1063
+ align-items: center;
1064
+ font-size: 16px;
1065
+ font-weight: 700;
1066
+ }
1067
+
1068
+ .c-title {
1069
+ font-size: 14px;
1070
+ font-family: PingFangSC-Regular, PingFang SC;
1071
+ font-weight: 400;
1072
+ color: #212121;
1073
+ }
1074
+ .c-sub-title {
1075
+ font-size: 12px;
1076
+ font-family: PingFangSC-Regular, PingFang SC;
1077
+ color: #4e4e4e;
1078
+ }
1079
+ .p-title {
1080
+ font-size: 14px;
1081
+ font-family: PingFangSC-Medium, PingFang SC;
1082
+ font-weight: 500;
1083
+ color: #212121;
1084
+ }
1085
+ &.tags-box {
1086
+ display: flex;
1087
+ flex-wrap: wrap;
1088
+ justify-content: flex-start;
1089
+ align-items: flex-start;
1090
+ align-content: flex-start;
1091
+ }
1092
+ }
1093
+ }
1094
+ .df {
1095
+ display: flex;
1096
+ }
1097
+ .search-input-icon {
1098
+ color: rgba(0, 0, 0, 0.4);
1099
+ margin-bottom: -2px;
1100
+ }
1101
+ .main-tree-container {
1102
+ overflow-y: auto;
1103
+ }
1104
+ </style>