@sy-common/organize-select-help 1.0.0-beta.11 → 1.0.0-beta.17

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.
package/src/index.vue CHANGED
@@ -1,34 +1,51 @@
1
1
  <template>
2
2
  <Modal
3
- title=""
4
- v-model="modal"
5
- @on-ok="confirm"
6
- @on-visible-change="visibleChange "
7
- width="1180"
8
- :mask-closable="false"
9
- class="modal-tree"
3
+ title=""
4
+ v-model="modal"
5
+ @on-ok="confirm"
6
+ @on-visible-change="visibleChange "
7
+ width="1180"
8
+ :mask-closable="false"
9
+ class="modal-tree"
10
10
  >
11
11
  <div slot="header" class="header-text"><Icon type="md-information" class="icon-tip" />人员选择</div>
12
12
  <div class="content-container">
13
13
  <div class="tree-orig">
14
14
  <div class="tab-content">
15
- <Tabs :value="tabName">
16
- <TabPane label="选择组织节点" name="org" v-if="name.includes('org')">
15
+ <Tabs :value="tabName" @input="handleTabChange">
16
+ <TabPane label="组织选择" name="org" v-if="name.includes('org')">
17
17
  <div class="tab">
18
- <Select v-model="orgSearch" filterable :remote-method="getOrgListBySearch" @on-change="getOrgOption" :loading="loadingOrg" placeholder="选择组织节点" clearable>
19
- <Option v-for="(option, index) in orgSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
18
+ <span>组织搜索:</span>
19
+ <Select v-model="orgSearch" filterable :remote-method="getOrgListBySearch" @on-change="getOrgOption" :loading="loadingOrg" placeholder="请输入组织名称" style="width: 600px;" clearable>
20
+ <Option v-for="(option, index) in orgSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
20
21
  </Select>
21
- <div class="tag-content">
22
+ <div class="tag-content" v-if="isQuickOpen && orgTagList.length > 0">
22
23
  <span>快捷选择:</span>
23
- <div class="tag-list">
24
- <span :class="['tag', item.checked && 'active']" v-for="item in tagList" :key="item.key" @click="fastChedkOrg(item)">{{item.v}}</span>
24
+ <div class="custom-select-wrapper">
25
+ <Select
26
+ v-model="selectedOrgTagKey"
27
+ placeholder="请选择快捷标签"
28
+ transfer
29
+ clearable
30
+ @on-change="handleOrgTagSelect"
31
+ @on-clear="handleOrgTagClear"
32
+ style="width: 600px;"
33
+ :disabled="!orgTagList.length"
34
+ >
35
+ <Option
36
+ v-for="item in orgTagList"
37
+ :value="item.quickPickKey"
38
+ :key="item.quickPickKey"
39
+ :class="{ 'active-option': item.checked }"
40
+ >
41
+ {{ item.quickPickName }}
42
+ </Option>
43
+ <!-- 无数据提示 -->
44
+ <Option value="" v-if="!postTagList.length" disabled>
45
+ 暂无快捷标签
46
+ </Option>
47
+ </Select>
25
48
  </div>
26
- <Poptip content="content" placement="right-end">
27
- <div slot="content" class="pop-content">
28
- <span :class="['tag', item.checked && 'active']" v-for="item in tagList" :key="item.key" @click="fastChedkOrg(item)">{{item.v}}</span>
29
- </div>
30
- <Icon type="ios-arrow-down" />
31
- </Poptip>
32
49
  </div>
33
50
  <div class="tree">
34
51
  <organizeTree @handleChange="getOrgList" ref="orgTree" :treeList="orgTree"></organizeTree>
@@ -43,23 +60,43 @@
43
60
  </div>
44
61
  </div>
45
62
  </TabPane>
46
- <TabPane label="选择岗位" name="post" v-if="name.includes('post')">
63
+ <TabPane label="岗位选择" name="post" v-if="name.includes('post')">
47
64
  <div class="tab post">
48
65
  <div class="left">
49
- <div v-for="item in positiontList" :class="[item.checked&&'active']" :key="item.positionId" @click="getPosionId(item)">{{ item.positionName }}</div>
66
+ <div v-for="item in positiontList" :class="[selectedPositionId === item.positionId ? 'active' : '']" :key="item.positionId" @click="getPosionId(item)">{{ item.positionName }}</div>
50
67
  </div>
51
68
  <div class="right">
52
- <Select v-model="postSearch" filterable :remote-method="getPostListBySearch" @on-change="getPostOption" placeholder="选择组织节点" :loading="loadingPost" clearable>
53
- <Option v-for="(option, index) in postSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
69
+ <span>组织搜索:</span>
70
+ <Select v-model="postSearch" filterable :remote-method="getPostListBySearch" @on-change="getPostOption" placeholder="组织选择" :loading="loadingPost" clearable @on-clear="handlePostSearchClear" style="width: 365px;">
71
+ <Option v-for="(option, index) in postSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
54
72
  </Select>
55
- <div class="tag-content">
73
+ <div class="tag-content" v-if="isQuickOpen && postTagList.length > 0">
56
74
  <span>快捷选择:</span>
57
- <div class="tag-list">
58
- <span class="tag" v-for="item in tagList" :key="item.key">{{item.v}}</span>
75
+ <div class="custom-select-wrapper">
76
+ <Select
77
+ v-model="selectedPostTagKey"
78
+ placeholder="请选择快捷标签"
79
+ transfer
80
+ clearable
81
+ @on-change="handlePostTagSelect"
82
+ @on-clear="handlePostTagClear"
83
+ style="width: 365px;"
84
+ :disabled="!postTagList.length"
85
+ >
86
+ <Option
87
+ v-for="item in postTagList"
88
+ :value="item.quickPickKey"
89
+ :key="item.quickPickKey"
90
+ :class="{ 'active-option': item.checked }"
91
+ >
92
+ {{ item.quickPickName }}
93
+ </Option>
94
+ <!-- 无数据提示 -->
95
+ <Option value="" v-if="!postTagList.length" disabled>
96
+ 暂无快捷标签
97
+ </Option>
98
+ </Select>
59
99
  </div>
60
- <Poptip title="Title" content="content">
61
- <Icon type="ios-arrow-down" />
62
- </Poptip>
63
100
  </div>
64
101
  <div class="tree">
65
102
  <organizeTree @handleChange="getPostList" ref="postTree" :treeList="postTree"></organizeTree>
@@ -77,31 +114,35 @@
77
114
  </TabPane>
78
115
  <TabPane label="选择人员" name="staff" v-if="name.includes('staff')">
79
116
  <div class="tab">
80
- <Input v-model="staffSearch" @on-enter="searchStaff" @on-search="searchStaff" search placeholder="搜索人员"/>
117
+ <Input v-model="staffSearch" @on-enter="searchStaff" @on-search="searchStaff" search placeholder="搜索人员"/>
81
118
  <div style="position:relative;">
82
119
  <div class="tree staff-content" @scroll="handleScroll">
83
- <div :class="['gust-item',item.checked && 'staff-active']" v-for="item in staffAllList" :key="item.id" @click="handlestaff(item)">
84
- <div class="left-panel">{{item.name && item.name.slice(0,1) || ''}}</div>
85
- <div class="right-panel">
86
- <p>{{item.name}}</p>
87
- <p>{{item.orgNodeName}}</p>
88
- </div>
89
- <div class="checked-icon" v-show="item.checked">✔</div>
120
+ <!-- 关键修改:用“index + 下划线 + item.id”做key,确保唯一且不影响数据 -->
121
+ <div :class="['gust-item',item.checked && 'staff-active']"
122
+ v-for="(item, index) in staffAllList"
123
+ :key="`staff_${index}_${item.id}`"
124
+ @click="handlestaff(item)">
125
+ <div class="left-panel">{{item.name && item.name.slice(0,1) || ''}}</div>
126
+ <div class="right-panel">
127
+ <p>{{item.name}}</p>
128
+ <p>{{item.orgNodeName || item.orgUnitName}}</p>
90
129
  </div>
91
- <p v-if="staffEnding" style="color:#CCCCCC;text-align: center">---我也是有底线的---</p>
130
+ <div class="checked-icon" v-show="item.checked">✔</div>
92
131
  </div>
93
- <Spin v-if="loadingStaff" size="large" fix />
94
- </div>
95
- <div class="bottom-select">
96
- <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>
97
- <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>
132
+ <p v-if="staffEnding" style="color:#CCCCCC;text-align: center">---我也是有底线的---</p>
98
133
  </div>
134
+ <Spin v-if="loadingStaff" size="large" fix />
99
135
  </div>
100
- </TabPane>
136
+ <div class="bottom-select">
137
+ <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>
138
+ <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>
139
+ </div>
140
+ </div>
141
+ </TabPane>
101
142
  </Tabs>
102
143
  </div>
103
144
  <div class="form-content">
104
- <p>已选择条件 <span class="num">({{getCheckTypenum}})</span></p>
145
+ <p v-if="name.length > 1">已选择条件 <span class="num">({{getCheckTypenum}})</span></p>
105
146
  <div class="node-list">
106
147
  <!-- 组织节点条件:-->
107
148
  <div v-if="name.includes('org')">
@@ -169,6 +210,10 @@ export default {
169
210
  name:{
170
211
  type:Array ,
171
212
  default(){return ['org','post','staff']}
213
+ },
214
+ isQuickOpen:{
215
+ type: Boolean,
216
+ default: false
172
217
  }
173
218
  },
174
219
  data(){
@@ -201,38 +246,109 @@ export default {
201
246
  lastLoadingTime:0,
202
247
  offset:0,
203
248
  staffEnding:false,
204
- tagList:[{
205
- v:'所选市公司所有部门',
206
- checked:false,
207
- key:'07'
208
- },{
209
- v:'所有市公司',
210
- checked:false,
211
- key:'08'
212
- },{
213
- v:'所有市公司',
214
- checked:false,
215
- key:'9'
216
- },{
217
- v:'所有市公司',
218
- checked:false,
219
- key:'10'
220
- },{
221
- v:'所有市公司',
222
- checked:false,
223
- key:'11'
224
- },{
225
- v:'所有市公司',
226
- checked:false,
227
- key:'12'
228
- }],
249
+ parentOrgList: [],
250
+ orgTagList:[],
251
+ postTagList:[],
252
+ selectedPositionId: null, // 新增:岗位单选的选中状态存储
253
+ selectedOrgTagKey: '', // 存储选中标签的唯一标识
254
+ selectedPostTagKey: '', // 存储选中标签的唯一标识
229
255
  }
230
256
  },
231
257
  mounted() {
258
+ this.queryTagList()
232
259
  this.queryPositionList()
233
260
  this.loadMore()
234
261
  },
235
262
  methods:{
263
+ handlePostTagSelect(quickPickKey) {
264
+ // 清空暂存列表
265
+ this.proPostList = [];
266
+ // 找到选中的标签对象
267
+ const selectedItem = this.postTagList.find(
268
+ item => item.quickPickKey === quickPickKey
269
+ );
270
+
271
+ if (selectedItem) {
272
+ // 重置其他标签的选中状态
273
+ this.postTagList.forEach(tag => tag.checked = false);
274
+ // 触发原有的处理方法
275
+ this.fastChedkPost(selectedItem);
276
+ // 清空搜索框绑定的值
277
+ this.postSearch = '';
278
+ // 清空搜索结果列表
279
+ this.postSearchList = [];
280
+ }
281
+ },
282
+ handleOrgTagSelect(quickPickKey) {
283
+ // 清空暂存列表
284
+ this.proOrgList = [];
285
+ // 找到选中的标签对象
286
+ const selectedItem = this.orgTagList.find(
287
+ item => item.quickPickKey === quickPickKey
288
+ );
289
+
290
+ if (selectedItem) {
291
+ // 重置其他标签的选中状态
292
+ this.orgTagList.forEach(tag => tag.checked = false);
293
+ // 触发原有的处理方法
294
+ this.fastChedkOrg(selectedItem);
295
+ // 清空搜索框绑定的值
296
+ this.orgSearch = '';
297
+ // 清空搜索结果列表
298
+ this.orgSearchList = [];
299
+ }
300
+ },
301
+ handleOrgTagClear() {
302
+ this.selectedOrgTagKey = '';
303
+ // 重置所有标签的选中状态
304
+ this.orgTagList.forEach(tag => {
305
+ tag.checked = false;
306
+ });
307
+
308
+ // 3. 清空树选择状态和暂存列表
309
+ if (this.$refs.orgTree && this.orgSearchList) {
310
+ this.$refs.orgTree.initData();
311
+ }
312
+ // 4. 可选:添加清空提示
313
+ this.$Message.info("已清空快捷标签选择");
314
+ },
315
+ // 修正清空逻辑方法(与事件名称对应)
316
+ handlePostTagClear() {
317
+ // 1. 重置选中的标签key
318
+ this.selectedPostTagKey = '';
319
+
320
+ // 2. 重置所有标签的选中状态
321
+ this.postTagList.forEach(tag => {
322
+ tag.checked = false;
323
+ });
324
+
325
+ // 3. 清空树选择状态和暂存列表
326
+ if (this.$refs.postTree) {
327
+ this.$refs.postTree.initData();
328
+ this.proPostList = [];
329
+ }
330
+
331
+ // 4. 可选:添加清空提示
332
+ this.$Message.info("已清空快捷标签选择");
333
+ },
334
+ handlePostSearchClear() {
335
+ // 清空搜索框绑定的值
336
+ this.postSearch = '';
337
+
338
+ // 清空搜索结果列表
339
+ this.postSearchList = [];
340
+
341
+ // 重置树组件状态(与原有逻辑保持一致)
342
+ if (this.$refs.postTree) {
343
+ this.$refs.postTree.initData();
344
+ }
345
+
346
+ // 清空暂存列表
347
+ this.proPostList = [];
348
+
349
+ // 可选:添加清空提示
350
+ this.$Message.info("已清空组织选择搜索");
351
+ },
236
352
  queryPositionList(){
237
353
  ajax.get('/pub-manage-server/pub/personHelpBox/q/queryPositionList').then((res)=>{
238
354
  if(res.data.code === 1){
@@ -246,64 +362,120 @@ export default {
246
362
  if (query !== '') {
247
363
  this.loadingOrg = true;
248
364
  this.getOrgUnitBySearchTerm(query,(res)=>{
249
- this.loadingOrg = false
250
- if(res.data.code === 1){
251
- let resp = res.data.data?.items??[]
252
- this.orgSearchList = resp
253
- }else{
254
- this.orgSearchList = []
255
- this.$Message.error("获取组织节点列表失败!")
256
- }
365
+ this.loadingOrg = false
366
+ if(res.data.code === 1){
367
+ let resp = res.data.data?.items??[]
368
+ this.orgSearchList = resp
369
+ }else{
370
+ this.orgSearchList = []
371
+ this.$Message.error("获取组织节点列表失败!")
372
+ }
257
373
  })
258
374
  }else{
259
375
  this.orgSearchList = [];
260
376
  }
261
377
  },
262
- getPostListBySearch(query){
378
+ getPostListBySearch(query) {
263
379
  if (query !== '') {
264
380
  this.loadingPost = true;
265
- this.getOrgUnitBySearchTerm(query,(res)=>{
266
- this.loadingPost = false
267
- if(res.data.code === 1){
268
- let resp = res.data.data?.items??[]
269
- this.postSearchList = resp
270
- }else{
271
- this.postSearchList = []
272
- this.$Message.error("获取组织节点列表失败!")
273
- }
274
- })
275
- }else{
276
- this.$refs.postTree.initData()
381
+ this.getOrgUnitBySearchTerm(query, (res) => {
382
+ this.loadingPost = false;
383
+ if (res.data.code === 1) {
384
+ let resp = res.data.data?.items ?? [];
385
+ // 修复:岗位搜索结果深拷贝,避免引用污染
386
+ this.postSearchList = this.safeDeepCopy(resp);
387
+ } else {
388
+ this.postSearchList = [];
389
+ this.$Message.error("获取组织节点列表失败!");
390
+ }
391
+ });
392
+ } else {
393
+ // 修复:清空搜索时,同时清空树数据和暂存列表(与组织Tab一致)
277
394
  this.postSearchList = [];
395
+ if (this.$refs.postTree) {
396
+ this.$refs.postTree.initData();
397
+ }
398
+ this.proPostList = []; // 清空岗位暂存列表
399
+ this.$set(this, 'postTree', []); // 清空岗位树数据
278
400
  }
279
401
  },
280
402
  getOrgUnitBySearchTerm(query,callback){
281
- ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitBySearchTerm?searchTerm='+query).then((res)=>{callback(res)})
403
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitBySearchTerm?searchTerm='+query).then((res)=>{callback(res)})
282
404
  },
283
- async getOrgOption(val){
405
+ async getOrgOption(val) {
406
+ this.selectedOrgTagKey = '';
407
+ this.orgTagList.forEach(tag => {
408
+ tag.checked = false;
409
+ });
410
+ this.proOrgList = [];
284
411
  if(!val) return this.$refs.orgTree.initData();
285
- let item = this.orgSearchList.filter((item)=> {return item.orgUnitId === val}).shift();
412
+ let item = this.orgSearchList.filter((item)=> item.orgUnitId === val).shift();
413
+ if (!item) return;
414
+
286
415
  const leafNode = await this.judgeNodeLeafe(item.orgUnitId);
287
- const ftem = {...item,orgNodeName:item.name,parentOrgUnitId:item.parentId,leafNode:leafNode}
416
+ const ftem = this.safeDeepCopy({
417
+ ...item,
418
+ orgNodeName: item.name,
419
+ parentOrgUnitId: item.parentId,
420
+ leafNode: leafNode
421
+ });
422
+
288
423
  try{
289
- let parentsList = await this.getParentOrgNodesByOrgUnitId(val)
290
- let tree = this.buildTree([...parentsList,ftem])
291
- this.orgTree = tree
424
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
425
+ // 对父节点列表进行安全拷贝
426
+ const safeParents = this.safeDeepCopy(parentsList);
427
+ let tree = this.buildTree([...safeParents, ftem]);
428
+ this.orgTree = this.safeDeepCopy(tree);
292
429
  }catch(e){
293
- this.$Message.error("获取组织节点列表失败!")
430
+ this.$Message.error("获取组织节点列表失败!");
294
431
  }
295
432
  },
296
- async getPostOption(val){
297
- if(!val) return this.$refs.postTree.initData();
298
- let item = this.postSearchList.filter((item)=> {return item.orgUnitId === val}).shift();
433
+ async getPostOption(val) {
434
+ // 1. 重置选中的标签key
435
+ this.selectedPostTagKey = '';
436
+
437
+ // 2. 重置所有标签的选中状态
438
+ this.postTagList.forEach(tag => {
439
+ tag.checked = false;
440
+ });
441
+ if (!val) {
442
+ // 修复:岗位树清空时,同时清空暂存列表和搜索框
443
+ if (this.$refs.postTree) {
444
+ this.$refs.postTree.initData();
445
+ }
446
+ this.proPostList = []; // 清空岗位暂存列表(与组织Tab保持一致)
447
+ this.postSearch = ''; // 清空搜索框
448
+ return;
449
+ }
450
+
451
+ let item = this.postSearchList.filter(item => item.orgUnitId === val).shift();
452
+ if (!item) return;
453
+
299
454
  const leafNode = await this.judgeNodeLeafe(item.orgUnitId);
300
- const ftem = {...item,orgNodeName:item.name,parentOrgUnitId:item.parentId,leafNode:leafNode}
301
- try{
302
- let parentsList = await this.getParentOrgNodesByOrgUnitId(val)
303
- let tree = this.buildTree([...parentsList,ftem])
304
- this.postTree = tree
305
- }catch(e){
306
- this.$Message.error("获取组织节点列表失败!")
455
+ // 修复:岗位节点深拷贝,避免引用污染
456
+ const ftem = this.safeDeepCopy({
457
+ ...item,
458
+ orgNodeName: item.name || `未命名组织(${item.orgUnitId})`,
459
+ parentOrgUnitId: item.parentId,
460
+ leafNode: leafNode
461
+ });
462
+
463
+ try {
464
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
465
+ const safeParents = this.safeDeepCopy(parentsList);
466
+ let tree = this.buildTree([...safeParents, ftem]);
467
+ // 修复:用$set确保岗位树数据响应式更新
468
+ this.$set(this, 'postTree', this.safeDeepCopy(tree));
469
+
470
+ // 修复:设置节点展开和选中(与组织Tab逻辑一致)
471
+ this.$nextTick(() => {
472
+ if (this.$refs.postTree && this.$refs.postTree.setCheckedNodes) {
473
+ this.$refs.postTree.setCheckedNodes([val]); // 选中当前节点
474
+ this.proPostList = [ftem]; // 同步暂存列表
475
+ }
476
+ });
477
+ } catch (e) {
478
+ this.$Message.error("获取组织节点列表失败!");
307
479
  }
308
480
  },
309
481
  getParentOrgNodesByOrgUnitId(val){
@@ -339,27 +511,28 @@ export default {
339
511
  })
340
512
  },
341
513
  buildTree(items) {
342
- // 创建一个映射表,用于快速查找节点
343
514
  const map = {};
344
515
  const roots = [];
516
+ // 对输入数据进行安全拷贝
517
+ const copiedItems = this.safeDeepCopy(items);
345
518
 
346
- // 初始化所有节点,添加children属性
347
- items.forEach(item => {
348
- map[item.orgUnitId] = { ...item, orgChildrenList: [] };
519
+ copiedItems.forEach(item => {
520
+ if (!item.orgUnitId) return; // 过滤无效节点
521
+ map[item.orgUnitId] = this.safeDeepCopy({
522
+ ...item,
523
+ orgChildrenList: []
524
+ });
349
525
  });
350
526
 
351
- // 构建树形结构
352
- items.forEach(item => {
527
+ copiedItems.forEach(item => {
528
+ if (!item.orgUnitId || !map[item.orgUnitId]) return;
529
+
353
530
  const node = map[item.orgUnitId];
354
- if (item.parentOrgUnitId === null) {
355
- // 根节点
531
+ if (!item.parentOrgUnitId || !map[item.parentOrgUnitId]) {
356
532
  roots.push(node);
357
533
  } else {
358
- // 子节点,找到父节点并添加到其children中
359
534
  const parent = map[item.parentOrgUnitId];
360
- if (parent) {
361
- parent.orgChildrenList.push(node);
362
- }
535
+ parent.orgChildrenList.push(node);
363
536
  }
364
537
  });
365
538
  return roots;
@@ -418,12 +591,21 @@ export default {
418
591
  }
419
592
  },
420
593
  getOrgList(data){
421
- this.proOrgList = data
594
+ if (this.proOrgList){
595
+ this.proOrgList = this.proOrgList.concat(data)
596
+ }else {
597
+ this.proOrgList = data
598
+ }
599
+
422
600
  },
423
601
  getPostList(data){
424
- this.proPostList = data
602
+ if (this.proPostList){
603
+ this.proPostList = this.proPostList.concat(data)
604
+ }else {
605
+ this.proPostList = data
606
+ }
425
607
  },
426
- addOrgList(){
608
+ addOrgList() {
427
609
  if(!this.proOrgList.length) return this.$Message.error("请先选择组织节点!")
428
610
  let proOrgList = deepCopy(this.proOrgList)
429
611
  proOrgList.forEach(item=>{
@@ -433,11 +615,18 @@ export default {
433
615
  })
434
616
  let list = this.orgList.concat(proOrgList)
435
617
  let uniqueArray = list.filter((item, index, self) =>
436
- index === self.findIndex(t => (
437
- t.id === item.id // 基于id属性去重
438
- ))
618
+ index === self.findIndex(t => (
619
+ t.id === item.id // 基于id属性去重
620
+ ))
439
621
  );
440
622
  this.orgList = uniqueArray
623
+
624
+ // 新增:清空组织树节点选中状态(关键优化)
625
+ if (this.$refs.orgTree && this.$refs.orgTree.clearAllChecked) {
626
+ this.$refs.orgTree.clearAllChecked(this.$refs.orgTree.data);
627
+ this.$refs.orgTree.$emit('handleChange', []); // 通知父组件清空暂存列表
628
+ }
629
+
441
630
  this.$refs.orgTree.upDataTree()
442
631
  this.proOrgList = []
443
632
  },
@@ -449,36 +638,44 @@ export default {
449
638
  },
450
639
  //岗位
451
640
  getPosionId(item){
452
- item.checked = !item.checked;
641
+ // item.checked = !item.checked;
642
+ this.selectedPositionId = item.positionId
453
643
  },
454
- addPostList(){
644
+ addPostList() {
455
645
  if(!this.proPostList.length) return this.$Message.error("请选择组织节点!")
456
646
  let proPostList = deepCopy(this.proPostList)
457
- let checkedPosition = this.positiontList.filter((item)=>item.checked === true)
458
- if(!checkedPosition.length){return this.$Message.error("请选择岗位!!")}
459
- //summary data
647
+ let checkedPosition = this.positiontList.find(item => item.positionId === this.selectedPositionId);
648
+ if(!checkedPosition){return this.$Message.error("请选择岗位!!")}
649
+ // summary data
460
650
  let totalList = []
461
- for(let it of checkedPosition){
462
- proPostList.forEach(item=>{
463
- totalList.push({
464
- ...item,
465
- ...it,
466
- title:`${this.includeLevelPost.length?(item.orgNodeName+'(包含下级组织节点)'):item.orgNodeName}`,
467
- includeLevel:this.includeLevelPost.includes('01'),
468
- id:this.includeLevelPost.includes('01')?('01'+'-'+it.positionId + '-' + item.orgUnitId):('00'+'-'+it.positionId + '-' + item.orgUnitId),
469
- })
651
+ proPostList.forEach(item=>{
652
+ totalList.push({
653
+ ...item,
654
+ ...checkedPosition,
655
+ title:`${this.includeLevelPost.length?(item.orgNodeName+'(包含下级组织节点)'):item.orgNodeName}`,
656
+ includeLevel:this.includeLevelPost.includes('01'),
657
+ id:this.includeLevelPost.includes('01')?('01'+'-'+checkedPosition.positionId + '-' + item.orgUnitId):('00'+'-'+checkedPosition.positionId + '-' + item.orgUnitId),
470
658
  })
471
- }
659
+ })
472
660
  let list = this.postList.concat(totalList)
473
661
  let uniqueArray = list.filter((item, index, self) =>
474
- index === self.findIndex(t => (
475
- t.id === item.id // 基于id属性去重
476
- ))
662
+ index === self.findIndex(t => (
663
+ t.id === item.id // 基于id属性去重
664
+ ))
477
665
  );
478
666
  this.postList = uniqueArray
667
+
668
+ // 新增:清空岗位树节点选中状态(关键优化)
669
+ if (this.$refs.postTree && this.$refs.postTree.clearAllChecked) {
670
+ this.$refs.postTree.clearAllChecked(this.$refs.postTree.data);
671
+ this.$refs.postTree.$emit('handleChange', []); // 通知父组件清空暂存列表
672
+ }
673
+
479
674
  this.$refs.postTree.upDataTree()
480
675
  this.proPostList = []
481
- this.positiontList.map((item)=>{item.checked=false})
676
+ this.selectedPositionId = null; // 清空岗位单选状态
677
+ this.$refs.postTree.initData()
678
+ this.selectedPostTagKey = '';
482
679
  },
483
680
  clearPost(){
484
681
  this.postList= []
@@ -488,15 +685,16 @@ export default {
488
685
  },
489
686
  //staff
490
687
  addStaffList(){
491
- let staffList = this.staffAllList.filter((item)=>item.checked===true)
492
- let list = this.staffList.concat(staffList)
493
- let uniqueArray = list.filter((item, index, self) =>
494
- index === self.findIndex(t => (
495
- t.id === item.id // 基于id属性去重
496
- ))
688
+ let staffList = this.staffAllList.filter((item)=>item.checked===true);
689
+ // 关键优化:基于item.id去重,不修改原数据字段,仅过滤重复项
690
+ let uniqueStaffList = staffList.filter(newItem =>
691
+ // 检查右侧条件区域(staffList)是否已有该人员,避免重复添加
692
+ !this.staffList.some(existItem => existItem.id === newItem.id)
497
693
  );
498
- this.staffList = uniqueArray
499
- this.staffAllList.map((item)=>item.checked=false)
694
+ // 合并去重后的列表,右侧条件区域数据结构完全不变
695
+ this.staffList = this.staffList.concat(uniqueStaffList);
696
+ // 清空选中状态(保持原有逻辑)
697
+ this.staffAllList.forEach(item => item.checked = false);
500
698
  },
501
699
  handlestaff(item){
502
700
  item.checked=!item.checked
@@ -518,9 +716,537 @@ export default {
518
716
  visibleChange(val){
519
717
  this.$emit('input',val);
520
718
  },
521
- fastChedkOrg(item){
522
- item.checked = !item.checked
719
+ // 处理Tab切换,同步更新tabName
720
+ handleTabChange(tabName) {
721
+ this.tabName = tabName;
722
+ // 可选:切换到岗位Tab时,初始化岗位树选中状态(避免残留组织Tab数据)
723
+ if (tabName === 'post' && this.$refs.postTree && this.proPostList.length === 0) {
724
+ this.proPostList = [];
725
+ this.$refs.postTree.initData();
726
+ }
727
+ },
728
+ async fastChedkOrg(item) {
729
+ // 1. 改用组织 Tab 专属标签列表判断
730
+ if (!this.orgTagList.length) {
731
+ this.$Message.warning("快捷选择标签正在加载中,请稍后");
732
+ return;
733
+ }
734
+ const treeRef = this.$refs.orgTree;
735
+ const proListKey = 'proOrgList';
736
+ const treeDataKey = 'orgTree';
737
+ this[proListKey] = [];
738
+ if (treeRef) await treeRef.initData();
739
+ if (!treeRef) {
740
+ this.$Message.error("组织树组件未初始化,请稍后再试");
741
+ item.checked = !item.checked;
742
+ return;
743
+ }
744
+
745
+ // 2. 清空组织 Tab 标签选中状态(仅操作 orgTagList)
746
+ this.orgTagList.forEach(tag => {
747
+ tag.checked = false;
748
+ });
749
+
750
+ // 3. 后续逻辑保持不变,仅操作当前 item(属于 orgTagList)
751
+ const isCurrentlyChecked = item.checked;
752
+ if (!isCurrentlyChecked) {
753
+ item.checked = true;
754
+ }
755
+
756
+ if (!item.checked) {
757
+ this.$Message.info("已取消该快捷选择");
758
+ return;
759
+ }
760
+
761
+ try {
762
+ await this.querySpecificParam(item, true, treeRef, proListKey, treeDataKey);
763
+ } catch (error) {
764
+ this.$Message.error(`快捷选择处理失败:${error.message.slice(0, 50)}`);
765
+ item.checked = !item.checked;
766
+ }
767
+ },
768
+ async fastChedkPost(item) {
769
+ // 1. 改用岗位 Tab 专属标签列表判断
770
+ if (!this.postTagList.length) {
771
+ this.$Message.warning("快捷选择标签正在加载中,请稍后");
772
+ return;
773
+ }
774
+
775
+ const isOrgTab = this.tabName === 'org';
776
+ const treeRef = isOrgTab ? this.$refs.orgTree : this.$refs.postTree;
777
+ const proListKey = isOrgTab ? 'proOrgList' : 'proPostList';
778
+ const treeDataKey = isOrgTab ? 'orgTree' : 'postTree';
779
+
780
+ this[proListKey] = [];
781
+ const isCurrentlyChecked = item.checked;
782
+ if (treeRef) await treeRef.initData();
783
+
784
+ // 2. 清空岗位 Tab 标签选中状态(仅操作 postTagList)
785
+ this.postTagList.forEach(tag => {
786
+ tag.checked = false;
787
+ });
788
+
789
+ // 3. 后续逻辑保持不变,仅操作当前 item(属于 postTagList)
790
+ if (!isCurrentlyChecked) {
791
+ item.checked = true;
792
+ }
793
+
794
+ if (!item.checked) {
795
+ this.$Message.info("已取消该快捷选择");
796
+ return;
797
+ }
798
+
799
+ try {
800
+ await this.querySpecificParam(item, isOrgTab, treeRef, proListKey, treeDataKey);
801
+ } catch (error) {
802
+ this.$Message.error(`快捷选择处理失败:${error.message.slice(0, 50)}`);
803
+ item.checked = !item.checked;
804
+ }
523
805
  },
806
+ queryTagList() {
807
+ ajax.get('/pub-manage-server/pub/helpBoxTag/q/queryQuickPickTags').then((res) => {
808
+ if (res.data.code === 1) {
809
+ let baseTagList = res.data.data || [];
810
+ // 组织 Tab 标签:深拷贝并初始化选中状态
811
+ this.orgTagList = this.safeDeepCopy(baseTagList).map(item => ({
812
+ ...item,
813
+ checked: false // 独立选中状态
814
+ }));
815
+ // 岗位 Tab 标签:深拷贝并初始化选中状态(与组织 Tab 完全独立)
816
+ this.postTagList = this.safeDeepCopy(baseTagList).map(item => ({
817
+ ...item,
818
+ checked: false // 独立选中状态
819
+ }));
820
+ }
821
+ });
822
+ },
823
+
824
+ querySpecificParam: function (item, isOrgTab, treeRef, proListKey, treeDataKey) {
825
+ return new Promise((resolve, reject) => {
826
+ // 取消选中时处理(仅当手动点击已选中的标签时触发)
827
+ if (!item.checked) {
828
+ if (treeRef && treeRef.initData) {
829
+ treeRef.initData(); // 清空对应树组件
830
+ }
831
+ this[proListKey] = []; // 清空对应暂存列表
832
+ this.$Message.info("已取消该快捷选择");
833
+ resolve();
834
+ return;
835
+ }
836
+
837
+ // 请求快捷选择对应的orgUnitId列表
838
+ ajax.get(`/pub-manage-server/pub/helpBoxTag/q/querySpecificParam?quickPickKey=${item.quickPickKey}`)
839
+ .then(async (res) => {
840
+ if (res.data.code !== 1) {
841
+ this.$Message.error("获取快捷选择配置失败");
842
+ item.checked = !item.checked;
843
+ reject(new Error("获取配置失败"));
844
+ return;
845
+ }
846
+
847
+ const orgUnitIds = res.data.data?.param || [];
848
+ const safeOrgUnitIds = Array.isArray(orgUnitIds) ? orgUnitIds : [];
849
+ if (safeOrgUnitIds.length === 0) {
850
+ this.$Message.warning("该快捷选择未配置任何组织节点");
851
+ item.checked = !item.checked;
852
+ reject(new Error("无配置节点"));
853
+ return;
854
+ }
855
+
856
+ // 校验树组件是否存在(通用校验)
857
+ if (!treeRef) {
858
+ this.$Message.error("树组件未初始化");
859
+ item.checked = !item.checked;
860
+ reject(new Error("树组件不存在"));
861
+ return;
862
+ }
863
+ const originalTreeData = treeRef.data && treeRef.data.length ? this.safeDeepCopy(treeRef.data) : [];
864
+ const matchedNodes = [];
865
+
866
+ // 批量处理每个orgUnitId(循环逻辑不变,通用处理)
867
+ for (const orgUnitId of safeOrgUnitIds) {
868
+ try {
869
+ // 节点详情查询(复用原有方法)
870
+ let currentNode = await this.queryOrgNodeDetail(orgUnitId);
871
+ if (!currentNode || !currentNode.orgUnitId) {
872
+ this.$Message.warning(`节点【${orgUnitId}】数据异常,跳过处理`);
873
+ continue;
874
+ }
875
+
876
+ // 循环引用检测与处理(复用原有方法)
877
+ const cycle = this.detectCircularReferences(currentNode);
878
+ if (cycle) {
879
+ this.$Message.warning(`节点【${orgUnitId}】存在循环引用,已自动修复`);
880
+ currentNode = this.safeDeepCopy(currentNode);
881
+ }
882
+
883
+ // 节点名称默认值处理(复用原有逻辑)
884
+ currentNode.orgNodeName = currentNode.orgNodeName || currentNode.orgUnitName || `未命名组织(${orgUnitId})`;
885
+ currentNode.orgUnitName = currentNode.orgNodeName;
886
+
887
+ // 节点是否已在树中检测(复用原有方法)
888
+ const nodeInTree = this.findNodeInTree(originalTreeData, orgUnitId);
889
+ if (nodeInTree) {
890
+ if (this.detectCircularReferences(nodeInTree)) {
891
+ this.$Message.warning(`树中节点【${orgUnitId}】存在循环引用,已跳过`);
892
+ continue;
893
+ }
894
+ matchedNodes.push(nodeInTree);
895
+ continue;
896
+ }
897
+
898
+ // 父节点追溯与节点链挂载(修复:使用当前Tab的树数据originalTreeData,而非固定orgTree)
899
+ let targetParent = null;
900
+ let nodeChain = [currentNode];
901
+ const MAX_DEPTH = 8;
902
+ let depth = 0;
903
+ let lastParentId = '';
904
+
905
+ while (!targetParent && depth < MAX_DEPTH) {
906
+ const parentId = currentNode.parentOrgUnitId;
907
+ if (!parentId || typeof parentId !== 'string' || parentId === lastParentId) {
908
+ break;
909
+ }
910
+ lastParentId = parentId;
911
+
912
+ const parentNode = await this.queryOrgNodeDetail(parentId);
913
+ if (!parentNode || !parentNode.orgUnitId) {
914
+ break;
915
+ }
916
+
917
+ if (this.detectCircularReferences(parentNode)) {
918
+ this.$Message.warning(`父节点【${parentId}】存在循环引用,已跳过`);
919
+ break;
920
+ }
921
+
922
+ parentNode.orgNodeName = parentNode.orgNodeName || parentNode.orgUnitName || `未命名组织(${parentId})`;
923
+ // 修复:查询父节点是否在当前Tab的树数据中(originalTreeData),而非固定orgTree
924
+ const parentInTree = this.findNodeInTree(originalTreeData, parentNode.orgUnitId);
925
+
926
+ if (parentInTree) {
927
+ targetParent = parentInTree;
928
+ break;
929
+ }
930
+
931
+ nodeChain.unshift(parentNode);
932
+ currentNode = parentNode;
933
+ depth++;
934
+ }
935
+
936
+ // 节点链挂载(复用原有方法)
937
+ if (targetParent) {
938
+ let currentMountParent = targetParent;
939
+ for (const node of nodeChain) {
940
+ const formattedNode = this.safeDeepCopy({
941
+ ...node,
942
+ leafNode: await this.judgeNodeLeafe(node.orgUnitId),
943
+ expand: true,
944
+ checked: false,
945
+ orgChildrenList: [],
946
+ children: []
947
+ });
948
+
949
+ if (!Array.isArray(currentMountParent.orgChildrenList)) {
950
+ currentMountParent.orgChildrenList = [];
951
+ currentMountParent.children = [];
952
+ }
953
+
954
+ const isDuplicate = currentMountParent.orgChildrenList.some(
955
+ child => child.orgUnitId === formattedNode.orgUnitId
956
+ );
957
+ if (!isDuplicate) {
958
+ currentMountParent.orgChildrenList.push(formattedNode);
959
+ currentMountParent.leafNode = false;
960
+ }
961
+
962
+ currentMountParent = currentMountParent.orgChildrenList.find(
963
+ child => child.orgUnitId === formattedNode.orgUnitId
964
+ ) || formattedNode;
965
+ }
966
+
967
+ const mountedTarget = this.findNodeInTree(originalTreeData, orgUnitId);
968
+ if (mountedTarget) {
969
+ matchedNodes.push(mountedTarget);
970
+ }
971
+ }
972
+
973
+ } catch (error) {
974
+ this.$Message.error(`处理节点【${orgUnitId}】失败:${error.message.slice(0, 50)}`);
975
+ continue;
976
+ }
977
+ }
978
+
979
+ // 最终数据赋值(根据Tab类型区分,核心统一逻辑)
980
+
981
+ if (matchedNodes.length > 0) {
982
+ // 同步暂存列表(组织proOrgList/岗位proPostList)
983
+ this[proListKey] = matchedNodes;
984
+ const finalTreeData = this.safeDeepCopy(originalTreeData);
985
+ // 响应式更新对应树数据(组织orgTree/岗位postTree)
986
+ this.$set(this, treeDataKey, finalTreeData);
987
+
988
+ await this.$nextTick(async () => {
989
+ // 更新树组件数据(通用逻辑)
990
+ if (treeRef.updateTreeData) {
991
+ treeRef.updateTreeData(this.safeDeepCopy(finalTreeData));
992
+ } else {
993
+ treeRef.data = this.safeDeepCopy(finalTreeData);
994
+ }
995
+
996
+ // 设置树选中状态(通用逻辑)
997
+ const safeNodeIds = matchedNodes.map(node => node.orgUnitId);
998
+ if (treeRef.setCheckedNodes) {
999
+ treeRef.setCheckedNodes([]);
1000
+ treeRef.setCheckedNodes(safeNodeIds);
1001
+ treeRef.handleChange(matchedNodes); // 触发子组件选中事件
1002
+ } else {
1003
+ this.updateTreeNodesStatus(
1004
+ finalTreeData,
1005
+ new Set(safeNodeIds),
1006
+ new Set(safeNodeIds)
1007
+ );
1008
+ treeRef.handleChange(matchedNodes);
1009
+ }
1010
+
1011
+ // 提示信息(通用逻辑)
1012
+ this.$Message.success(`成功选中${matchedNodes.length}个组织节点`);
1013
+ });
1014
+ } else {
1015
+ this.$Message.warning("未找到任何可匹配的组织节点");
1016
+ item.checked = !item.checked;
1017
+ }
1018
+ resolve();
1019
+ })
1020
+ .catch((error) => {
1021
+ const errMsg = error.message || '网络异常';
1022
+ this.$Message.error(`快捷选择失败:${errMsg.slice(0, 50)}`);
1023
+ item.checked = !item.checked;
1024
+ reject(error);
1025
+ });
1026
+ });
1027
+ },
1028
+
1029
+ // 新增:移除树数据中的 parent 引用,避免子组件处理时形成循环
1030
+ removeParentReferences(treeData) {
1031
+ if (!Array.isArray(treeData)) return;
1032
+ treeData.forEach(node => {
1033
+ // 删除可能存在的 parent 引用(如果节点有此属性)
1034
+ if (node.parent) delete node.parent;
1035
+ // 递归处理子节点
1036
+ if (Array.isArray(node.orgChildrenList)) {
1037
+ this.removeParentReferences(node.orgChildrenList);
1038
+ }
1039
+ if (Array.isArray(node.children)) {
1040
+ this.removeParentReferences(node.children);
1041
+ }
1042
+ });
1043
+ },
1044
+ // 在 methods 中添加循环引用检测工具函数
1045
+ detectCircularReferences(obj, path = [], visited = new Map()) {
1046
+ if (obj === null || typeof obj !== 'object') return null;
1047
+
1048
+ if (visited.has(obj)) {
1049
+ return { path: [...path, visited.get(obj)], node: obj };
1050
+ }
1051
+
1052
+ visited.set(obj, Array.isArray(obj) ? 'Array' : `Object(${obj.orgUnitId || 'unknown'})`);
1053
+
1054
+ const keys = Array.isArray(obj) ? obj.map((_, i) => i) : Object.keys(obj);
1055
+ for (const key of keys) {
1056
+ const value = obj[key];
1057
+ const newPath = [...path, key];
1058
+ if (value && typeof value === 'object') {
1059
+ const cycle = this.detectCircularReferences(value, newPath, visited);
1060
+ if (cycle) return cycle;
1061
+ }
1062
+ }
1063
+
1064
+ visited.delete(obj);
1065
+ return null;
1066
+ },
1067
+
1068
+ // 增强安全深拷贝,主动移除循环引用字段
1069
+ safeDeepCopy(obj, hash = new WeakMap()) {
1070
+ if (obj === null || typeof obj !== 'object') {
1071
+ return obj;
1072
+ }
1073
+
1074
+ // 检测到循环引用时,仅保留基础字段
1075
+ if (hash.has(obj)) {
1076
+ const safeObj = { orgUnitId: obj.orgUnitId, name: obj.name || obj.orgNodeName };
1077
+ hash.set(obj, safeObj);
1078
+ return safeObj;
1079
+ }
1080
+
1081
+ let copy;
1082
+ if (obj instanceof Array) {
1083
+ copy = [];
1084
+ hash.set(obj, copy);
1085
+ for (let i = 0; i < obj.length; i++) {
1086
+ copy[i] = this.safeDeepCopy(obj[i], hash);
1087
+ }
1088
+ } else if (obj instanceof Object) {
1089
+ copy = {};
1090
+ hash.set(obj, copy);
1091
+ for (let key in obj) {
1092
+ if (obj.hasOwnProperty(key)) {
1093
+ // 严格过滤可能导致循环的字段
1094
+ if (['parent', '__parent', 'parentNode', '__vue__', '__ob__', '$parent'].includes(key)) {
1095
+ continue;
1096
+ }
1097
+ // 对子节点数组进行特殊处理
1098
+ if (['orgChildrenList', 'children'].includes(key) && Array.isArray(obj[key])) {
1099
+ copy[key] = obj[key].map(child => this.safeDeepCopy(child, hash));
1100
+ } else {
1101
+ copy[key] = this.safeDeepCopy(obj[key], hash);
1102
+ }
1103
+ }
1104
+ }
1105
+ } else {
1106
+ copy = obj;
1107
+ }
1108
+ return copy;
1109
+ },
1110
+
1111
+ /**
1112
+ * 查询单个节点的原生详情(适配后端接口,返回原生字段)
1113
+ * @param {string} orgUnitId - 节点ID(如11510101)
1114
+ * @returns {Promise<Object|null>} 原生节点详情(无数据返回null)
1115
+ */
1116
+ getOrgNodeDetail(orgUnitId) {
1117
+ return new Promise((resolve) => {
1118
+ ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
1119
+ params: { orgUnitId: orgUnitId }
1120
+ }).then((res) => {
1121
+ if (res.data.code === 1 && res.data.data) {
1122
+ // 仅保留原生树结构必需字段(过滤业务冗余字段)
1123
+ const { orgUnitId, parentOrgUnitId, orgNodeName, orgChildrenList } = res.data.data;
1124
+ resolve({
1125
+ orgUnitId,
1126
+ parentOrgUnitId,
1127
+ orgNodeName,
1128
+ orgChildrenList: orgChildrenList || [] // 兼容接口返回空的情况
1129
+ });
1130
+ } else {
1131
+ resolve(null);
1132
+ }
1133
+ }).catch(() => {
1134
+ this.$Message.error(`查询节点【${orgUnitId}】详情失败`);
1135
+ resolve(null);
1136
+ });
1137
+ });
1138
+ },
1139
+
1140
+ /**
1141
+ * 迭代查找节点(替代递归,避免栈溢出)
1142
+ * @param {Array} treeData - 树形数据
1143
+ * @param {string} targetOrgUnitId - 目标节点ID
1144
+ * @returns {Object|null} 找到的节点
1145
+ */
1146
+ findNodeInTree(treeData, targetOrgUnitId) {
1147
+ if (!Array.isArray(treeData) || !targetOrgUnitId || typeof targetOrgUnitId !== 'string') {
1148
+ return null;
1149
+ }
1150
+
1151
+ // 用队列实现广度优先搜索(BFS),避免递归栈溢出
1152
+ const queue = [...treeData];
1153
+ while (queue.length > 0) {
1154
+ const currentNode = queue.shift(); // 取出队列头部节点
1155
+ // 找到目标节点,直接返回
1156
+ if (currentNode.orgUnitId === targetOrgUnitId) {
1157
+ return currentNode;
1158
+ }
1159
+ // 将当前节点的子节点加入队列(继续查找)
1160
+ if (Array.isArray(currentNode.orgChildrenList) && currentNode.orgChildrenList.length > 0) {
1161
+ queue.push(...currentNode.orgChildrenList);
1162
+ }
1163
+ // 兼容子组件可能使用的children字段
1164
+ if (Array.isArray(currentNode.children) && currentNode.children.length > 0 && !currentNode.orgChildrenList) {
1165
+ queue.push(...currentNode.children);
1166
+ }
1167
+ }
1168
+
1169
+ // 遍历完所有节点未找到
1170
+ return null;
1171
+ },
1172
+
1173
+ /**
1174
+ * 辅助方法:查询单个orgUnitId的节点详情
1175
+ * @param {string} orgUnitId - 组织节点ID
1176
+ * @returns {Promise<Object|null>} 节点详情
1177
+ */
1178
+ queryOrgNodeDetail(orgUnitId) {
1179
+ return new Promise((resolve) => {
1180
+ // 前置校验:避免无效请求
1181
+ if (!orgUnitId || typeof orgUnitId !== 'string') {
1182
+ resolve(null);
1183
+ return;
1184
+ }
1185
+
1186
+ ajax.get(`/pub-manage-server/pub/organ/q/queryOrg?orgUnitId=${orgUnitId}`)
1187
+ .then((res) => {
1188
+ if (res.data.code === 1 && res.data.data && res.data.data.orgUnitId) {
1189
+ // 深拷贝并清理无效字段
1190
+ const node = this.safeDeepCopy(res.data.data);
1191
+ // 核心修复:确保节点名称有默认值,避免空显示
1192
+ const nodeName = node.orgNodeName || node.orgUnitName || node.name || `未命名组织(${orgUnitId})`;
1193
+ node.orgNodeName = nodeName;
1194
+ node.orgUnitName = nodeName; // 同步更新orgUnitName,确保所有使用场景都有值
1195
+ // 确保关键字段存在
1196
+ node.parentOrgUnitId = node.parentOrgUnitId || null;
1197
+ node.orgChildrenList = node.orgChildrenList || [];
1198
+ resolve(node);
1199
+ } else {
1200
+ // 接口返回空时,返回默认结构避免报错
1201
+ resolve({
1202
+ orgUnitId,
1203
+ orgNodeName: `未命名组织(${orgUnitId})`,
1204
+ orgUnitName: `未命名组织(${orgUnitId})`,
1205
+ parentOrgUnitId: null,
1206
+ orgChildrenList: []
1207
+ });
1208
+ }
1209
+ })
1210
+ .catch(() => {
1211
+ // 请求失败时,返回默认结构避免报错
1212
+ resolve({
1213
+ orgUnitId,
1214
+ orgNodeName: `未命名组织(${orgUnitId})`,
1215
+ orgUnitName: `未命名组织(${orgUnitId})`,
1216
+ parentOrgUnitId: null,
1217
+ orgChildrenList: []
1218
+ });
1219
+ });
1220
+ });
1221
+ },
1222
+
1223
+
1224
+ /**
1225
+ * 手动更新树节点状态(如果树组件没有提供update方法)
1226
+ * @param {Array} nodeList - 节点列表
1227
+ * @param {Set} expandIds - 需要展开的节点ID集合
1228
+ * @param {Set} checkedIds - 需要选中的节点ID集合
1229
+ */
1230
+ updateTreeNodesStatus(nodeList, expandIds, checkedIds) {
1231
+ nodeList.forEach(node => {
1232
+ // 更新展开状态
1233
+ if (expandIds.has(node.orgUnitId)) {
1234
+ node.expand = true;
1235
+ }
1236
+
1237
+ // 更新选中状态
1238
+ if (checkedIds.has(node.orgUnitId)) {
1239
+ node.checked = true;
1240
+ }
1241
+
1242
+ // 递归处理子节点
1243
+ const children = node.children || node.orgChildrenList;
1244
+ if (children && children.length > 0) {
1245
+ this.updateTreeNodesStatus(children, expandIds, checkedIds);
1246
+ }
1247
+ });
1248
+ }
1249
+
524
1250
  },
525
1251
  computed:{
526
1252
  getCheckedStaff(){
@@ -562,6 +1288,64 @@ export default {
562
1288
  }
563
1289
  </script>
564
1290
  <style lang="less" scoped>
1291
+ .custom-select-wrapper {
1292
+ position: relative;
1293
+
1294
+ /* 优化清空按钮样式 */
1295
+ /deep/ .ivu-select-clear {
1296
+ right: 32px; /* 调整位置,避免与下拉箭头重叠 */
1297
+ color: #999;
1298
+ font-size: 16px;
1299
+ opacity: 1 !important; /* 始终显示(有值时) */
1300
+ transition: all 0.2s ease;
1301
+
1302
+ &:hover {
1303
+ color: #ff4d4f; /* hover时变红 */
1304
+ transform: scale(1.1); /* 轻微放大效果 */
1305
+ }
1306
+ }
1307
+
1308
+ /* 调整下拉箭头位置 */
1309
+ /deep/ .ivu-select-arrow {
1310
+ right: 12px;
1311
+ }
1312
+
1313
+ /* 优化输入框内边距,避免文字被按钮遮挡 */
1314
+ /deep/ .ivu-select-input {
1315
+ padding-right: 45px !important;
1316
+ }
1317
+
1318
+ /* 选中选项样式 */
1319
+ /deep/ .active-option {
1320
+ background-color: #f2f8ff;
1321
+ color: var(--primary-color);
1322
+ }
1323
+
1324
+ /* 无数据选项样式 */
1325
+ /deep/ .ivu-select-option-disabled {
1326
+ color: #ccc !important;
1327
+ background: #f5f5f5 !important;
1328
+ }
1329
+ }
1330
+ .tag-select-container {
1331
+ margin-left: 10px;
1332
+
1333
+ /deep/ .ivu-select-dropdown {
1334
+ max-height: 200px;
1335
+ overflow-y: auto;
1336
+ }
1337
+
1338
+ /deep/ .active-option {
1339
+ background-color: #f2f8ff;
1340
+ color: var(--primary-color);
1341
+ }
1342
+
1343
+ /deep/ .ivu-select-selected-value {
1344
+ max-width: 150px;
1345
+ overflow: hidden;
1346
+ text-overflow: ellipsis;
1347
+ }
1348
+ }
565
1349
  .modal-tree{
566
1350
  .header-text{
567
1351
  font-weight: bold;
@@ -652,10 +1436,10 @@ export default {
652
1436
  .tag{
653
1437
  margin-top: 10px;
654
1438
  &.active{
655
- background: var(--primary-color);
656
- border-radius: 4px;
657
- color: #fff;
658
- }
1439
+ background: var(--primary-color);
1440
+ border-radius: 4px;
1441
+ color: #fff;
1442
+ }
659
1443
  }
660
1444
  }
661
1445
  .post{