@sy-common/organize-select-help 1.0.0-beta.7 → 1.0.0-beta.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.
package/src/index.vue CHANGED
@@ -1,29 +1,59 @@
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
+ :fullscreen="true"
10
11
  >
11
12
  <div slot="header" class="header-text"><Icon type="md-information" class="icon-tip" />人员选择</div>
12
13
  <div class="content-container">
13
14
  <div class="tree-orig">
14
- <div class="tab-content">
15
- <Tabs :value="tabName">
16
- <TabPane label="选择组织节点" name="org">
15
+ <div class="tab-content-pro">
16
+ <Tabs :value="tabName" @input="handleTabChange">
17
+ <TabPane label="组织选择" name="org" v-if="name.includes('org')">
17
18
  <div class="tab">
18
- <Input v-model="orgSearch" placeholder="搜索组织节点"/>
19
- <div class="tag-content">
19
+ <div class="search-row">
20
+ <span class="search-label">组织搜索:</span>
21
+ <div class="search-input-wrapper">
22
+ <Select v-model="orgSearch" filterable :remote-method="getOrgListBySearch" @on-change="getOrgOption" :loading="loadingOrg" placeholder="请输入组织名称" style="width: 100%;" clearable>
23
+ <Option v-for="(option, index) in orgSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
24
+ </Select>
25
+ </div>
26
+ </div>
27
+ <div class="tag-content" v-if="isQuickOpen && orgTagList.length > 0">
20
28
  <span>快捷选择:</span>
21
- <div class="tag-list">
22
- <span class="tag" v-for="item in tagList" :key="item.key">{{item.v}}</span>
29
+ <div class="custom-select-wrapper">
30
+ <Select
31
+ v-model="selectedOrgTagKey"
32
+ placeholder="请选择快捷标签"
33
+ transfer
34
+ clearable
35
+ @on-change="handleOrgTagSelect"
36
+ @on-clear="handleOrgTagClear"
37
+ style="width: 600px;"
38
+ :disabled="!orgTagList.length"
39
+ >
40
+ <Option
41
+ v-for="item in orgTagList"
42
+ :value="item.quickPickKey"
43
+ :key="item.quickPickKey"
44
+ :class="{ 'active-option': item.checked }"
45
+ >
46
+ {{ item.quickPickName }}
47
+ </Option>
48
+ <!-- 无数据提示 -->
49
+ <Option value="" v-if="!postTagList.length" disabled>
50
+ 暂无快捷标签
51
+ </Option>
52
+ </Select>
23
53
  </div>
24
54
  </div>
25
55
  <div class="tree">
26
- <organizeTree @handleChange="getOrgList" ref="organizeTree"></organizeTree>
56
+ <organizeTree v-if="orgTreeLoaded" @handleChange="getOrgList" ref="orgTree" :treeList="orgTree" :is-custom-tree="true" :disable-lazy-load="false"></organizeTree>
27
57
  </div>
28
58
  <div class="bottom-select">
29
59
  <CheckboxGroup v-model="includeLevelOrg">
@@ -35,21 +65,58 @@
35
65
  </div>
36
66
  </div>
37
67
  </TabPane>
38
- <TabPane label="选择岗位" name="post">
68
+ <TabPane label="岗位选择" name="post" v-if="name.includes('post')">
39
69
  <div class="tab post">
40
- <div class="left">
41
- <div v-for="item in positiontList" :class="[item.checked&&'active']" :key="item.positionId" @click="getPosionId(item)">{{ item.positionName }}</div>
70
+ <div class="left" style="height: 450px; overflow: hidden; position: relative; z-index: 1;">
71
+ <div class="scroll-container">
72
+ <div class="position-item"
73
+ v-for="item in positiontList"
74
+ :class="[selectedPositionId === item.positionId ? 'active' : '']"
75
+ :key="item.positionId"
76
+ @click="getPosionId(item)">
77
+ {{ item.positionName }} <!-- 无需修改模板,仅通过样式实现自动换行 -->
78
+ </div>
79
+ </div>
42
80
  </div>
43
81
  <div class="right">
44
- <Input v-model="orgSearch" placeholder="搜索组织节点"/>
45
- <div class="tag-content">
82
+ <div class="search-row">
83
+ <span class="search-label">组织搜索:</span>
84
+ <div class="search-input-wrapper">
85
+ <Select v-model="postSearch" filterable :remote-method="getPostListBySearch" @on-change="getPostOption" placeholder="请输入组织名称" :loading="loadingPost" clearable @on-clear="handlePostSearchClear" style="width: 100%;">
86
+ <Option v-for="(option, index) in postSearchList" :value="option.orgUnitId" :key="index">{{option.name}}</Option>
87
+ </Select>
88
+ </div>
89
+ </div>
90
+ <div class="tag-content" v-if="isQuickOpen && postTagList.length > 0">
46
91
  <span>快捷选择:</span>
47
- <div class="tag-list">
48
- <span class="tag" v-for="item in tagList" :key="item.key">{{item.v}}</span>
92
+ <div class="custom-select-wrapper">
93
+ <Select
94
+ v-model="selectedPostTagKey"
95
+ placeholder="请选择快捷标签"
96
+ transfer
97
+ clearable
98
+ @on-change="handlePostTagSelect"
99
+ @on-clear="handlePostTagClear"
100
+ style="width: 365px;"
101
+ :disabled="!postTagList.length"
102
+ >
103
+ <Option
104
+ v-for="item in postTagList"
105
+ :value="item.quickPickKey"
106
+ :key="item.quickPickKey"
107
+ :class="{ 'active-option': item.checked }"
108
+ >
109
+ {{ item.quickPickName }}
110
+ </Option>
111
+ <!-- 无数据提示 -->
112
+ <Option value="" v-if="!postTagList.length" disabled>
113
+ 暂无快捷标签
114
+ </Option>
115
+ </Select>
49
116
  </div>
50
117
  </div>
51
118
  <div class="tree">
52
- <organizeTree @handleChange="getPostList" ref="postTree"></organizeTree>
119
+ <organizeTree v-if="postTreeLoaded" @handleChange="getPostList" ref="postTree" :treeList="postTree" :is-custom-tree="true" :disable-lazy-load="false"></organizeTree>
53
120
  </div>
54
121
  <div class="bottom-select">
55
122
  <CheckboxGroup v-model="includeLevelPost">
@@ -62,32 +129,127 @@
62
129
  </div>
63
130
  </div>
64
131
  </TabPane>
65
- <TabPane label="选择人员" name="staff">
66
- <div class="tab">
67
- <Input v-model="staffSearch" @on-blur="queryAllStaffList" placeholder="搜索组织节点"/>
68
- <div class="tree staff-content">
69
- <div :class="['gust-item',item.checked && 'staff-active']" v-for="item in staffAllList" :key="item.id" @click="handlestaff(item)">
70
- <div class="left-panel">{{item.name && item.name.slice(0,1) || ''}}</div>
71
- <div class="right-panel">
72
- <p>{{item.name}}</p>
73
- <p>{{item.orgNodeName}}</p>
132
+ <TabPane label="直接选择人员" name="staff" v-if="name.includes('staff')">
133
+ <div class="tab staff-left-right-layout">
134
+ <!-- 左侧:组织选择区域(限制高度) -->
135
+ <div class="staff-left-panel">
136
+ <div class="panel-title">组织选择</div>
137
+ <div class="tree staff-org-tree">
138
+ <div class="tree-scroll-force">
139
+ <organizeTree
140
+ v-if="staffTreeLoaded"
141
+ @handleChange="getStaffList"
142
+ ref="staffTree"
143
+ :treeList="staffTree"
144
+ :showCheckbox="true"
145
+ :checkStrictly="true"
146
+ :autoExpandParent="true"
147
+ :isSingleSelect="true"
148
+ :default-org-unit-id="defaultOrgUnitId"
149
+ :is-custom-tree="true"
150
+ :disable-lazy-load="false"
151
+ ></organizeTree>
74
152
  </div>
75
- <div class="checked-icon" v-show="item.checked">✔</div>
76
153
  </div>
77
154
  </div>
78
- <div class="bottom-select">
79
- <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>
80
- <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>
155
+ <div class="staff-right-panel">
156
+ <Input v-model="staffSearch" @on-enter="searchStaff" @on-search="searchStaff" search placeholder="搜索人员"/>
157
+ <div style="position:relative;">
158
+ <div class="tree staff-content" @scroll="handleScroll">
159
+ <div :class="['gust-item',item.checked && 'staff-active']"
160
+ v-for="(item, index) in staffAllList"
161
+ :key="`staff_${index}_${item.id}`"
162
+ @click="handlestaff(item)">
163
+ <!-- 移除 Checkbox 组件 -->
164
+ <div class="left-panel-pro">{{item.name && item.name.slice(0,1) || ''}}</div>
165
+ <div class="right-panel-pro">
166
+ <p>{{item.name}}</p>
167
+ <p>{{item.orgNodeName || item.orgUnitName}}</p>
168
+ </div>
169
+ </div>
170
+ <p v-if="staffEnding" style="color:#CCCCCC;text-align: center">---我也是有底线的---</p>
171
+ </div>
172
+ <Spin v-if="loadingStaff" size="large" fix />
173
+ </div>
174
+ <!-- <div class="bottom-select">-->
175
+ <!-- <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>-->
176
+ <!-- <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>-->
177
+ <!-- </div>-->
178
+ </div>
179
+ </div>
180
+ </TabPane>
181
+ <TabPane label="组织选择" name="orgNew" v-if="name.includes('orgNew')">
182
+ <div class="tab org-new-tab">
183
+ <div class="org-new-header">
184
+ <Input v-model="orgNewSearch" @on-enter="searchOrgNew" @on-search="searchOrgNew" search placeholder="搜索组织节点名称(如:办公室)" style="width: 100%;"/>
185
+ <div class="org-new-actions">
186
+ <Button type="primary" ghost @click="expandAllOrgNew">展开全部</Button>
187
+ <Button type="primary" ghost @click="collapseAllOrgNew">折叠全部</Button>
188
+ <!-- <Button type="default" @click="clearOrgNew">清空</Button>-->
189
+ </div>
190
+ </div>
191
+ <div class="org-new-content">
192
+ <div class="org-card" v-for="(province, index) in filteredOrgNewTree" :key="province.orgUnitId">
193
+ <div class="org-card-header" @click="toggleProvinceExpand(province)">
194
+ <div class="org-card-title" @click.stop="addOrgNewItem(province)">
195
+ <img src="./assets/icon.png" alt="" style="margin-right: 8px; width: 14px; height: 14px;"/>
196
+ {{ province.title }}
197
+ </div>
198
+ <div class="org-card-actions" v-if="province.children && province.children.length > 0">
199
+ <Button type="text" @click.stop="selectAllProvinceOrgNew(province)">全选(包含下级)</Button>
200
+ <Icon size="20" :type="province.expand ? 'md-arrow-dropdown' : 'md-arrow-dropright'" style="margin-left: 8px;"/>
201
+ </div>
202
+ </div>
203
+ <div class="org-card-body" v-if="province.expand && province.children && province.children.length > 0">
204
+ <div class="city-card" v-for="city in province.children" :key="city.orgUnitId">
205
+ <div class="city-card-header" @click="toggleCityExpand(city)">
206
+ <div class="city-card-title" @click.stop="addOrgNewItem(city)">
207
+ <img src="./assets/icon.png" alt="" style="margin-right: 8px; width: 14px; height: 14px;"/>
208
+ {{ city.title }}
209
+ </div>
210
+ <div class="city-card-actions" v-if="city.children && city.children.length > 0">
211
+ <Button type="text" @click.stop="selectAllCityOrgNew(city)">全选(包含下级)</Button>
212
+ <!-- <Button type="text" @click.stop="selectAllCityLevelOrgNew(city)">全选市本级</Button>-->
213
+ <Icon size="20" :type="city.expand ? 'md-arrow-dropdown' : 'md-arrow-dropright'" style="margin-left: 8px;"/>
214
+ </div>
215
+ </div>
216
+ <div class="city-card-body" v-if="city.expand && city.children && city.children.length > 0">
217
+ <div class="district-grid">
218
+ <div class="district-item" v-for="district in city.children" :key="district.orgUnitId">
219
+ <div class="district-header" @click="toggleDistrictExpand(district)">
220
+ <div class="district-title" @click.stop="addOrgNewItem(district)">
221
+ <img src="./assets/icon.png" alt="" style="margin-right: 8px; width: 14px; height: 14px;"/>
222
+ {{ district.title }}
223
+ </div>
224
+ <div class="district-actions" v-if="district.children && district.children.length > 0">
225
+ <Button type="text" @click.stop="selectAllDistrictOrgNew(district)">全选</Button>
226
+ <Icon size="20" :type="district.expand ? 'md-arrow-dropdown' : 'md-arrow-dropright'" style="margin-left: 8px;"/>
227
+ </div>
228
+ </div>
229
+ <div class="district-body" v-if="district.expand && district.children && district.children.length > 0">
230
+ <div class="org-unit-item" v-for="unit in district.children" :key="unit.orgUnitId">
231
+ <div class="unit-title" @click="addOrgNewItem(unit)">
232
+ <img src="./assets/icon.png" alt="" style="margin-right: 8px; width: 14px; height: 14px;"/>
233
+ {{ unit.title }}
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </div>
81
243
  </div>
82
244
  </div>
83
245
  </TabPane>
84
246
  </Tabs>
85
247
  </div>
86
248
  <div class="form-content">
87
- <p>已选择条件 <span class="num">({{getCheckTypenum}})</span></p>
249
+ <p v-if="name.length > 1">已选择条件 <span class="num">({{getCheckTypenum}})</span></p>
88
250
  <div class="node-list">
89
251
  <!-- 组织节点条件:-->
90
- <div>
252
+ <div v-if="name.includes('org')">
91
253
  <div class="group-box flex-r-bt">
92
254
  <div>组织节点条件:</div>
93
255
  <div class="clear-btn" @click="clearGroup">清除全部</div>
@@ -101,7 +263,7 @@
101
263
  </div>
102
264
  </div>
103
265
  <!-- 组织节点+岗位条件://-->
104
- <div>
266
+ <div v-if="name.includes('post')">
105
267
  <div class="group-box flex-r-bt">
106
268
  <div>岗位条件+组织节点:</div>
107
269
  <div class="clear-btn" @click="clearPost">清除全部</div>
@@ -115,17 +277,51 @@
115
277
  </div>
116
278
  </div>
117
279
  <!-- 直接选择人员:-->
118
- <div>
280
+ <div v-if="name.includes('staff')">
119
281
  <div class="group-box flex-r-bt">
120
- <div>直接选择人员:</div>
282
+ <div>已选择人员:</div>
121
283
  <div class="clear-btn" @click="clearStaff">清除全部</div>
122
284
  </div>
123
285
  <div class="group-list-content">
124
- <p class="list-item flex-r-bt" v-for="(item,index) in staffList" :key="item.id">
286
+ <p class="list-item flex-r-bt" v-for="(item,index) in staffList" :key="item.userId">
125
287
  <span>{{item.name}}</span>
126
288
  <img class="clear-icon" src="./assets/delete.png" alt="" @click="clearStaffByIndex(index)">
127
289
  </p>
128
- <p style="color:#CCCCCC" class="list-item" v-if="!staffList.length">未选择直接选择人员</p>
290
+ <p style="color:#CCCCCC" class="list-item" v-if="!staffList.length">未选择人员</p>
291
+ </div>
292
+ </div>
293
+ <!-- 组织选择(新):-->
294
+ <div v-if="name.includes('orgNew')">
295
+ <div class="group-box flex-r-bt">
296
+ <div>已选择组织:</div>
297
+ <div class="clear-btn" @click="clearOrgNewList">清除全部</div>
298
+ </div>
299
+ <div class="group-list-content">
300
+ <div v-if="groupedOrgNewList.length > 0">
301
+ <div v-for="group in groupedOrgNewList" :key="group.parentOrgUnitId" class="org-group">
302
+ <div class="org-group-title">
303
+ <Icon type="md-folder" class="folder-icon" />
304
+ <span>{{ group.parentOrgName }}</span>
305
+ <span class="org-group-count">({{ group.items.length }})</span>
306
+ </div>
307
+ <div v-for="item in group.items" class="org-group-items">
308
+ <!-- <Tooltip -->
309
+ <!-- v-for="item in group.items" -->
310
+ <!-- :key="item.id" -->
311
+ <!-- :content="item.fullPath || item.title" -->
312
+ <!-- placement="left"-->
313
+ <!-- :max-width="400"-->
314
+ <!-- transfer-->
315
+ <!-- >-->
316
+ <p class="list-item flex-r-bt">
317
+ <span class="org-item-name">{{ item.title }}</span>
318
+ <img class="clear-icon" src="./assets/delete.png" alt="" @click="clearOrgNewByIndex(item.originalIndex)">
319
+ </p>
320
+ <!-- </Tooltip>-->
321
+ </div>
322
+ </div>
323
+ </div>
324
+ <p style="color:#CCCCCC" class="list-item" v-else>未选择组织节点</p>
129
325
  </div>
130
326
  </div>
131
327
  </div>
@@ -147,16 +343,28 @@ export default {
147
343
  },
148
344
  data:{
149
345
  type:Object,
150
- default:{}
346
+ default:()=>{}
151
347
  },
152
348
  name:{
153
- value:String,
154
- default:"org"
349
+ type:Array ,
350
+ default(){return ['org','post','staff']}
351
+ },
352
+ isQuickOpen:{
353
+ type: Boolean,
354
+ default: false
355
+ },
356
+ defaultOrgUnitId:{
357
+ type: String,
358
+ default: ''
359
+ },
360
+ staffSingle:{
361
+ type: Boolean,
362
+ default: false
155
363
  }
156
364
  },
157
365
  data(){
158
366
  return {
159
- tabName:this.name,
367
+ tabName:this.name[0],
160
368
  modal:false,
161
369
  dialogOpen:false,
162
370
  orgSearch:'',
@@ -164,31 +372,408 @@ export default {
164
372
  proOrgList:[],
165
373
  includeLevelOrg:[],
166
374
  orgList:[],
375
+ loadingOrg:false,
376
+ orgSearchList:[],
377
+ orgTree:[],
167
378
  //岗位--form
379
+ postSearch:'',
168
380
  proPostList:[],
169
381
  postList:[],
382
+ loadingPost:false,
383
+ postSearchList:[],
170
384
  includeLevelPost:[],
171
385
  positiontList:[],
386
+ postTree:[],
172
387
  //直接人员
173
388
  staffSearch:'',
174
389
  staffAllList:[],
175
390
  staffList:[],
176
- tagList:[{
177
- v:'所选市公司所有部门',
178
- on:false,
179
- key:'07'
180
- },{
181
- v:'所有市公司',
182
- on:false,
183
- key:'08'
184
- }],
391
+ loadingStaff:false,
392
+ lastLoadingTime:0,
393
+ offset:0,
394
+ staffEnding:false,
395
+ parentOrgList: [],
396
+ orgTagList:[],
397
+ postTagList:[],
398
+ selectedPositionId: null, // 岗位单选的选中状态存储
399
+ selectedOrgTagKey: '', // 存储选中标签的唯一标识
400
+ selectedPostTagKey: '', // 存储选中标签的唯一标识
401
+ //人员选择
402
+ staffTree:[],
403
+ staffTagList:[],
404
+ staffOrgList:[],
405
+ staffSearchList:[],
406
+ selectedStaffOrgId: '', // 选中的组织节点ID
407
+ proStaffOrgList: [], // 暂存选中的组织节点
408
+ initialDefaultOrgUnitId: '', // 缓存初始defaultOrgUnitId
409
+ // staffTreeInited: false, // 组织树初始化完成标记
410
+ // 树加载完成标记
411
+ orgTreeLoaded: false,
412
+ postTreeLoaded: false,
413
+ staffTreeLoaded: false,
414
+ // 缓存原始组织树/岗位树数据
415
+ originalOrgTree: [], // 组织Tab原始树缓存
416
+ originalPostTree: [], // 岗位Tab原始树缓存
417
+ staffTreeInited: false, // 组织树初始化完成标记(用于需求3)
418
+ //组织选择(新)
419
+ orgNewSearch: '',
420
+ orgNewTree: [],
421
+ proOrgNewList: [],
422
+ includeLevelOrgNew: [],
423
+ orgNewList: []
185
424
  }
186
425
  },
187
426
  mounted() {
427
+ this.queryTagList()
188
428
  this.queryPositionList()
189
- this.queryAllStaffList()
429
+ // this.loadMore()
430
+ this.initialDefaultOrgUnitId = this.defaultOrgUnitId || ''; // 缓存初始值
431
+ this.initOrgOrgTree()
432
+ this.initPostOrgTree()
433
+ this.initStaffOrgTree()
434
+ this.initOrgNewTree()
190
435
  },
436
+
191
437
  methods:{
438
+ async initOrgOrgTree() {
439
+ try {
440
+ this.proOrgList = [];
441
+ this.orgSearch = '';
442
+ this.orgSearchList = [];
443
+ this.orgTreeLoaded = false;
444
+
445
+ const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
446
+ params: { orgUnitId: '' }
447
+ });
448
+
449
+ if (orgDetailRes.data.code !== 1 || !orgDetailRes.data.data) {
450
+ this.$Message.warning("未查询到组织根节点,显示空树");
451
+ this.orgTree = [];
452
+ this.originalOrgTree = []; // 缓存空数据
453
+ this.orgTreeLoaded = true;
454
+ return;
455
+ }
456
+
457
+ const rootOrgData = orgDetailRes.data.data;
458
+ const leafNode = await this.judgeNodeLeafe(rootOrgData.orgUnitId);
459
+ const rootNode = this.safeDeepCopy({
460
+ orgUnitId: rootOrgData.orgUnitId,
461
+ orgUnitName: rootOrgData.orgUnitName,
462
+ parentOrgUnitId: rootOrgData.parentOrgUnitId,
463
+ leafNode: leafNode,
464
+ title: rootOrgData.orgUnitName || `未命名组织(${rootOrgData.orgUnitId})`,
465
+ expand: true,
466
+ orgChildrenList: [],
467
+ children: []
468
+ });
469
+
470
+ let parentsList = [];
471
+ try {
472
+ parentsList = await this.getParentOrgNodesByOrgUnitId(rootOrgData.orgUnitId);
473
+ } catch (e) {
474
+ console.warn('父节点查询失败,仅显示根节点:', e);
475
+ parentsList = [];
476
+ }
477
+
478
+ const safeParents = this.safeDeepCopy(parentsList).map(node => ({
479
+ orgUnitId: node.orgUnitId,
480
+ orgUnitName: node.orgUnitName || node.name,
481
+ parentOrgUnitId: node.parentOrgUnitId || node.parentId,
482
+ leafNode: false,
483
+ title: node.orgUnitName || node.name || `未命名组织(${node.orgUnitId})`,
484
+ expand: true,
485
+ orgChildrenList: [],
486
+ children: []
487
+ }));
488
+
489
+ const treeSourceData = [...safeParents, rootNode].filter(Boolean);
490
+ this.orgTree = this.buildTree(treeSourceData);
491
+ // 缓存原始树数据
492
+ this.originalOrgTree = this.safeDeepCopy(this.orgTree);
493
+
494
+ if (this.orgTree.length === 0) {
495
+ this.orgTree = [rootNode];
496
+ this.originalOrgTree = [rootNode]; // 同步缓存
497
+ }
498
+
499
+ this.orgTreeLoaded = true;
500
+
501
+ } catch (e) {
502
+ console.error("组织选择Tab树初始化失败:", e);
503
+ this.orgTree = [];
504
+ this.originalOrgTree = []; // 缓存空数据
505
+ this.orgTreeLoaded = true;
506
+ this.$Message.error("组织树初始化失败,请刷新重试");
507
+ }
508
+ },
509
+ async initPostOrgTree() {
510
+ try {
511
+ this.proPostList = [];
512
+ this.postSearch = '';
513
+ this.postSearchList = [];
514
+ this.selectedPositionId = null;
515
+ this.postTreeLoaded = false;
516
+
517
+ const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
518
+ params: { orgUnitId: '' }
519
+ });
520
+
521
+ if (orgDetailRes.data.code !== 1 || !orgDetailRes.data.data) {
522
+ this.$Message.warning("未查询到组织根节点,显示空树");
523
+ this.postTree = [];
524
+ this.originalPostTree = []; // 缓存空数据
525
+ this.postTreeLoaded = true;
526
+ return;
527
+ }
528
+
529
+ const rootOrgData = orgDetailRes.data.data;
530
+ const leafNode = await this.judgeNodeLeafe(rootOrgData.orgUnitId);
531
+ const rootNode = this.safeDeepCopy({
532
+ orgUnitId: rootOrgData.orgUnitId,
533
+ orgUnitName: rootOrgData.orgUnitName,
534
+ parentOrgUnitId: rootOrgData.parentOrgUnitId,
535
+ leafNode: leafNode,
536
+ title: rootOrgData.orgUnitName || `未命名组织(${rootOrgData.orgUnitId})`,
537
+ expand: true,
538
+ orgChildrenList: [],
539
+ children: []
540
+ });
541
+
542
+ let parentsList = [];
543
+ try {
544
+ parentsList = await this.getParentOrgNodesByOrgUnitId(rootOrgData.orgUnitId);
545
+ } catch (e) {
546
+ console.warn('岗位Tab父节点查询失败:', e);
547
+ parentsList = [];
548
+ }
549
+
550
+ const safeParents = this.safeDeepCopy(parentsList).map(node => ({
551
+ orgUnitId: node.orgUnitId,
552
+ orgUnitName: node.orgUnitName || node.name,
553
+ parentOrgUnitId: node.parentOrgUnitId || node.parentId,
554
+ leafNode: false,
555
+ title: node.orgUnitName || node.name || `未命名组织(${node.orgUnitId})`,
556
+ expand: true,
557
+ orgChildrenList: [],
558
+ children: []
559
+ }));
560
+
561
+ const treeSourceData = [...safeParents, rootNode].filter(Boolean);
562
+ this.postTree = this.buildTree(treeSourceData);
563
+ // 缓存原始树数据
564
+ this.originalPostTree = this.safeDeepCopy(this.postTree);
565
+
566
+ if (this.postTree.length === 0) {
567
+ this.postTree = [rootNode];
568
+ this.originalPostTree = [rootNode]; // 同步缓存
569
+ }
570
+
571
+ this.postTreeLoaded = true;
572
+
573
+ } catch (e) {
574
+ console.error("岗位选择Tab树初始化失败:", e);
575
+ this.postTree = [];
576
+ this.originalPostTree = []; // 缓存空数据
577
+ this.postTreeLoaded = true;
578
+ this.$Message.error("岗位组织树初始化失败,请刷新重试");
579
+ }
580
+ },
581
+
582
+ async initStaffOrgTree() {
583
+ try {
584
+ // 初始化时重置选中状态
585
+ this.$set(this, 'selectedStaffOrgId', '');
586
+ this.proStaffOrgList = [];
587
+ this.staffTreeLoaded = false;
588
+ const rootOrgId = this.defaultOrgUnitId || '';
589
+ let rootNode = null;
590
+ let treeSourceData = []; // 存储树原始数据,避免后续覆盖
591
+
592
+ // if (rootOrgId) {
593
+ // const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
594
+ // params: { orgUnitId: rootOrgId }
595
+ // });
596
+ // if (orgDetailRes.data.code === 1 && orgDetailRes.data.data) {
597
+ // const orgData = orgDetailRes.data.data;
598
+ // const leafNode = await this.judgeNodeLeafe(rootOrgId);
599
+ // rootNode = this.safeDeepCopy({
600
+ // ...orgData,
601
+ // orgNodeName: orgData.orgUnitName || '',
602
+ // title: orgData.orgUnitName || `未命名组织(${orgData.orgUnitId || ''})`,
603
+ // leafNode,
604
+ // expand: true,
605
+ // parentOrgUnitId: orgData.parentOrgUnitId || orgData.parentId,
606
+ // orgChildrenList: [], // 初始化自定义子节点字段
607
+ // children: [] // 初始化 Tree 组件识别的 children 字段
608
+ // });
609
+ // // 父节点列表同样同步初始化 children 字段
610
+ // let parentsList = await this.getParentOrgNodesByOrgUnitId(rootOrgId);
611
+ // const safeParents = this.safeDeepCopy(parentsList);
612
+ // safeParents.forEach(node => {
613
+ // node.expand = true;
614
+ // node.parentOrgUnitId = node.parentId;
615
+ // node.orgNodeName = node.orgUnitName || node.orgNodeName || '';
616
+ // node.title = node.orgUnitName || node.name || `未命名组织(${node.orgUnitId || ''})`;
617
+ // node.orgChildrenList = [];
618
+ // node.children = []; // 父节点也初始化 children 字段
619
+ // node.leafNode = false;
620
+ // });
621
+ // treeSourceData = [...safeParents, rootNode];
622
+ // } else {
623
+ // this.$Message.error("根据默认组织ID查询根节点失败!");
624
+ // return;
625
+ // }
626
+ // } else {
627
+ // // defaultOrgUnitId为空时,沿用原有接口/pub/personHelpBox/q/getOrgUnitList
628
+ // const res = await ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitList', {
629
+ // params: { containsCurLevel: true }
630
+ // });
631
+ // if (res.data.code === 1) {
632
+ // rootNode = res.data.data[0] || null;
633
+ // if (rootNode) {
634
+ // rootNode.expand = true;
635
+ // rootNode.parentOrgUnitId = rootNode.parentId;
636
+ // treeSourceData = [rootNode];
637
+ // }
638
+ // }
639
+ // }
640
+ const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
641
+ params: { orgUnitId: rootOrgId }
642
+ });
643
+ if (orgDetailRes.data.code === 1 && orgDetailRes.data.data) {
644
+ const orgData = orgDetailRes.data.data;
645
+ const leafNode = await this.judgeNodeLeafe(rootOrgId);
646
+ rootNode = this.safeDeepCopy({
647
+ ...orgData,
648
+ orgNodeName: orgData.orgUnitName || '',
649
+ title: orgData.orgUnitName || `未命名组织(${orgData.orgUnitId || ''})`,
650
+ leafNode,
651
+ expand: true,
652
+ parentOrgUnitId: orgData.parentOrgUnitId || orgData.parentId,
653
+ orgChildrenList: [], // 初始化自定义子节点字段
654
+ children: [] // 初始化 Tree 组件识别的 children 字段
655
+ });
656
+ // 父节点列表同样同步初始化 children 字段
657
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(rootOrgId);
658
+ const safeParents = this.safeDeepCopy(parentsList);
659
+ safeParents.forEach(node => {
660
+ node.expand = true;
661
+ node.parentOrgUnitId = node.parentId;
662
+ node.orgNodeName = node.orgUnitName || node.orgNodeName || '';
663
+ node.title = node.orgUnitName || node.name || `未命名组织(${node.orgUnitId || ''})`;
664
+ node.orgChildrenList = [];
665
+ node.children = []; // 父节点也初始化 children 字段
666
+ node.leafNode = false;
667
+ });
668
+ treeSourceData = [...safeParents, rootNode];
669
+ } else {
670
+ this.$Message.error("根据默认组织ID查询根节点失败!");
671
+ this.staffTreeLoaded = true;
672
+ return;
673
+ }
674
+ // 构建组织树,且仅赋值一次,避免后续覆盖
675
+ if (treeSourceData.length > 0) {
676
+ const tree = this.buildTree(treeSourceData);
677
+ this.staffTree = this.safeDeepCopy(tree);
678
+ // 标记树已初始化完成,防止后续重复构建覆盖
679
+ // this.staffTreeInited = true;
680
+ this.staffTreeLoaded = true;
681
+ }
682
+ } catch (e) {
683
+ this.$Message.error("人员选择组织树初始化失败!");
684
+ console.error(e);
685
+ this.staffTreeLoaded = true;
686
+ }
687
+ },
688
+ handlePostTagSelect(quickPickKey) {
689
+ // 清空暂存列表
690
+ this.proPostList = [];
691
+ // 找到选中的标签对象
692
+ const selectedItem = this.postTagList.find(
693
+ item => item.quickPickKey === quickPickKey
694
+ );
695
+
696
+ if (selectedItem) {
697
+ // 重置其他标签的选中状态
698
+ this.postTagList.forEach(tag => tag.checked = false);
699
+ // 触发原有的处理方法
700
+ this.fastChedkPost(selectedItem);
701
+ // 清空搜索框绑定的值
702
+ this.postSearch = '';
703
+ // 清空搜索结果列表
704
+ this.postSearchList = [];
705
+ }
706
+ },
707
+ handleOrgTagSelect(quickPickKey) {
708
+ // 清空暂存列表
709
+ this.proOrgList = [];
710
+ // 找到选中的标签对象
711
+ const selectedItem = this.orgTagList.find(
712
+ item => item.quickPickKey === quickPickKey
713
+ );
714
+
715
+ if (selectedItem) {
716
+ // 重置其他标签的选中状态
717
+ this.orgTagList.forEach(tag => tag.checked = false);
718
+ // 触发原有的处理方法
719
+ this.fastChedkOrg(selectedItem);
720
+ // 清空搜索框绑定的值
721
+ this.orgSearch = '';
722
+ // 清空搜索结果列表
723
+ this.orgSearchList = [];
724
+ }
725
+ },
726
+ handleOrgTagClear() {
727
+ this.selectedOrgTagKey = '';
728
+ // 重置所有标签的选中状态
729
+ this.orgTagList.forEach(tag => {
730
+ tag.checked = false;
731
+ });
732
+
733
+ // 3. 清空树选择状态和暂存列表
734
+ if (this.$refs.orgTree && this.orgSearchList) {
735
+ this.$refs.orgTree.initData();
736
+ }
737
+ // 4. 可选:添加清空提示
738
+ this.$Message.info("已清空快捷标签选择");
739
+ },
740
+ // 修正清空逻辑方法(与事件名称对应)
741
+ handlePostTagClear() {
742
+ // 1. 重置选中的标签key
743
+ this.selectedPostTagKey = '';
744
+
745
+ // 2. 重置所有标签的选中状态
746
+ this.postTagList.forEach(tag => {
747
+ tag.checked = false;
748
+ });
749
+
750
+ // 3. 清空树选择状态和暂存列表
751
+ if (this.$refs.postTree) {
752
+ this.$refs.postTree.initData();
753
+ this.proPostList = [];
754
+ }
755
+
756
+ // 4. 可选:添加清空提示
757
+ this.$Message.info("已清空快捷标签选择");
758
+ },
759
+ handlePostSearchClear() {
760
+ // 清空搜索框绑定的值
761
+ this.postSearch = '';
762
+
763
+ // 清空搜索结果列表
764
+ this.postSearchList = [];
765
+
766
+ // 重置树组件状态(与原有逻辑保持一致)
767
+ if (this.$refs.postTree) {
768
+ this.$refs.postTree.initData();
769
+ }
770
+
771
+ // 清空暂存列表
772
+ this.proPostList = [];
773
+
774
+ // 可选:添加清空提示
775
+ this.$Message.info("已清空组织选择搜索");
776
+ },
192
777
  queryPositionList(){
193
778
  ajax.get('/pub-manage-server/pub/personHelpBox/q/queryPositionList').then((res)=>{
194
779
  if(res.data.code === 1){
@@ -198,38 +783,487 @@ export default {
198
783
  }
199
784
  })
200
785
  },
201
- queryAllStaffList(){
202
- ajax.get('/pub-manage-server/pub/personHelpBox/q/queryAllStaffList?search='+this.staffSearch).then((res)=>{
203
- if(res.data.code === 1){
204
- let staffAllList = res.data.data && res.data.data.rows || []
205
- staffAllList.map((item)=>item.checked=false)
206
- this.staffAllList = staffAllList
786
+ getOrgListBySearch(query){
787
+ if (query !== '') {
788
+ this.loadingOrg = true;
789
+ this.getOrgUnitBySearchTerm(query,(res)=>{
790
+ this.loadingOrg = false
791
+ if(res.data.code === 1){
792
+ let resp = res.data.data?.items??[]
793
+ this.orgSearchList = resp
794
+ }else{
795
+ this.orgSearchList = []
796
+ this.$Message.error("获取组织节点列表失败!")
797
+ }
798
+ })
799
+ }else{
800
+ this.orgSearchList = [];
801
+ }
802
+ },
803
+ getPostListBySearch(query) {
804
+ if (query !== '') {
805
+ this.loadingPost = true;
806
+ this.getOrgUnitBySearchTerm(query, (res) => {
807
+ this.loadingPost = false;
808
+ if (res.data.code === 1) {
809
+ let resp = res.data.data?.items ?? [];
810
+ this.postSearchList = this.safeDeepCopy(resp);
811
+ } else {
812
+ this.postSearchList = [];
813
+ this.$Message.error("获取组织节点列表失败!");
814
+ }
815
+ });
816
+ } else {
817
+ // 仅清空搜索结果,不重置树数据
818
+ this.postSearchList = [];
819
+ // 仅重置选中状态,保留树结构
820
+ if (this.$refs.postTree) {
821
+ this.$refs.postTree.initData(); // 清空选中状态
822
+ this.proPostList = []; // 清空暂存列表
207
823
  }
208
- })
824
+ // 移除清空树数据的代码
825
+ // this.$set(this, 'postTree', []);
826
+ }
827
+ },
828
+ getOrgUnitBySearchTerm(query,callback){
829
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitBySearchTerm?searchTerm='+query).then((res)=>{callback(res)})
830
+ },
831
+ async getOrgOption(val) {
832
+ this.selectedOrgTagKey = '';
833
+ this.orgTagList.forEach(tag => tag.checked = false);
834
+ this.proOrgList = [];
835
+ if (!val) {
836
+ // 清空搜索时恢复原始树数据,而非置空
837
+ this.orgTree = this.safeDeepCopy(this.originalOrgTree);
838
+ // 同步更新子组件数据
839
+ this.$nextTick(() => {
840
+ if (this.$refs.orgTree) {
841
+ this.$refs.orgTree.data = this.safeDeepCopy(this.orgTree);
842
+ // 重置树选中状态,但保留树结构
843
+ this.$refs.orgTree.initData();
844
+ }
845
+ });
846
+ return;
847
+ }
848
+ const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
849
+ params: { orgUnitId: val }
850
+ });
851
+
852
+ if (orgDetailRes.data.code !== 1 || !orgDetailRes.data.data) {
853
+ this.$Message.error("获取组织节点详情失败!");
854
+ return;
855
+ }
856
+
857
+ const nodeData = orgDetailRes.data.data;
858
+ const currentNode = this.safeDeepCopy({
859
+ orgUnitId: nodeData.orgUnitId,
860
+ orgUnitName: nodeData.orgUnitName,
861
+ parentOrgUnitId: nodeData.parentOrgUnitId,
862
+ leafNode: nodeData.leafNode || await this.judgeNodeLeafe(nodeData.orgUnitId),
863
+ title: nodeData.orgUnitName || `未命名组织(${nodeData.orgUnitId})`,
864
+ expand: true,
865
+ orgChildrenList: [],
866
+ children: []
867
+ });
868
+
869
+ try {
870
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
871
+ const safeParents = this.safeDeepCopy(parentsList).map(node => ({
872
+ orgUnitId: node.orgUnitId,
873
+ orgUnitName: node.orgUnitName || node.name,
874
+ parentOrgUnitId: node.parentOrgUnitId || node.parentId,
875
+ leafNode: false,
876
+ title: node.orgUnitName || node.name || `未命名组织(${node.orgUnitId})`,
877
+ expand: true,
878
+ orgChildrenList: [],
879
+ children: []
880
+ }));
881
+
882
+ const treeSourceData = [...safeParents, currentNode].filter(Boolean);
883
+ this.orgTree = this.buildTree(treeSourceData);
884
+
885
+ // 强制更新子组件数据
886
+ this.$nextTick(() => {
887
+ if (this.$refs.orgTree) {
888
+ this.$refs.orgTree.data = this.safeDeepCopy(this.orgTree);
889
+ }
890
+ });
891
+ } catch (e) {
892
+ this.$Message.error("更新组织树失败!");
893
+ }
894
+ },
895
+ async getPostOption(val) {
896
+ this.selectedPostTagKey = '';
897
+ this.postTagList.forEach(tag => tag.checked = false);
898
+
899
+ if (!val) {
900
+ // 清空搜索时恢复原始树数据
901
+ this.postTree = this.safeDeepCopy(this.originalPostTree);
902
+ this.proPostList = [];
903
+ this.postSearch = '';
904
+ // 同步更新子组件数据
905
+ this.$nextTick(() => {
906
+ if (this.$refs.postTree) {
907
+ this.$refs.postTree.data = this.safeDeepCopy(this.postTree);
908
+ // 重置树选中状态,但保留树结构
909
+ this.$refs.postTree.initData();
910
+ }
911
+ });
912
+ return;
913
+ }
914
+
915
+ // 原有逻辑保持不变
916
+ const orgDetailRes = await ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
917
+ params: { orgUnitId: val }
918
+ });
919
+
920
+ if (orgDetailRes.data.code !== 1 || !orgDetailRes.data.data) {
921
+ this.$Message.error("获取组织节点详情失败!");
922
+ return;
923
+ }
924
+
925
+ const nodeData = orgDetailRes.data.data;
926
+ const currentNode = this.safeDeepCopy({
927
+ orgUnitId: nodeData.orgUnitId,
928
+ orgUnitName: nodeData.orgUnitName,
929
+ parentOrgUnitId: nodeData.parentOrgUnitId,
930
+ leafNode: nodeData.leafNode || await this.judgeNodeLeafe(nodeData.orgUnitId),
931
+ title: nodeData.orgUnitName || `未命名组织(${nodeData.orgUnitId})`,
932
+ expand: true,
933
+ orgChildrenList: [],
934
+ children: []
935
+ });
936
+
937
+ try {
938
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
939
+ const safeParents = this.safeDeepCopy(parentsList).map(node => ({
940
+ orgUnitId: node.orgUnitId,
941
+ orgUnitName: node.orgUnitName || node.name,
942
+ parentOrgUnitId: node.parentOrgUnitId || node.parentId,
943
+ leafNode: false,
944
+ title: node.orgUnitName || node.name || `未命名组织(${node.orgUnitId})`,
945
+ expand: true,
946
+ orgChildrenList: [],
947
+ children: []
948
+ }));
949
+
950
+ const treeSourceData = [...safeParents, currentNode].filter(Boolean);
951
+ this.postTree = this.buildTree(treeSourceData);
952
+
953
+ this.$nextTick(() => {
954
+ if (this.$refs.postTree && this.$refs.postTree.setCheckedNodes) {
955
+ this.$refs.postTree.setCheckedNodes([val]);
956
+ this.proPostList = [currentNode];
957
+ }
958
+ // 强制更新子组件数据
959
+ if (this.$refs.postTree) {
960
+ this.$refs.postTree.data = this.safeDeepCopy(this.postTree);
961
+ }
962
+ });
963
+ } catch (e) {
964
+ this.$Message.error("更新岗位组织树失败!");
965
+ }
209
966
  },
210
- getOrgList(data){
211
- this.proOrgList = data
967
+ async getStaffOption(val) {
968
+ this.staffOrgList = [];
969
+ if(!val) return this.$refs.staffTree.initData();
970
+ let item = this.staffSearchList.filter((item)=> item.orgUnitId === val).shift();
971
+ if (!item) return;
972
+
973
+ const leafNode = await this.judgeNodeLeafe(item.orgUnitId);
974
+ const ftem = this.safeDeepCopy({
975
+ ...item,
976
+ orgNodeName: item.name,
977
+ parentOrgUnitId: item.parentId,
978
+ leafNode: leafNode
979
+ });
980
+
981
+ try{
982
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
983
+ // 对父节点列表进行安全拷贝
984
+ const safeParents = this.safeDeepCopy(parentsList);
985
+ let tree = this.buildTree([...safeParents, ftem]);
986
+ this.staffTree = this.safeDeepCopy(tree);
987
+ }catch(e){
988
+ this.$Message.error("获取组织节点列表失败!");
989
+ }
212
990
  },
213
- getPostList(data){
214
- this.proPostList = data
991
+ getParentOrgNodesByOrgUnitId(val) {
992
+ return new Promise((resolve) => {
993
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/getParentOrgNodesByOrgUnitId?orgUnitId='+val)
994
+ .then((res) => {
995
+ if (res.data.code === 1) {
996
+ const parentsList = res.data?.data?.items ?? [];
997
+ resolve(parentsList); // 接口正常返回则取数据
998
+ } else {
999
+ resolve([]); // 接口报错返回空数组
1000
+ }
1001
+ })
1002
+ .catch(() => {
1003
+ resolve([]); // 网络/请求失败返回空数组
1004
+ });
1005
+ });
215
1006
  },
216
- addOrgList(){
217
- if(!this.proOrgList.length) return this.$Message.error("请先选择组织节点!")
218
- let proOrgList = deepCopy(this.proOrgList)
219
- proOrgList.forEach(item=>{
220
- item.title = this.includeLevelOrg.includes('01')?`${item.orgNodeName}(包含下级组织节点)`:item.orgNodeName
221
- item.id = this.includeLevelOrg.includes('01')?('01'+'-' + item.orgUnitId):('00'+'-'+item.orgUnitId)
222
- item.includeLevel = this.includeLevelOrg.includes('01')
223
- })
224
- let list = this.orgList.concat(proOrgList)
1007
+ judgeNodeLeafe(orgUnitId) {
1008
+ return new Promise((resolve) => {
1009
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitList', {
1010
+ params: {
1011
+ containsCurLevel: true,
1012
+ orgUnitId: orgUnitId,
1013
+ }
1014
+ }).then((res) => {
1015
+ if (res.data.code === 1) {
1016
+ const treeList = res.data.data || [];
1017
+ resolve(treeList.length === 0);
1018
+ } else {
1019
+ resolve(false); // 接口失败默认非叶子节点
1020
+ }
1021
+ }).catch(() => {
1022
+ resolve(false); // 网络错误默认非叶子节点
1023
+ });
1024
+ });
1025
+ },
1026
+ buildTree(items) {
1027
+ if (!Array.isArray(items) || items.length === 0) {
1028
+ console.warn('构建树入参为空,返回空数组');
1029
+ return [];
1030
+ }
1031
+
1032
+ const map = new Map();
1033
+ const roots = [];
1034
+ const copiedItems = this.safeDeepCopy(items);
1035
+
1036
+ copiedItems.forEach(item => {
1037
+ if (!item.orgUnitId) return;
1038
+ map.set(item.orgUnitId, this.safeDeepCopy({
1039
+ orgUnitId: item.orgUnitId,
1040
+ orgUnitName: item.orgUnitName,
1041
+ parentOrgUnitId: item.parentOrgUnitId,
1042
+ leafNode: item.leafNode || false,
1043
+ title: item.title || item.orgUnitName || `未命名组织(${item.orgUnitId})`,
1044
+ expand: item.expand || true, // 保留展开状态
1045
+ orgChildrenList: item.orgChildrenList || [],
1046
+ children: item.children || []
1047
+ }));
1048
+ });
1049
+
1050
+ copiedItems.forEach(item => {
1051
+ if (!item.orgUnitId || !map.has(item.orgUnitId)) return;
1052
+
1053
+ const node = map.get(item.orgUnitId);
1054
+ if (!item.parentOrgUnitId || !map.has(item.parentOrgUnitId)) {
1055
+ roots.push(node);
1056
+ } else {
1057
+ const parent = map.get(item.parentOrgUnitId);
1058
+ parent.orgChildrenList.push(node);
1059
+ parent.children.push(node);
1060
+ parent.leafNode = false; // 有子节点则强制非叶子节点
1061
+ parent.expand = true; // 有子节点则强制展开
1062
+ }
1063
+ });
1064
+
1065
+ if (roots.length === 0 && map.size > 0) {
1066
+ roots.push(Array.from(map.values())[0]);
1067
+ }
1068
+
1069
+ return roots;
1070
+ },
1071
+
1072
+ searchStaff(){
1073
+ this.staffEnding = false;
1074
+ this.offset = 0;
1075
+ this.staffAllList = [];
1076
+ // 50ms延迟,解决极端情况下的异步更新问题
1077
+ setTimeout(() => {
1078
+ this.loadMore();
1079
+ }, 50);
1080
+ },
1081
+ queryAllStaffList() {
1082
+ const params = {
1083
+ search: this.staffSearch,
1084
+ offset: this.offset,
1085
+ limit: 20
1086
+ };
1087
+
1088
+ if (this.selectedStaffOrgId && typeof this.selectedStaffOrgId === 'string' && this.selectedStaffOrgId.trim()) {
1089
+ params.orgUnitId = this.selectedStaffOrgId.trim();
1090
+ } else if (this.initialDefaultOrgUnitId && this.initialDefaultOrgUnitId.trim()) {
1091
+ // 初始化及未选中节点时,使用传入的defaultOrgUnitId
1092
+ params.orgUnitId = this.initialDefaultOrgUnitId.trim();
1093
+ } else {
1094
+ params.orgUnitId = '';
1095
+ }
1096
+
1097
+ console.log('人员查询接口参数:', params);
1098
+
1099
+ return new Promise((resolve, reject) => {
1100
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/queryAllStaffList', {
1101
+ params: params
1102
+ }).then((res) => {
1103
+ if (res.data.code === 1) {
1104
+ resolve(res.data.data);
1105
+ } else {
1106
+ reject(false);
1107
+ }
1108
+ }).catch((err) => {
1109
+ reject(err);
1110
+ });
1111
+ });
1112
+ },
1113
+ handleScroll(e){
1114
+ const { scrollTop, clientHeight, scrollHeight } = e.target;
1115
+ if (scrollHeight - (scrollTop + clientHeight) < 50) {
1116
+ this.loadMore();
1117
+ }
1118
+ },
1119
+ async loadMore(){
1120
+ if (this.loadingStaff || this.staffEnding) return
1121
+ this.loadingStaff = true
1122
+ try {
1123
+ console.log("--触底加载/强制加载---", new Date().getTime());
1124
+ let res = await this.queryAllStaffList()
1125
+ let list = res.rows;
1126
+ // 移除这行:list.map((item)=>item.checked=false)
1127
+ this.staffAllList = this.staffAllList.concat(list)
1128
+ this.offset += 20;
1129
+ this.loadingStaff = false
1130
+ this.lastLoadingTime = Date.now()
1131
+ if(res.total===this.staffAllList.length){
1132
+ this.staffEnding = true
1133
+ }
1134
+ }catch (e){
1135
+ this.loadingStaff = false
1136
+ this.lastLoadingTime = Date.now()
1137
+ console.log(e)
1138
+ }
1139
+ },
1140
+ getOrgList(data) {
1141
+ const validNodes = Array.isArray(data)
1142
+ ? data.filter(node => node.orgUnitId)
1143
+ : [];
1144
+
1145
+ // 去重:基于orgUnitId避免重复
1146
+ const uniqueNodes = validNodes.filter((node, index, self) =>
1147
+ self.findIndex(item => item.orgUnitId === node.orgUnitId) === index
1148
+ );
1149
+
1150
+
1151
+ const completeNodes = uniqueNodes.map(node => ({
1152
+ ...node,
1153
+ orgNodeName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`,
1154
+ orgUnitName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`
1155
+ }));
1156
+
1157
+ this.proOrgList = this.safeDeepCopy(completeNodes);
1158
+ },
1159
+
1160
+ getPostList(data) {
1161
+ // 过滤有效节点:仅要求orgUnitId
1162
+ const validNodes = Array.isArray(data)
1163
+ ? data.filter(node => node.orgUnitId)
1164
+ : [];
1165
+
1166
+ // 去重:基于orgUnitId
1167
+ const uniqueNodes = validNodes.filter((node, index, self) =>
1168
+ self.findIndex(item => item.orgUnitId === node.orgUnitId) === index
1169
+ );
1170
+
1171
+ // 补全缺失字段
1172
+ const completeNodes = uniqueNodes.map(node => ({
1173
+ ...node,
1174
+ orgNodeName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`,
1175
+ orgUnitName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`
1176
+ }));
1177
+
1178
+ // 覆盖暂存列表(而非concat,避免累积无效数据)
1179
+ this.proPostList = this.safeDeepCopy(completeNodes);
1180
+ },
1181
+ getStaffList(data) {
1182
+ if (!Array.isArray(data) || data.length === 0) {
1183
+ this.proStaffOrgList = [];
1184
+ // 强制清空并触发响应式更新
1185
+ this.$set(this, 'selectedStaffOrgId', '');
1186
+ // 立即触发搜索,确保orgUnitId参数实时更新为空
1187
+ this.$nextTick(() => this.searchStaff());
1188
+ return;
1189
+ }
1190
+
1191
+ // 过滤仅含有效orgUnitId的节点,排除无效数据
1192
+ const validNodes = data.filter(node =>
1193
+ node.orgUnitId && typeof node.orgUnitId === 'string' // 确保orgUnitId存在且为字符串
1194
+ );
1195
+
1196
+ if (validNodes.length === 0) {
1197
+ this.$Message.warning("所选节点无效,请重新选择");
1198
+ this.proStaffOrgList = [];
1199
+ this.$set(this, 'selectedStaffOrgId', '');
1200
+ // 立即触发搜索,更新参数为空
1201
+ this.$nextTick(() => this.searchStaff());
1202
+ return;
1203
+ }
1204
+
1205
+ // 单选逻辑强化,仅保留最后一个有效选中节点
1206
+ const currentNode = validNodes[validNodes.length - 1];
1207
+ const currentOrgUnitId = currentNode.orgUnitId.trim(); // 去除空格,避免无效字符串
1208
+
1209
+ // 补全节点字段,确保orgUnitId绝对存在
1210
+ const pureNode = this.safeDeepCopy({
1211
+ ...currentNode,
1212
+ orgNodeName: currentNode.orgNodeName || currentNode.name || `未命名组织(${currentOrgUnitId})`,
1213
+ orgUnitName: currentNode.orgNodeName || currentNode.name || `未命名组织(${currentOrgUnitId})`
1214
+ });
1215
+
1216
+ // 清除子节点引用,避免循环引用导致的更新延迟
1217
+ if (pureNode.orgChildrenList) pureNode.orgChildrenList = [];
1218
+ if (pureNode.children) pureNode.children = [];
1219
+
1220
+ // 强制响应式更新,避免Vue数据延迟
1221
+ this.$set(this, 'proStaffOrgList', [pureNode]);
1222
+ this.$set(this, 'selectedStaffOrgId', currentOrgUnitId);
1223
+
1224
+ // 双重nextTick确保数据完全更新后再查询
1225
+ this.$nextTick(() => {
1226
+ if (this.selectedStaffOrgId !== currentOrgUnitId) {
1227
+ this.$set(this, 'selectedStaffOrgId', currentOrgUnitId);
1228
+ this.$nextTick(() => this.searchStaff());
1229
+ } else {
1230
+ this.searchStaff();
1231
+ }
1232
+ console.log('人员选择-当前选中orgUnitId:', this.selectedStaffOrgId); // 调试日志
1233
+ });
1234
+ },
1235
+
1236
+ addOrgList() {
1237
+ // 过滤空节点(仅校验orgUnitId)
1238
+ const validOrgList = this.proOrgList.filter(node => node.orgUnitId);
1239
+ if (!validOrgList.length) return this.$Message.error("请先选择组织节点!");
1240
+
1241
+ let proOrgList = deepCopy(validOrgList);
1242
+ proOrgList.forEach(item => {
1243
+ // 用补全后的orgNodeName,避免空显示
1244
+ item.title = this.includeLevelOrg.includes('01')
1245
+ ? `${item.orgNodeName}(包含下级组织节点)`
1246
+ : item.orgNodeName;
1247
+ item.id = this.includeLevelOrg.includes('01')
1248
+ ? (`01-${item.orgUnitId}`)
1249
+ : (`00-${item.orgUnitId}`);
1250
+ item.includeLevel = this.includeLevelOrg.includes('01');
1251
+ });
1252
+
1253
+ // 去重合并
1254
+ let list = this.orgList.concat(proOrgList);
225
1255
  let uniqueArray = list.filter((item, index, self) =>
226
- index === self.findIndex(t => (
227
- t.id === item.id // 基于id属性去重
228
- ))
1256
+ index === self.findIndex(t => t.id === item.id)
229
1257
  );
230
- this.orgList = uniqueArray
231
- this.$refs.organizeTree.upDataTree()
232
- this.proOrgList = []
1258
+ this.orgList = uniqueArray;
1259
+
1260
+ // 清空选中状态
1261
+ if (this.$refs.orgTree && this.$refs.orgTree.clearAllChecked) {
1262
+ this.$refs.orgTree.clearAllChecked(this.$refs.orgTree.data);
1263
+ this.$refs.orgTree.$emit('handleChange', []);
1264
+ }
1265
+ this.$refs.orgTree.upDataTree();
1266
+ this.proOrgList = [];
233
1267
  },
234
1268
  clearGroup(){
235
1269
  this.orgList = []
@@ -239,36 +1273,47 @@ export default {
239
1273
  },
240
1274
  //岗位
241
1275
  getPosionId(item){
242
- item.checked = !item.checked;
243
- },
244
- addPostList(){
245
- if(!this.proPostList.length) return this.$Message.error("请选择组织节点!")
246
- let proPostList = deepCopy(this.proPostList)
247
- let checkedPosition = this.positiontList.filter((item)=>item.checked === true)
248
- if(!checkedPosition.length){return this.$Message.error("请选择岗位!!")}
249
- //summary data
250
- let totalList = []
251
- for(let it of checkedPosition){
252
- proPostList.forEach(item=>{
253
- totalList.push({
254
- ...item,
255
- ...it,
256
- title:`${this.includeLevelPost.length?(item.orgNodeName+'(包含下级组织节点)'):item.orgNodeName}`,
257
- includeLevel:this.includeLevelPost.includes('01'),
258
- id:this.includeLevelPost.includes('01')?('01'+'-'+it.positionId + '-' + item.orgUnitId):('00'+'-'+it.positionId + '-' + item.orgUnitId),
259
- })
260
- })
261
- }
262
- let list = this.postList.concat(totalList)
1276
+ // item.checked = !item.checked;
1277
+ this.selectedPositionId = item.positionId
1278
+ },
1279
+ addPostList() {
1280
+ // 过滤空节点
1281
+ const validPostList = this.proPostList.filter(node => node.orgUnitId);
1282
+ if (!validPostList.length) return this.$Message.error("请选择组织节点!");
1283
+
1284
+ let checkedPosition = this.positiontList.find(item => item.positionId === this.selectedPositionId);
1285
+ if (!checkedPosition) { return this.$Message.error("请选择岗位!"); }
1286
+
1287
+ let totalList = [];
1288
+ validPostList.forEach(item => {
1289
+ totalList.push({
1290
+ ...item,
1291
+ ...checkedPosition,
1292
+ title: `${this.includeLevelPost.length ? (item.orgNodeName + '(包含下级组织节点)') : item.orgNodeName}`,
1293
+ includeLevel: this.includeLevelPost.includes('01'),
1294
+ id: this.includeLevelPost.includes('01')
1295
+ ? (`01-${checkedPosition.positionId}-${item.orgUnitId}`)
1296
+ : (`00-${checkedPosition.positionId}-${item.orgUnitId}`),
1297
+ });
1298
+ });
1299
+
1300
+ // 去重合并
1301
+ let list = this.postList.concat(totalList);
263
1302
  let uniqueArray = list.filter((item, index, self) =>
264
- index === self.findIndex(t => (
265
- t.id === item.id // 基于id属性去重
266
- ))
1303
+ index === self.findIndex(t => t.id === item.id)
267
1304
  );
268
- this.postList = uniqueArray
269
- this.$refs.postTree.upDataTree()
270
- this.proPostList = []
271
- this.positiontList.map((item)=>{item.checked=false})
1305
+ this.postList = uniqueArray;
1306
+
1307
+ // 清空选中状态
1308
+ if (this.$refs.postTree && this.$refs.postTree.clearAllChecked) {
1309
+ this.$refs.postTree.clearAllChecked(this.$refs.postTree.data);
1310
+ this.$refs.postTree.$emit('handleChange', []);
1311
+ }
1312
+ this.$refs.postTree.upDataTree();
1313
+ this.proPostList = [];
1314
+ this.selectedPositionId = null;
1315
+ // this.$refs.postTree.initData();
1316
+ this.selectedPostTagKey = '';
272
1317
  },
273
1318
  clearPost(){
274
1319
  this.postList= []
@@ -277,19 +1322,50 @@ export default {
277
1322
  this.postList.splice(index,1)
278
1323
  },
279
1324
  //staff
280
- addStaffList(){
281
- let staffList = this.staffAllList.filter((item)=>item.checked===true)
282
- let list = this.staffList.concat(staffList)
283
- let uniqueArray = list.filter((item, index, self) =>
284
- index === self.findIndex(t => (
285
- t.id === item.id // 基于id属性去重
286
- ))
287
- );
288
- this.staffList = uniqueArray
289
- this.staffAllList.map((item)=>item.checked=false)
290
- },
1325
+ // addStaffList(){
1326
+ // console.log("this.staffSingle", this.staffSingle)
1327
+ // let staffList = this.staffAllList.filter((item)=>item.checked===true);
1328
+ //
1329
+ // if (!staffList.length) {
1330
+ // this.$Message.error("请选择人员");
1331
+ // return;
1332
+ // }
1333
+ // if (this.staffSingle && this.staffList.length >= 1) {
1334
+ // this.$Message.error("请先清除已选人员,再重新添加");
1335
+ // return;
1336
+ // }
1337
+ //
1338
+ // if(this.staffSingle && staffList.length > 1){
1339
+ // this.$Message.error("温馨提示:当前业务暂时只支持选择一位人员");
1340
+ // return;
1341
+ // }
1342
+ //
1343
+ // // 基于item.id去重,不修改原数据字段,仅过滤重复项
1344
+ // let uniqueStaffList = staffList.filter(newItem =>
1345
+ // // 检查右侧条件区域(staffList)是否已有该人员,避免重复添加
1346
+ // !this.staffList.some(existItem => existItem.userId === newItem.userId)
1347
+ // );
1348
+ // // 合并去重后的列表
1349
+ // this.staffList = this.staffList.concat(uniqueStaffList);
1350
+ // // 清空选中状态
1351
+ // this.staffAllList.forEach(item => item.checked = false);
1352
+ // },
291
1353
  handlestaff(item){
292
- item.checked=!item.checked
1354
+ // 处理单选逻辑
1355
+ if (this.staffSingle) {
1356
+ // 单选时先清空已选人员
1357
+ this.staffList = [];
1358
+ }
1359
+ // 去重校验:避免重复添加
1360
+ const isExist = this.staffList.some(existItem => existItem.userId === item.userId);
1361
+ if (!isExist) {
1362
+ // 深拷贝防止原数据引用污染
1363
+ const newItem = this.safeDeepCopy(item);
1364
+ this.staffList.push(newItem);
1365
+ this.$Message.success(`已添加【${item.name}】`);
1366
+ } else {
1367
+ this.$Message.warning(`【${item.name}】已在选择列表中`);
1368
+ }
293
1369
  },
294
1370
  clearStaff(){
295
1371
  this.staffList = []
@@ -297,27 +1373,1059 @@ export default {
297
1373
  clearStaffByIndex(index){
298
1374
  this.staffList.splice(index,1)
299
1375
  },
1376
+ clearOrgNewList(){
1377
+ this.orgNewList = []
1378
+ },
1379
+ clearOrgNewByIndex(index){
1380
+ this.orgNewList.splice(index,1)
1381
+ },
300
1382
  confirm(){
1383
+ const cleanOrgNewList = this.orgNewList.map(item => ({
1384
+ id: item.id,
1385
+ orgUnitId: item.orgUnitId,
1386
+ orgNodeName: item.orgNodeName || item.title,
1387
+ title: item.title,
1388
+ includeLevel: item.includeLevel,
1389
+ parentOrgUnitId: item.parentOrgUnitId
1390
+ }))
301
1391
  const v = {
302
1392
  orgList:this.orgList,
303
1393
  postList:this.postList,
304
- staffList:this.staffList
1394
+ staffList:this.staffList,
1395
+ orgNewList:cleanOrgNewList
305
1396
  }
306
1397
  this.$emit('confirm',deepCopy(v))
307
1398
  },
308
- visibleChange(val){
309
- this.$emit('input',val);
1399
+ visibleChange(val) {
1400
+ this.$emit('input', val);
1401
+ if (val) {
1402
+ this.resetStaffTreeChecked();
1403
+ this.staffEnding = false;
1404
+ this.offset = 0;
1405
+ this.staffAllList = [];
1406
+ this.loadingStaff = false;
1407
+ if (this.tabName === 'staff') {
1408
+ this.loadMore().catch(err => console.log("弹窗打开加载失败:", err));
1409
+ this.$nextTick(() => {
1410
+ setTimeout(() => {
1411
+ this.loadMore();
1412
+ }, 100);
1413
+ });
1414
+ }
1415
+ } else {
1416
+ this.staffEnding = false;
1417
+ this.offset = 0;
1418
+ this.staffAllList = [];
1419
+ this.$set(this, 'selectedStaffOrgId', '');
1420
+ if (this.$refs.staffTree) {
1421
+ this.$refs.staffTree.clearAllChecked(this.$refs.staffTree.data);
1422
+ this.$refs.staffTree.$emit('handleChange', []);
1423
+ }
1424
+ }
1425
+ },
1426
+ resetStaffTreeChecked() {
1427
+ this.$set(this, 'selectedStaffOrgId', '');
1428
+ this.proStaffOrgList = [];
1429
+ if (this.$refs.staffTree) {
1430
+ this.$refs.staffTree.clearAllChecked(this.$refs.staffTree.data);
1431
+ this.$refs.staffTree.$emit('handleChange', []);
1432
+ }
1433
+ if (this.tabName === 'staff') {
1434
+ this.$nextTick(() => {
1435
+ this.loadMore();
1436
+ });
1437
+ }
1438
+ },
1439
+
1440
+ handleTabChange(tabName) {
1441
+ this.tabName = tabName;
1442
+
1443
+ if (tabName === 'staff') {
1444
+ this.resetStaffTreeChecked();
1445
+ this.staffEnding = false;
1446
+ this.offset = 0;
1447
+ this.staffAllList = [];
1448
+ this.loadingStaff = false;
1449
+ // 仅当树未初始化时才加载
1450
+ if (!this.staffTreeLoaded || this.staffTree.length === 0) {
1451
+ this.loadMore().catch(err => console.log("同步加载失败:", err));
1452
+ }
1453
+ } else if (tabName === 'post' && this.proPostList.length === 0) {
1454
+ this.proPostList = [];
1455
+ // 移除initData调用,避免重置树数据
1456
+ // this.$refs.postTree.initData();
1457
+ }
1458
+ },
1459
+ initStaffList() {
1460
+ this.staffEnding = false;
1461
+ this.offset = 0;
1462
+ this.staffAllList = [];
1463
+ // 小幅延迟,确保树重置完成后再发起查询(避免时序差)
1464
+ this.$nextTick(() => {
1465
+ setTimeout(() => {
1466
+ this.loadMore();
1467
+ }, 50); // 50ms足够,可根据实际情况调整
1468
+ });
1469
+ },
1470
+ async fastChedkOrg(item) {
1471
+ // 1. 改用组织 Tab 专属标签列表判断
1472
+ if (!this.orgTagList.length) {
1473
+ this.$Message.warning("快捷选择标签正在加载中,请稍后");
1474
+ return;
1475
+ }
1476
+ const treeRef = this.$refs.orgTree;
1477
+ const proListKey = 'proOrgList';
1478
+ const treeDataKey = 'orgTree';
1479
+ this[proListKey] = [];
1480
+ // 移除initData调用
1481
+ // if (treeRef) await treeRef.initData();
1482
+ if (!treeRef) {
1483
+ this.$Message.error("组织树组件未初始化,请稍后再试");
1484
+ item.checked = !item.checked;
1485
+ return;
1486
+ }
1487
+
1488
+ // 2. 清空组织 Tab 标签选中状态(仅操作 orgTagList)
1489
+ this.orgTagList.forEach(tag => {
1490
+ tag.checked = false;
1491
+ });
1492
+
1493
+ // 3. 后续逻辑保持不变,仅操作当前 item(属于 orgTagList)
1494
+ const isCurrentlyChecked = item.checked;
1495
+ if (!isCurrentlyChecked) {
1496
+ item.checked = true;
1497
+ }
1498
+
1499
+ if (!item.checked) {
1500
+ this.$Message.info("已取消该快捷选择");
1501
+ return;
1502
+ }
1503
+
1504
+ try {
1505
+ await this.querySpecificParam(item, true, treeRef, proListKey, treeDataKey);
1506
+ } catch (error) {
1507
+ this.$Message.error(`快捷选择处理失败:${error.message.slice(0, 50)}`);
1508
+ item.checked = !item.checked;
1509
+ }
1510
+ },
1511
+ async fastChedkPost(item) {
1512
+ // 1. 改用岗位 Tab 专属标签列表判断
1513
+ if (!this.postTagList.length) {
1514
+ this.$Message.warning("快捷选择标签正在加载中,请稍后");
1515
+ return;
1516
+ }
1517
+
1518
+ const isOrgTab = this.tabName === 'org';
1519
+ const treeRef = isOrgTab ? this.$refs.orgTree : this.$refs.postTree;
1520
+ const proListKey = isOrgTab ? 'proOrgList' : 'proPostList';
1521
+ const treeDataKey = isOrgTab ? 'orgTree' : 'postTree';
1522
+
1523
+ this[proListKey] = [];
1524
+ const isCurrentlyChecked = item.checked;
1525
+ // 移除initData调用,避免重置树数据
1526
+ // if (treeRef) await treeRef.initData();
1527
+
1528
+ // 2. 清空岗位 Tab 标签选中状态(仅操作 postTagList)
1529
+ this.postTagList.forEach(tag => {
1530
+ tag.checked = false;
1531
+ });
1532
+
1533
+ // 3. 后续逻辑保持不变,仅操作当前 item(属于 postTagList)
1534
+ if (!isCurrentlyChecked) {
1535
+ item.checked = true;
1536
+ }
1537
+
1538
+ if (!item.checked) {
1539
+ this.$Message.info("已取消该快捷选择");
1540
+ return;
1541
+ }
1542
+
1543
+ try {
1544
+ await this.querySpecificParam(item, isOrgTab, treeRef, proListKey, treeDataKey);
1545
+ } catch (error) {
1546
+ this.$Message.error(`快捷选择处理失败:${error.message.slice(0, 50)}`);
1547
+ item.checked = !item.checked;
1548
+ }
1549
+ },
1550
+ queryTagList() {
1551
+ ajax.get('/pub-manage-server/pub/helpBoxTag/q/queryQuickPickTags').then((res) => {
1552
+ if (res.data.code === 1) {
1553
+ let baseTagList = res.data.data || [];
1554
+ // 组织 Tab 标签:深拷贝并初始化选中状态
1555
+ this.orgTagList = this.safeDeepCopy(baseTagList).map(item => ({
1556
+ ...item,
1557
+ checked: false // 独立选中状态
1558
+ }));
1559
+ // 岗位 Tab 标签:深拷贝并初始化选中状态(与组织 Tab 完全独立)
1560
+ this.postTagList = this.safeDeepCopy(baseTagList).map(item => ({
1561
+ ...item,
1562
+ checked: false // 独立选中状态
1563
+ }));
1564
+ }
1565
+ });
1566
+ },
1567
+
1568
+ querySpecificParam: function (item, isOrgTab, treeRef, proListKey, treeDataKey) {
1569
+ return new Promise((resolve, reject) => {
1570
+ // 取消选中时处理(仅当手动点击已选中的标签时触发)
1571
+ if (!item.checked) {
1572
+ if (treeRef && treeRef.initData) {
1573
+ treeRef.initData(); // 清空对应树组件
1574
+ }
1575
+ this[proListKey] = []; // 清空对应暂存列表
1576
+ this.$Message.info("已取消该快捷选择");
1577
+ resolve();
1578
+ return;
1579
+ }
1580
+
1581
+ // 请求快捷选择对应的orgUnitId列表
1582
+ ajax.get(`/pub-manage-server/pub/helpBoxTag/q/querySpecificParam?quickPickKey=${item.quickPickKey}`)
1583
+ .then(async (res) => {
1584
+ if (res.data.code !== 1) {
1585
+ this.$Message.error("获取快捷选择配置失败");
1586
+ item.checked = !item.checked;
1587
+ reject(new Error("获取配置失败"));
1588
+ return;
1589
+ }
1590
+
1591
+ const orgUnitIds = res.data.data?.param || [];
1592
+ const safeOrgUnitIds = Array.isArray(orgUnitIds) ? orgUnitIds : [];
1593
+ if (safeOrgUnitIds.length === 0) {
1594
+ this.$Message.warning("该快捷选择未配置任何组织节点");
1595
+ item.checked = !item.checked;
1596
+ reject(new Error("无配置节点"));
1597
+ return;
1598
+ }
1599
+
1600
+ // 校验树组件是否存在(通用校验)
1601
+ if (!treeRef) {
1602
+ this.$Message.error("树组件未初始化");
1603
+ item.checked = !item.checked;
1604
+ reject(new Error("树组件不存在"));
1605
+ return;
1606
+ }
1607
+ const originalTreeData = treeRef.data && treeRef.data.length ? this.safeDeepCopy(treeRef.data) : [];
1608
+ const matchedNodes = [];
1609
+
1610
+ // 批量处理每个orgUnitId(循环逻辑不变,通用处理)
1611
+ for (const orgUnitId of safeOrgUnitIds) {
1612
+ try {
1613
+ // 节点详情查询(复用原有方法)
1614
+ let currentNode = await this.queryOrgNodeDetail(orgUnitId);
1615
+ if (!currentNode || !currentNode.orgUnitId) {
1616
+ this.$Message.warning(`节点【${orgUnitId}】数据异常,跳过处理`);
1617
+ continue;
1618
+ }
1619
+
1620
+ // 循环引用检测与处理(复用原有方法)
1621
+ const cycle = this.detectCircularReferences(currentNode);
1622
+ if (cycle) {
1623
+ this.$Message.warning(`节点【${orgUnitId}】存在循环引用,已自动修复`);
1624
+ currentNode = this.safeDeepCopy(currentNode);
1625
+ }
1626
+
1627
+ // 节点名称默认值处理(复用原有逻辑)
1628
+ currentNode.orgNodeName = currentNode.orgNodeName || currentNode.orgUnitName || `未命名组织(${orgUnitId})`;
1629
+ currentNode.orgUnitName = currentNode.orgNodeName;
1630
+
1631
+ // 节点是否已在树中检测(复用原有方法)
1632
+ const nodeInTree = this.findNodeInTree(originalTreeData, orgUnitId);
1633
+ if (nodeInTree) {
1634
+ if (this.detectCircularReferences(nodeInTree)) {
1635
+ this.$Message.warning(`树中节点【${orgUnitId}】存在循环引用,已跳过`);
1636
+ continue;
1637
+ }
1638
+ matchedNodes.push(nodeInTree);
1639
+ continue;
1640
+ }
1641
+
1642
+ // 父节点追溯与节点链挂载(使用当前Tab的树数据originalTreeData,而非固定orgTree)
1643
+ let targetParent = null;
1644
+ let nodeChain = [currentNode];
1645
+ const MAX_DEPTH = 8;
1646
+ let depth = 0;
1647
+ let lastParentId = '';
1648
+
1649
+ while (!targetParent && depth < MAX_DEPTH) {
1650
+ const parentId = currentNode.parentOrgUnitId;
1651
+ if (!parentId || typeof parentId !== 'string' || parentId === lastParentId) {
1652
+ break;
1653
+ }
1654
+ lastParentId = parentId;
1655
+
1656
+ const parentNode = await this.queryOrgNodeDetail(parentId);
1657
+ if (!parentNode || !parentNode.orgUnitId) {
1658
+ break;
1659
+ }
1660
+
1661
+ if (this.detectCircularReferences(parentNode)) {
1662
+ this.$Message.warning(`父节点【${parentId}】存在循环引用,已跳过`);
1663
+ break;
1664
+ }
1665
+
1666
+ parentNode.orgNodeName = parentNode.orgNodeName || parentNode.orgUnitName || `未命名组织(${parentId})`;
1667
+ // 查询父节点是否在当前Tab的树数据中(originalTreeData),而非固定orgTree
1668
+ const parentInTree = this.findNodeInTree(originalTreeData, parentNode.orgUnitId);
1669
+
1670
+ if (parentInTree) {
1671
+ targetParent = parentInTree;
1672
+ break;
1673
+ }
1674
+
1675
+ nodeChain.unshift(parentNode);
1676
+ currentNode = parentNode;
1677
+ depth++;
1678
+ }
1679
+
1680
+ // 节点链挂载(复用原有方法)
1681
+ if (targetParent) {
1682
+ let currentMountParent = targetParent;
1683
+ for (const node of nodeChain) {
1684
+ const formattedNode = this.safeDeepCopy({
1685
+ ...node,
1686
+ leafNode: await this.judgeNodeLeafe(node.orgUnitId),
1687
+ expand: true,
1688
+ checked: false,
1689
+ orgChildrenList: [],
1690
+ children: []
1691
+ });
1692
+
1693
+ if (!Array.isArray(currentMountParent.orgChildrenList)) {
1694
+ currentMountParent.orgChildrenList = [];
1695
+ currentMountParent.children = [];
1696
+ }
1697
+
1698
+ const isDuplicate = currentMountParent.orgChildrenList.some(
1699
+ child => child.orgUnitId === formattedNode.orgUnitId
1700
+ );
1701
+ if (!isDuplicate) {
1702
+ currentMountParent.orgChildrenList.push(formattedNode);
1703
+ currentMountParent.leafNode = false;
1704
+ }
1705
+
1706
+ currentMountParent = currentMountParent.orgChildrenList.find(
1707
+ child => child.orgUnitId === formattedNode.orgUnitId
1708
+ ) || formattedNode;
1709
+ }
1710
+
1711
+ const mountedTarget = this.findNodeInTree(originalTreeData, orgUnitId);
1712
+ if (mountedTarget) {
1713
+ matchedNodes.push(mountedTarget);
1714
+ }
1715
+ }
1716
+
1717
+ } catch (error) {
1718
+ this.$Message.error(`处理节点【${orgUnitId}】失败:${error.message.slice(0, 50)}`);
1719
+ continue;
1720
+ }
1721
+ }
1722
+
1723
+
1724
+ if (matchedNodes.length > 0) {
1725
+ // 同步暂存列表(组织proOrgList/岗位proPostList)
1726
+ this[proListKey] = matchedNodes;
1727
+ const finalTreeData = this.safeDeepCopy(originalTreeData);
1728
+ // 响应式更新对应树数据(组织orgTree/岗位postTree)
1729
+ this.$set(this, treeDataKey, finalTreeData);
1730
+
1731
+ await this.$nextTick(async () => {
1732
+ // 更新树组件数据(通用逻辑)
1733
+ if (treeRef.updateTreeData) {
1734
+ treeRef.updateTreeData(this.safeDeepCopy(finalTreeData));
1735
+ } else {
1736
+ treeRef.data = this.safeDeepCopy(finalTreeData);
1737
+ }
1738
+
1739
+ // 设置树选中状态(通用逻辑)
1740
+ const safeNodeIds = matchedNodes.map(node => node.orgUnitId);
1741
+ if (treeRef.setCheckedNodes) {
1742
+ treeRef.setCheckedNodes([]);
1743
+ treeRef.setCheckedNodes(safeNodeIds);
1744
+ treeRef.handleChange(matchedNodes); // 触发子组件选中事件
1745
+ } else {
1746
+ this.updateTreeNodesStatus(
1747
+ finalTreeData,
1748
+ new Set(safeNodeIds),
1749
+ new Set(safeNodeIds)
1750
+ );
1751
+ treeRef.handleChange(matchedNodes);
1752
+ }
1753
+
1754
+ // 提示信息
1755
+ this.$Message.success(`成功选中${matchedNodes.length}个组织节点`);
1756
+ });
1757
+ } else {
1758
+ this.$Message.warning("未找到任何可匹配的组织节点");
1759
+ item.checked = !item.checked;
1760
+ }
1761
+ resolve();
1762
+ })
1763
+ .catch((error) => {
1764
+ const errMsg = error.message || '网络异常';
1765
+ this.$Message.error(`快捷选择失败:${errMsg.slice(0, 50)}`);
1766
+ item.checked = !item.checked;
1767
+ reject(error);
1768
+ });
1769
+ });
1770
+ },
1771
+
1772
+ // 移除树数据中的 parent 引用,避免子组件处理时形成循环
1773
+ removeParentReferences(treeData) {
1774
+ if (!Array.isArray(treeData)) return;
1775
+ treeData.forEach(node => {
1776
+ if (node.parent) delete node.parent;
1777
+ // 递归处理子节点
1778
+ if (Array.isArray(node.orgChildrenList)) {
1779
+ this.removeParentReferences(node.orgChildrenList);
1780
+ }
1781
+ if (Array.isArray(node.children)) {
1782
+ this.removeParentReferences(node.children);
1783
+ }
1784
+ });
1785
+ },
1786
+ // 在 methods 中添加循环引用检测工具函数
1787
+ detectCircularReferences(obj, path = [], visited = new Map()) {
1788
+ if (obj === null || typeof obj !== 'object') return null;
1789
+
1790
+ if (visited.has(obj)) {
1791
+ return { path: [...path, visited.get(obj)], node: obj };
1792
+ }
1793
+
1794
+ visited.set(obj, Array.isArray(obj) ? 'Array' : `Object(${obj.orgUnitId || 'unknown'})`);
1795
+
1796
+ const keys = Array.isArray(obj) ? obj.map((_, i) => i) : Object.keys(obj);
1797
+ for (const key of keys) {
1798
+ const value = obj[key];
1799
+ const newPath = [...path, key];
1800
+ if (value && typeof value === 'object') {
1801
+ const cycle = this.detectCircularReferences(value, newPath, visited);
1802
+ if (cycle) return cycle;
1803
+ }
1804
+ }
1805
+
1806
+ visited.delete(obj);
1807
+ return null;
1808
+ },
1809
+
1810
+ safeDeepCopy(obj, hash = new WeakMap()) {
1811
+ if (obj === null || typeof obj !== 'object') {
1812
+ return obj;
1813
+ }
1814
+
1815
+ if (hash.has(obj)) {
1816
+ const safeObj = {
1817
+ orgUnitId: obj.orgUnitId,
1818
+ orgNodeName: obj.orgNodeName || obj.name || `未命名组织(${obj.orgUnitId || 'unknown'})`,
1819
+ orgUnitName: obj.orgUnitName || obj.orgNodeName || obj.name || `未命名组织(${obj.orgUnitId || 'unknown'})`,
1820
+ parentOrgUnitId: obj.parentOrgUnitId || obj.parentId || null
1821
+ };
1822
+ hash.set(obj, safeObj);
1823
+ return safeObj;
1824
+ }
1825
+
1826
+ let copy;
1827
+ if (obj instanceof Array) {
1828
+ copy = [];
1829
+ hash.set(obj, copy);
1830
+ for (let i = 0; i < obj.length; i++) {
1831
+ copy[i] = this.safeDeepCopy(obj[i], hash);
1832
+ }
1833
+ } else if (obj instanceof Object) {
1834
+ copy = {};
1835
+ hash.set(obj, copy);
1836
+ for (let key in obj) {
1837
+ if (obj.hasOwnProperty(key)) {
1838
+ if (['__vue__', '__ob__', '$parent', '$children'].includes(key)) {
1839
+ continue;
1840
+ }
1841
+ if (['orgChildrenList', 'children'].includes(key) && Array.isArray(obj[key])) {
1842
+ copy[key] = obj[key].map(child => this.safeDeepCopy(child, hash));
1843
+ } else {
1844
+ copy[key] = this.safeDeepCopy(obj[key], hash);
1845
+ }
1846
+ }
1847
+ }
1848
+ } else {
1849
+ copy = obj;
1850
+ }
1851
+ return copy;
1852
+ },
1853
+
1854
+ /**
1855
+ * 查询单个节点的原生详情
1856
+ * @param {string} orgUnitId
1857
+ * @returns {Promise<Object|null>}
1858
+ */
1859
+ getOrgNodeDetail(orgUnitId) {
1860
+ return new Promise((resolve) => {
1861
+ ajax.get(`/pub-manage-server/pub/organ/q/queryOrg`, {
1862
+ params: { orgUnitId: orgUnitId }
1863
+ }).then((res) => {
1864
+ if (res.data.code === 1 && res.data.data) {
1865
+ const { orgUnitId, parentOrgUnitId, orgNodeName, orgChildrenList } = res.data.data;
1866
+ resolve({
1867
+ orgUnitId,
1868
+ parentOrgUnitId,
1869
+ orgNodeName,
1870
+ orgChildrenList: orgChildrenList || []
1871
+ });
1872
+ } else {
1873
+ resolve(null);
1874
+ }
1875
+ }).catch(() => {
1876
+ this.$Message.error(`查询节点【${orgUnitId}】详情失败`);
1877
+ resolve(null);
1878
+ });
1879
+ });
1880
+ },
1881
+
1882
+ /**
1883
+ * 迭代查找节点
1884
+ * @param {Array} treeData - 树形数据
1885
+ * @param {string} targetOrgUnitId - 目标节点ID
1886
+ * @returns {Object|null} 找到的节点
1887
+ */
1888
+ findNodeInTree(treeData, targetOrgUnitId) {
1889
+ if (!Array.isArray(treeData) || !targetOrgUnitId || typeof targetOrgUnitId !== 'string') {
1890
+ return null;
1891
+ }
1892
+
1893
+ const queue = [...treeData];
1894
+ while (queue.length > 0) {
1895
+ const currentNode = queue.shift();
1896
+ const nodeId = currentNode.orgUnitId || currentNode.id || '';
1897
+ if (nodeId === targetOrgUnitId) {
1898
+ return currentNode;
1899
+ }
1900
+ // 兼容children和orgChildrenList两种子节点字段
1901
+ const childNodes = currentNode.orgChildrenList || currentNode.children || [];
1902
+ if (childNodes.length > 0) {
1903
+ queue.push(...childNodes);
1904
+ }
1905
+ }
1906
+ return null;
1907
+ },
1908
+
1909
+ /**
1910
+ * 辅助方法:查询单个orgUnitId的节点详情
1911
+ * @param {string} orgUnitId - 组织节点ID
1912
+ * @returns {Promise<Object|null>} 节点详情
1913
+ */
1914
+ queryOrgNodeDetail(orgUnitId) {
1915
+ return new Promise((resolve) => {
1916
+ // 前置校验:避免无效请求
1917
+ if (!orgUnitId || typeof orgUnitId !== 'string') {
1918
+ resolve(null);
1919
+ return;
1920
+ }
1921
+
1922
+ ajax.get(`/pub-manage-server/pub/organ/q/queryOrg?orgUnitId=${orgUnitId}`)
1923
+ .then((res) => {
1924
+ if (res.data.code === 1 && res.data.data && res.data.data.orgUnitId) {
1925
+ // 深拷贝并清理无效字段
1926
+ const node = this.safeDeepCopy(res.data.data);
1927
+ // 确保节点名称有默认值,避免空显示
1928
+ const nodeName = node.orgNodeName || node.orgUnitName || node.name || `未命名组织(${orgUnitId})`;
1929
+ node.orgNodeName = nodeName;
1930
+ node.orgUnitName = nodeName; // 同步更新orgUnitName,确保所有使用场景都有值
1931
+ // 确保关键字段存在
1932
+ node.parentOrgUnitId = node.parentOrgUnitId || null;
1933
+ node.orgChildrenList = node.orgChildrenList || [];
1934
+ resolve(node);
1935
+ } else {
1936
+ // 接口返回空时,返回默认结构避免报错
1937
+ resolve({
1938
+ orgUnitId,
1939
+ orgNodeName: `未命名组织(${orgUnitId})`,
1940
+ orgUnitName: `未命名组织(${orgUnitId})`,
1941
+ parentOrgUnitId: null,
1942
+ orgChildrenList: []
1943
+ });
1944
+ }
1945
+ })
1946
+ .catch(() => {
1947
+ // 请求失败时,返回默认结构避免报错
1948
+ resolve({
1949
+ orgUnitId,
1950
+ orgNodeName: `未命名组织(${orgUnitId})`,
1951
+ orgUnitName: `未命名组织(${orgUnitId})`,
1952
+ parentOrgUnitId: null,
1953
+ orgChildrenList: []
1954
+ });
1955
+ });
1956
+ });
1957
+ },
1958
+
1959
+
1960
+ /**
1961
+ * 手动更新树节点状态(如果树组件没有提供update方法)
1962
+ * @param {Array} nodeList - 节点列表
1963
+ * @param {Set} expandIds - 需要展开的节点ID集合
1964
+ * @param {Set} checkedIds - 需要选中的节点ID集合
1965
+ */
1966
+ updateTreeNodesStatus(nodeList, expandIds, checkedIds) {
1967
+ nodeList.forEach(node => {
1968
+ // 更新展开状态
1969
+ if (expandIds.has(node.orgUnitId)) {
1970
+ node.expand = true;
1971
+ }
1972
+
1973
+ // 更新选中状态
1974
+ if (checkedIds.has(node.orgUnitId)) {
1975
+ node.checked = true;
1976
+ }
1977
+
1978
+ // 递归处理子节点
1979
+ const children = node.children || node.orgChildrenList;
1980
+ if (children && children.length > 0) {
1981
+ this.updateTreeNodesStatus(children, expandIds, checkedIds);
1982
+ }
1983
+ });
1984
+ },
1985
+ // 组织选择(新)相关方法
1986
+ async initOrgNewTree() {
1987
+ try {
1988
+ const res = await ajax.get('/pub-manage-server/pub/personHelpBox/q/getFullOrgUnitTree');
1989
+ if (res.data.code === 1) {
1990
+ // 直接使用返回的完整树形结构
1991
+ const treeData = res.data.data || [];
1992
+ // 处理树形数据,添加必要的字段
1993
+ const processedTree = this.processOrgNewTree(treeData);
1994
+ this.orgNewTree = this.safeDeepCopy(processedTree);
1995
+ // 树加载完成后,刷新 orgNewList 的父组织信息
1996
+ this.$nextTick(() => {
1997
+ this.refreshOrgNewListParentInfo();
1998
+ });
1999
+ }
2000
+ } catch (e) {
2001
+ this.$Message.error("组织选择(新)树初始化失败!");
2002
+ console.error(e);
2003
+ }
2004
+ },
2005
+ buildOrgNewTree(items) {
2006
+ const map = {};
2007
+ const roots = [];
2008
+ const copiedItems = this.safeDeepCopy(items);
2009
+
2010
+ copiedItems.forEach(item => {
2011
+ if (!item.orgUnitId) return;
2012
+ map[item.orgUnitId] = this.safeDeepCopy({
2013
+ ...item,
2014
+ orgChildrenList: [],
2015
+ children: [],
2016
+ title: item.orgUnitName || item.orgNodeName || item.name || `未命名组织(${item.orgUnitId || ''})`,
2017
+ expand: false,
2018
+ checked: false
2019
+ });
2020
+ });
2021
+
2022
+ copiedItems.forEach(item => {
2023
+ if (!item.orgUnitId || !map[item.orgUnitId]) return;
2024
+
2025
+ const node = map[item.orgUnitId];
2026
+ if (!item.parentOrgUnitId || !map[item.parentOrgUnitId]) {
2027
+ node.expand = true;
2028
+ roots.push(node);
2029
+ } else {
2030
+ const parent = map[item.parentOrgUnitId];
2031
+ parent.orgChildrenList.push(node);
2032
+ parent.children.push(node);
2033
+ parent.leafNode = false;
2034
+ }
2035
+ });
2036
+ return roots;
2037
+ },
2038
+ // 处理新接口返回的组织树数据
2039
+ processOrgNewTree(treeData) {
2040
+ if (!Array.isArray(treeData)) return [];
2041
+
2042
+ return treeData.map(node => {
2043
+ // 添加必要的字段
2044
+ const processedNode = {
2045
+ ...node,
2046
+ title: node.orgNodeName || node.orgUnitName || `未命名组织(${node.orgUnitId || ''})`,
2047
+ expand: false,
2048
+ checked: false,
2049
+ orgChildrenList: node.children || []
2050
+ };
2051
+
2052
+ // 递归处理子节点
2053
+ if (processedNode.children && processedNode.children.length > 0) {
2054
+ processedNode.children = this.processOrgNewTree(processedNode.children);
2055
+ processedNode.expand = true; // 默认展开有子节点的节点
2056
+ }
2057
+
2058
+ return processedNode;
2059
+ });
2060
+ },
2061
+ searchOrgNew() {
2062
+ // 搜索功能已通过计算属性实现,这里可以添加额外的逻辑
2063
+ },
2064
+ toggleProvinceExpand(province) {
2065
+ // 只有有子节点时才允许展开/折叠
2066
+ if (province.children && province.children.length > 0) {
2067
+ province.expand = !province.expand;
2068
+ // 同步更新原始数据中的节点状态
2069
+ this.updateOriginalNodeExpand(province);
2070
+ }
2071
+ },
2072
+ toggleCityExpand(city) {
2073
+ // 只有有子节点时才允许展开/折叠
2074
+ if (city.children && city.children.length > 0) {
2075
+ city.expand = !city.expand;
2076
+ // 同步更新原始数据中的节点状态
2077
+ this.updateOriginalNodeExpand(city);
2078
+ }
2079
+ },
2080
+ toggleDistrictExpand(district) {
2081
+ // 只有有子节点时才允许展开/折叠
2082
+ if (district.children && district.children.length > 0) {
2083
+ district.expand = !district.expand;
2084
+ // 同步更新原始数据中的节点状态
2085
+ this.updateOriginalNodeExpand(district);
2086
+ }
2087
+ },
2088
+ updateOriginalNodeExpand(node) {
2089
+ // 查找并更新原始数据中的节点状态
2090
+ const findAndUpdate = (nodes) => {
2091
+ for (let i = 0; i < nodes.length; i++) {
2092
+ if (nodes[i].orgUnitId === node.orgUnitId) {
2093
+ nodes[i].expand = node.expand;
2094
+ return true;
2095
+ }
2096
+ if (nodes[i].children && nodes[i].children.length > 0) {
2097
+ if (findAndUpdate(nodes[i].children)) {
2098
+ return true;
2099
+ }
2100
+ }
2101
+ }
2102
+ return false;
2103
+ };
2104
+ findAndUpdate(this.orgNewTree);
2105
+ },
2106
+ selectAllProvinceOrgNew(province) {
2107
+ // 全选并添加到已选择条件框,包含下级节点
2108
+ this.addOrgNewItemWithChildren(province, true);
2109
+ },
2110
+ selectAllCityOrgNew(city) {
2111
+ // 全选并添加到已选择条件框,包含下级节点
2112
+ this.addOrgNewItemWithChildren(city, true);
2113
+ },
2114
+ selectAllCityLevelOrgNew(city) {
2115
+ // 全选市本级并添加到已选择条件框,包含下级节点
2116
+ if (city.children && city.children.length > 0) {
2117
+ const cityLevel = city.children[0];
2118
+ this.addOrgNewItemWithChildren(cityLevel, true);
2119
+ }
2120
+ },
2121
+ selectAllDistrictOrgNew(district) {
2122
+ // 全选并添加到已选择条件框,包含下级节点
2123
+ this.addOrgNewItemWithChildren(district, true);
2124
+ },
2125
+ addOrgNewItemWithChildren(node, includeLevel) {
2126
+ if (!node.orgUnitId) return this.$Message.error("组织节点无效!");
2127
+
2128
+ // 检查是否已存在
2129
+ const id = includeLevel ? `01-${node.orgUnitId}` : `00-${node.orgUnitId}`;
2130
+ const existingIndex = this.orgNewList.findIndex(item => item.id === id);
2131
+ if (existingIndex > -1) {
2132
+ this.$Message.warning(`【${node.title}】已在选择列表中`);
2133
+ return;
2134
+ }
2135
+
2136
+ // 获取父组织信息
2137
+ const parentInfo = this.findParentInfo(node.orgUnitId);
2138
+
2139
+ // 获取完整路径
2140
+ const fullPath = this.getOrgFullPath(node.orgUnitId);
2141
+
2142
+ // 根节点没有父组织时,使用自身作为分组(让根节点和其下级分到同一组)
2143
+ const finalParentId = parentInfo.id || node.orgUnitId;
2144
+ const finalParentName = parentInfo.name || node.title || node.orgNodeName;
2145
+
2146
+ // 创建组织条件项
2147
+ const orgItem = {
2148
+ ...node,
2149
+ title: includeLevel ? `${node.title}(包含下级组织节点)` : node.title,
2150
+ id: id,
2151
+ includeLevel: includeLevel,
2152
+ orgNodeName: node.orgNodeName || node.title,
2153
+ parentOrgUnitId: finalParentId,
2154
+ parentOrgName: finalParentName,
2155
+ fullPath: fullPath
2156
+ };
2157
+
2158
+ // 添加到已选择列表
2159
+ this.orgNewList.push(orgItem);
2160
+ this.$Message.success(`已添加【${orgItem.title}】`);
2161
+ },
2162
+ findParentInfo(orgUnitId) {
2163
+ let result = { id: '', name: '' };
2164
+ const findParent = (nodes, parentId = '', parentName = '') => {
2165
+ for (const node of nodes) {
2166
+ if (node.orgUnitId === orgUnitId) {
2167
+ result = { id: parentId, name: parentName };
2168
+ return true;
2169
+ }
2170
+ if (node.children && node.children.length > 0) {
2171
+ if (findParent(node.children, node.orgUnitId, node.title || node.orgNodeName)) {
2172
+ return true;
2173
+ }
2174
+ }
2175
+ }
2176
+ return false;
2177
+ };
2178
+ findParent(this.orgNewTree);
2179
+ return result;
2180
+ },
2181
+ getOrgFullPath(orgUnitId) {
2182
+ const path = [];
2183
+ const findPath = (nodes, currentPath = []) => {
2184
+ for (const node of nodes) {
2185
+ const newPath = [...currentPath, node.title || node.orgNodeName];
2186
+ if (node.orgUnitId === orgUnitId) {
2187
+ path.push(...newPath);
2188
+ return true;
2189
+ }
2190
+ if (node.children && node.children.length > 0) {
2191
+ if (findPath(node.children, newPath)) {
2192
+ return true;
2193
+ }
2194
+ }
2195
+ }
2196
+ return false;
2197
+ };
2198
+ findPath(this.orgNewTree);
2199
+ return path.join(' > ');
2200
+ },
2201
+ findParentInfoFromTree(parentOrgUnitId) {
2202
+ let result = { id: parentOrgUnitId, name: '' };
2203
+ const findNode = (nodes) => {
2204
+ for (const node of nodes) {
2205
+ if (node.orgUnitId === parentOrgUnitId) {
2206
+ result.name = node.title || node.orgNodeName;
2207
+ return true;
2208
+ }
2209
+ if (node.children && node.children.length > 0) {
2210
+ if (findNode(node.children)) {
2211
+ return true;
2212
+ }
2213
+ }
2214
+ }
2215
+ return false;
2216
+ };
2217
+ findNode(this.orgNewTree);
2218
+ return result;
2219
+ },
2220
+ buildFullPathFromParent(item, parentInfo) {
2221
+ if (parentInfo.name) {
2222
+ return `${parentInfo.name} > ${item.title || item.orgNodeName}`;
2223
+ }
2224
+ return item.title || item.orgNodeName;
2225
+ },
2226
+ refreshOrgNewListParentInfo() {
2227
+ if (!this.orgNewTree || this.orgNewTree.length === 0) {
2228
+ return;
2229
+ }
2230
+ this.orgNewList.forEach(item => {
2231
+ if (item.parentOrgUnitId) {
2232
+ const parentInfo = this.findParentInfoFromTree(item.parentOrgUnitId);
2233
+ if (parentInfo.name) {
2234
+ item.parentOrgName = parentInfo.name;
2235
+ item.fullPath = this.buildFullPathFromParent(item, parentInfo);
2236
+ } else {
2237
+ item.parentOrgName = item.parentOrgName || '未知上级组织';
2238
+ }
2239
+ } else {
2240
+ item.parentOrgName = item.parentOrgName || '未知上级组织';
2241
+ }
2242
+ });
2243
+ },
2244
+ isProvinceAllSelected(province) {
2245
+ if (!province.children || province.children.length === 0) return false;
2246
+ return province.children.every(city => this.isCityAllSelected(city));
2247
+ },
2248
+ isCityAllSelected(city) {
2249
+ if (!city.children || city.children.length === 0) return false;
2250
+ return city.children.every(district => this.isDistrictAllSelected(district));
2251
+ },
2252
+ isCityLevelAllSelected(city) {
2253
+ // 检查市本级是否全选
2254
+ if (!city.children || city.children.length === 0) return false;
2255
+ // 假设第一个子节点是市本级
2256
+ const cityLevel = city.children[0];
2257
+ return this.isDistrictAllSelected(cityLevel);
2258
+ },
2259
+ isDistrictAllSelected(district) {
2260
+ if (!district.children || district.children.length === 0) return false;
2261
+ return district.children.every(unit => unit.checked);
2262
+ },
2263
+ setProvinceChecked(province, checked) {
2264
+ if (province.children) {
2265
+ province.children.forEach(city => {
2266
+ this.setCityChecked(city, checked);
2267
+ });
2268
+ }
2269
+ },
2270
+ setCityChecked(city, checked) {
2271
+ if (city.children) {
2272
+ city.children.forEach(district => {
2273
+ this.setDistrictChecked(district, checked);
2274
+ });
2275
+ }
2276
+ },
2277
+ setCityLevelChecked(city, checked) {
2278
+ if (city.children && city.children.length > 0) {
2279
+ // 只设置市本级(第一个子节点)
2280
+ const cityLevel = city.children[0];
2281
+ this.setDistrictChecked(cityLevel, checked);
2282
+ }
2283
+ },
2284
+ setDistrictChecked(district, checked) {
2285
+ if (district.children) {
2286
+ district.children.forEach(unit => {
2287
+ unit.checked = checked;
2288
+ });
2289
+ }
2290
+ },
2291
+ handleOrgNewUnitChange(unit) {
2292
+ // 处理组织单位选择变化
2293
+ this.collectOrgNewCheckedUnits();
2294
+ },
2295
+ collectOrgNewCheckedUnits() {
2296
+ this.proOrgNewList = [];
2297
+ this.collectCheckedUnits(this.orgNewTree);
310
2298
  },
2299
+ collectCheckedUnits(nodeList) {
2300
+ if (!Array.isArray(nodeList)) return;
2301
+ nodeList.forEach(node => {
2302
+ if (node.checked) {
2303
+ this.proOrgNewList.push({
2304
+ ...node,
2305
+ orgNodeName: node.orgNodeName || node.title || `未命名组织(${node.orgUnitId})`
2306
+ });
2307
+ }
2308
+ if (node.children && node.children.length > 0) {
2309
+ this.collectCheckedUnits(node.children);
2310
+ }
2311
+ });
2312
+ },
2313
+ expandAllOrgNew() {
2314
+ this.setAllExpand(this.orgNewTree, true);
2315
+ },
2316
+ collapseAllOrgNew() {
2317
+ this.setAllExpand(this.orgNewTree, false);
2318
+ },
2319
+ setAllExpand(nodeList, expand) {
2320
+ if (!Array.isArray(nodeList)) return;
2321
+ nodeList.forEach(node => {
2322
+ node.expand = expand;
2323
+ if (node.children && node.children.length > 0) {
2324
+ this.setAllExpand(node.children, expand);
2325
+ }
2326
+ });
2327
+ },
2328
+ clearOrgNew() {
2329
+ this.clearAllCheckedOrgNew(this.orgNewTree);
2330
+ this.proOrgNewList = [];
2331
+ this.orgNewSearch = '';
2332
+ },
2333
+ clearAllCheckedOrgNew(nodeList) {
2334
+ if (!Array.isArray(nodeList)) return;
2335
+ nodeList.forEach(node => {
2336
+ node.checked = false;
2337
+ if (node.children && node.children.length > 0) {
2338
+ this.clearAllCheckedOrgNew(node.children);
2339
+ }
2340
+ });
2341
+ },
2342
+ addOrgNewItem(node) {
2343
+ // 调用通用方法,不包含下级节点
2344
+ this.addOrgNewItemWithChildren(node, false);
2345
+ },
2346
+ addOrgNewList() {
2347
+ const validOrgList = this.proOrgNewList.filter(node => node.orgUnitId);
2348
+ if (!validOrgList.length) return this.$Message.error("请先选择组织节点!");
2349
+
2350
+ validOrgList.forEach(node => {
2351
+ this.addOrgNewItemWithChildren(node, false);
2352
+ });
2353
+
2354
+ // 清空选中状态
2355
+ this.clearAllCheckedOrgNew(this.orgNewTree);
2356
+ this.proOrgNewList = [];
2357
+ }
2358
+
311
2359
  },
312
2360
  computed:{
313
- getCheckedStaff(){
314
- return this.staffAllList.filter((item)=>item.checked===true).length
315
- },
2361
+ // getCheckedStaff(){
2362
+ // return this.staffAllList.filter((item)=>item.checked===true).length
2363
+ // },
316
2364
  getCheckTypenum(){
317
2365
  let l1 = this.orgList.length?1:0;
318
2366
  let l2 = this.postList.length?1:0;
319
2367
  let l3 = this.staffList.length?1:0;
320
- return l1+l2+l3
2368
+ let l4 = this.orgNewList.length?1:0;
2369
+ return l1+l2+l3+l4
2370
+ },
2371
+ filteredOrgNewTree() {
2372
+ // 前端过滤,模糊匹配
2373
+ const searchTerm = this.orgNewSearch.toLowerCase().trim();
2374
+ if (!searchTerm) {
2375
+ return this.orgNewTree;
2376
+ }
2377
+
2378
+ // 递归过滤函数
2379
+ const filterTree = (nodes) => {
2380
+ return nodes.map(node => {
2381
+ // 检查当前节点是否匹配
2382
+ const isMatch = node.title.toLowerCase().includes(searchTerm);
2383
+
2384
+ // 递归过滤子节点
2385
+ let filteredChildren = [];
2386
+ if (node.children && node.children.length > 0) {
2387
+ filteredChildren = filterTree(node.children);
2388
+ }
2389
+
2390
+ // 如果当前节点匹配或有匹配的子节点,则保留该节点
2391
+ if (isMatch || filteredChildren.length > 0) {
2392
+ // 创建一个新对象,保留原始节点的所有属性,包括expand状态
2393
+ return {
2394
+ ...node,
2395
+ children: filteredChildren
2396
+ };
2397
+ }
2398
+
2399
+ // 不匹配且没有匹配的子节点,则过滤掉
2400
+ return null;
2401
+ }).filter(Boolean); // 过滤掉null值
2402
+ };
2403
+
2404
+ // 直接使用原始树数据,不需要深拷贝
2405
+ return filterTree(this.orgNewTree);
2406
+ },
2407
+ groupedOrgNewList() {
2408
+ const groups = {};
2409
+ this.orgNewList.forEach((item, index) => {
2410
+ const groupKey = item.parentOrgUnitId || 'unknown';
2411
+ const groupName = item.parentOrgName || '未知上级组织';
2412
+ if (!groups[groupKey]) {
2413
+ groups[groupKey] = {
2414
+ parentOrgUnitId: groupKey,
2415
+ parentOrgName: groupName,
2416
+ items: []
2417
+ };
2418
+ }
2419
+ groups[groupKey].items.push({
2420
+ ...item,
2421
+ originalIndex: index
2422
+ });
2423
+ });
2424
+ return Object.values(groups).sort((a, b) => {
2425
+ if (a.parentOrgName === '未知上级组织') return 1;
2426
+ if (b.parentOrgName === '未知上级组织') return -1;
2427
+ return a.parentOrgName.localeCompare(b.parentOrgName, 'zh-CN');
2428
+ });
321
2429
  }
322
2430
  },
323
2431
  watch:{
@@ -326,6 +2434,7 @@ export default {
326
2434
  },
327
2435
  data:{
328
2436
  handler(val){
2437
+ if(!val) return
329
2438
  let map = deepCopy(val)
330
2439
  let orgList = map.orgList || []
331
2440
  orgList.forEach(item=>{
@@ -340,6 +2449,31 @@ export default {
340
2449
  })
341
2450
  this.postList = postList
342
2451
  this.staffList = map.staffList || []
2452
+ let orgNewList = map.orgNewList || []
2453
+ orgNewList.forEach(item => {
2454
+ item.title = item.includeLevel ? `${item.orgNodeName}(包含下级组织节点)` : item.orgNodeName
2455
+ item.id = item.includeLevel ? ('01' + '-' + item.orgUnitId) : ('00' + '-' + item.orgUnitId)
2456
+ if (!item.parentOrgName && item.parentOrgUnitId) {
2457
+ if (this.orgNewTree && this.orgNewTree.length > 0) {
2458
+ const parentInfo = this.findParentInfoFromTree(item.parentOrgUnitId)
2459
+ item.parentOrgName = parentInfo.name
2460
+ item.fullPath = item.fullPath || this.buildFullPathFromParent(item, parentInfo)
2461
+ } else {
2462
+ item.parentOrgName = item.parentOrgName || '加载中...'
2463
+ }
2464
+ }
2465
+ if (!item.parentOrgName) {
2466
+ item.parentOrgName = '未知上级组织'
2467
+ }
2468
+ })
2469
+ this.orgNewList = orgNewList
2470
+ if (orgNewList.some(item => item.parentOrgName === '加载中...')) {
2471
+ this.$nextTick(() => {
2472
+ setTimeout(() => {
2473
+ this.refreshOrgNewListParentInfo()
2474
+ }, 1000)
2475
+ })
2476
+ }
343
2477
  },
344
2478
  deep: true,
345
2479
  immediate: true,
@@ -348,7 +2482,691 @@ export default {
348
2482
  }
349
2483
  </script>
350
2484
  <style lang="less" scoped>
2485
+ // 新增checkbox样式
2486
+ //.staff-checkbox {
2487
+ // margin-right: 12px;
2488
+ // flex-shrink: 0;
2489
+ //
2490
+ // /deep/ .ivu-checkbox-wrapper {
2491
+ // display: flex;
2492
+ // align-items: center;
2493
+ // cursor: pointer;
2494
+ // }
2495
+ //
2496
+ // /deep/ .ivu-checkbox {
2497
+ // width: 18px;
2498
+ // height: 18px;
2499
+ //
2500
+ // &:checked {
2501
+ // /deep/ .ivu-checkbox-inner {
2502
+ // background-color: var(--primary-color);
2503
+ // border-color: var(--primary-color);
2504
+ // }
2505
+ // }
2506
+ // }
2507
+ //}
2508
+
2509
+ .staff-left-right-layout {
2510
+ display: flex;
2511
+ // 适配全屏:使用视口高度百分比 + 最小高度限制
2512
+ height: calc(100vh - 180px) !important;
2513
+ min-height: 500px !important;
2514
+ gap: 15px;
2515
+ padding: 0 5px;
2516
+ width: 100%;
2517
+ box-sizing: border-box;
2518
+ flex-wrap: nowrap !important;
2519
+
2520
+ // 小屏幕 不改变左右结构
2521
+ @media (max-width: 1200px) {
2522
+ height: calc(100vh - 200px) !important;
2523
+
2524
+ & > .staff-left-panel {
2525
+ width: calc(60% - 7.5px) !important;
2526
+ min-width: 280px !important;
2527
+ max-width: none !important;
2528
+ }
2529
+ }
2530
+
2531
+ @media (max-width: 768px) {
2532
+ flex-wrap: wrap !important;
2533
+
2534
+ & > .staff-left-panel {
2535
+ width: 100% !important;
2536
+ min-width: 100% !important;
2537
+ max-width: 100% !important;
2538
+ height: 40% !important;
2539
+ margin-bottom: 10px;
2540
+ }
2541
+
2542
+ .staff-right-panel {
2543
+ width: 100% !important;
2544
+ height: 60% !important;
2545
+ }
2546
+ }
2547
+
2548
+ & > .staff-left-panel {
2549
+ width: calc(60% - 7.5px) !important;
2550
+ min-width: 320px !important;
2551
+ max-width: none !important;
2552
+ display: flex !important;
2553
+ flex-direction: column !important;
2554
+ box-sizing: border-box !important;
2555
+ flex-shrink: 0 !important;
2556
+ flex-grow: 0 !important;
2557
+ height: 100% !important;
2558
+ position: relative !important;
2559
+
2560
+ ::v-deep(.panel-title) {
2561
+ all: unset !important;
2562
+ display: flex !important;
2563
+ align-items: center !important;
2564
+ visibility: visible !important;
2565
+ opacity: 1 !important;
2566
+ height: 20px !important;
2567
+ line-height: 20px !important;
2568
+ font-size: 14px !important;
2569
+ font-weight: 500 !important;
2570
+ color: #333 !important;
2571
+ margin-bottom: 8px !important;
2572
+ background: #fff !important;
2573
+ z-index: 999 !important;
2574
+ padding: 0 !important;
2575
+ box-sizing: border-box !important;
2576
+ position: relative !important;
2577
+ width: 100% !important;
2578
+ // 左侧竖条样式
2579
+ &::before {
2580
+ content: '' !important;
2581
+ display: inline-block !important;
2582
+ width: 5px !important; // 竖条宽度
2583
+ height: 16px !important; // 竖条高度(略小于文字行高)
2584
+ background-color: #1890ff !important; // 蓝色竖条
2585
+ margin-right: 8px !important; // 竖条与文字间距
2586
+ //border-radius: 1.5px !important;
2587
+ }
2588
+ }
2589
+
2590
+ .staff-org-tree {
2591
+ height: calc(100% - 28px - 10px) !important; /* 28=标题高度+间距 + 10px额外缩减,确保按钮可见 */
2592
+ border: 1px solid #EAECF0;
2593
+ border-radius: 4px;
2594
+ padding: 0 !important;
2595
+ box-sizing: border-box;
2596
+ width: 100% !important;
2597
+ overflow-x: auto !important;
2598
+ overflow-y: auto !important;
2599
+ position: relative;
2600
+ flex-shrink: 1;
2601
+ min-height: 0 !important;
2602
+
2603
+ &::-webkit-scrollbar {
2604
+ width: 8px !important;
2605
+ height: 8px !important;
2606
+ background: #f9f9f9 !important;
2607
+ }
2608
+ &::-webkit-scrollbar-thumb {
2609
+ background: #ccc !important;
2610
+ border-radius: 4px !important;
2611
+ }
2612
+ scrollbar-width: thin !important;
2613
+ scrollbar-color: #ccc #f9f9f9 !important;
2614
+ }
2615
+
2616
+ .tree-scroll-force {
2617
+ width: 100% !important;
2618
+ height: 100% !important;
2619
+ margin: 0 !important;
2620
+ padding: 8px !important;
2621
+ box-sizing: border-box;
2622
+ overflow: visible !important;
2623
+ position: relative;
2624
+ }
2625
+ }
2626
+
2627
+ .staff-right-panel {
2628
+ width: calc(40% - 7.5px) !important;
2629
+ min-width: 0 !important;
2630
+ display: flex;
2631
+ flex-direction: column;
2632
+ box-sizing: border-box;
2633
+ gap: 8px; /* 搜索框与列表、列表与底部选择栏的间距统一为8px */
2634
+ padding: 0 5px;
2635
+ flex-shrink: 1 !important;
2636
+
2637
+ // 搜索框:固定高度
2638
+ .ivu-input-wrapper {
2639
+ height: 32px;
2640
+ flex-shrink: 0;
2641
+ }
2642
+
2643
+ // 右侧人员列表容器:适配全屏高度
2644
+ .staff-content {
2645
+ height: calc(100% - 32px - 40px - 16px - 10px) !important;
2646
+ min-height: 200px !important; // 小屏幕最小高度限制
2647
+ border: 1px solid #EAECF0;
2648
+ border-radius: 4px;
2649
+ overflow-x: hidden !important;
2650
+ overflow-y: auto !important;
2651
+ flex-shrink: 1;
2652
+
2653
+ &::-webkit-scrollbar {
2654
+ width: 8px !important;
2655
+ height: 8px !important;
2656
+ background: #f9f9f9 !important;
2657
+ }
2658
+ &::-webkit-scrollbar-thumb {
2659
+ background: #ccc !important;
2660
+ border-radius: 4px !important;
2661
+ }
2662
+ scrollbar-width: thin !important;
2663
+ scrollbar-color: #ccc #f9f9f9 !important;
2664
+ }
2665
+
2666
+ // 底部选择栏:固定高度
2667
+ .bottom-select {
2668
+ height: 40px;
2669
+ flex-shrink: 0;
2670
+ display: flex;
2671
+ align-items: center;
2672
+ justify-content: space-between;
2673
+ padding: 8px 0;
2674
+ background: #fff !important;
2675
+ border-top: 1px solid #f0f0f0;
2676
+
2677
+ //.num {
2678
+ // color: var(--primary-color);
2679
+ //}
2680
+ }
2681
+ }
2682
+ }
2683
+
2684
+
2685
+ .tab-content-pro {
2686
+ .tab {
2687
+ .search-row {
2688
+ display: flex;
2689
+ align-items: center;
2690
+ flex-wrap: nowrap; // 禁止换行
2691
+ width: 100%;
2692
+ gap: 8px; // 文字和搜索框之间的间距
2693
+ }
2694
+ .tree {
2695
+ // 全屏 使用百分比高度 + 最小高度
2696
+ height: calc(100vh - 340px) !important; // 从300px增加到340px,缩减40px高度
2697
+ min-height: 360px !important; // 降低最小高度,确保按钮可见
2698
+ max-height: none !important; // 移除最大高度限制
2699
+ margin-top:20px;
2700
+ overflow-y: auto !important;
2701
+ overflow-x: auto !important;
2702
+ border: 1px solid #EAECF0;
2703
+ border-radius: 4px;
2704
+ box-sizing: border-box;
2705
+
2706
+ &::-webkit-scrollbar {
2707
+ width: 8px !important;
2708
+ height: 8px !important;
2709
+ display: block !important;
2710
+ background: #f9f9f9 !important;
2711
+ }
2712
+ &::-webkit-scrollbar-thumb {
2713
+ background: #ccc !important;
2714
+ border-radius: 4px !important;
2715
+ }
2716
+ }
2717
+
2718
+ &.post .right .tree {
2719
+ // 岗位树适配全屏(缩减高度确保按钮可见)
2720
+ height: calc(100vh - 430px) !important; // 从400px增加到430px,缩减30px高度
2721
+ min-height: 270px !important; // 降低最小高度,确保按钮可见
2722
+ max-height: none !important;
2723
+ }
2724
+
2725
+ .bottom-select {
2726
+ display:flex;
2727
+ align-items: center;
2728
+ justify-content: space-between;
2729
+ margin-top:15px !important;
2730
+ padding: 8px 0;
2731
+ background: #fff !important;
2732
+ position: relative;
2733
+ z-index: 10;
2734
+ border-top: 1px solid #f0f0f0;
2735
+ .num{
2736
+ color:var(--primary-color);
2737
+ }
2738
+ }
2739
+ // 搜索标签样式(固定宽度,不换行)
2740
+ .search-label {
2741
+ white-space: nowrap; // 文字不换行
2742
+ flex-shrink: 0; // 不被压缩
2743
+ font-size: 14px;
2744
+ color: #333;
2745
+ }
2746
+ // 搜索框容器样式(自适应剩余宽度)
2747
+ .search-input-wrapper {
2748
+ flex: 1; // 占满剩余宽度
2749
+ min-width: 0; // 允许搜索框收缩
2750
+ }
2751
+ }
2752
+ }
2753
+
2754
+ /deep/ .ivu-tree {
2755
+ width: 100% !important;
2756
+ box-sizing: border-box;
2757
+ }
2758
+
2759
+ /deep/ .ivu-tree-node-content {
2760
+ width: 100% !important;
2761
+ box-sizing: border-box;
2762
+ }
2763
+
2764
+ .gust-item {
2765
+ display: flex;
2766
+ align-items: center;
2767
+ width: 100% !important;
2768
+ padding: 12px 12px;
2769
+ margin-top: 8px;
2770
+ cursor: pointer;
2771
+ box-sizing: border-box;
2772
+ border: none !important;
2773
+ &:hover {
2774
+ background-color: #f2f8ff;
2775
+ border-radius: 4px;
2776
+ }
2777
+ }
2778
+
2779
+ // 滚动条样式
2780
+ .staff-content::-webkit-scrollbar {
2781
+ width: 8px !important;
2782
+ height: 8px;
2783
+ display: block !important;
2784
+ }
2785
+
2786
+ .staff-content::-webkit-scrollbar-thumb {
2787
+ background-color: #ccc !important;
2788
+ border-radius: 4px !important;
2789
+ }
2790
+
2791
+ .staff-content::-webkit-scrollbar-track {
2792
+ background-color: #f9f9f9 !important;
2793
+ }
2794
+
2795
+ .tab.post .left {
2796
+ width: calc(50% - 10px) !important;
2797
+ // 适配全屏高度
2798
+ height: calc(100vh - 380px) !important;
2799
+ min-height: 270px !important;
2800
+ overflow: hidden !important;
2801
+ padding-right: 10px;
2802
+ box-sizing: border-box;
2803
+
2804
+ // 小屏幕仅调整宽度,不改变左右结构
2805
+ @media (max-width: 1200px) {
2806
+ width: calc(50% - 10px) !important;
2807
+ }
2808
+
2809
+ // 超小屏幕
2810
+ @media (max-width: 768px) {
2811
+ width: 100%;
2812
+ margin-bottom: 10px;
2813
+ }
2814
+ }
2815
+
2816
+ .scroll-container {
2817
+ height: 100% !important;
2818
+ overflow-y: auto !important;
2819
+ overflow-x: hidden !important;
2820
+ padding: 8px 0;
2821
+ margin: 0;
2822
+ box-sizing: border-box;
2823
+ }
2824
+
2825
+ .scroll-container::-webkit-scrollbar {
2826
+ width: 8px !important;
2827
+ height: 8px;
2828
+ display: block !important;
2829
+ }
2830
+
2831
+ .scroll-container::-webkit-scrollbar-thumb {
2832
+ background-color: #ccc !important;
2833
+ border-radius: 4px !important;
2834
+ transition: background-color 0.2s;
2835
+ }
2836
+
2837
+ .scroll-container::-webkit-scrollbar-thumb:hover {
2838
+ background-color: #999 !important;
2839
+ }
2840
+
2841
+ .scroll-container::-webkit-scrollbar-track {
2842
+ background-color: #f9f9f9 !important;
2843
+ border-radius: 4px !important;
2844
+ }
2845
+
2846
+ .position-item {
2847
+ border: 1px solid #ddd;
2848
+ margin: 5px 0;
2849
+ padding: 8px 10px;
2850
+ border-radius: 2px;
2851
+ cursor: pointer;
2852
+ transition: background-color 0.2s;
2853
+ white-space: normal !important;
2854
+ overflow: visible !important;
2855
+ text-overflow: clip !important;
2856
+ line-height: 1.4;
2857
+ min-height: 40px;
2858
+ word-wrap: break-word;
2859
+ }
2860
+
2861
+ .position-item.active {
2862
+ border-color: #1890ff;
2863
+ background-color: #e6f7ff;
2864
+ }
2865
+
2866
+ .position-item:hover:not(.active) {
2867
+ background-color: #f5f5f5;
2868
+ }
2869
+
2870
+ /* 组织选择(新)样式 */
2871
+ .org-new-tab {
2872
+ .org-new-header {
2873
+ display: flex;
2874
+ justify-content: space-between;
2875
+ align-items: center;
2876
+ margin-bottom: 16px;
2877
+ flex-wrap: wrap;
2878
+ gap: 10px;
2879
+ }
2880
+
2881
+ .org-new-actions {
2882
+ display: flex;
2883
+ gap: 10px;
2884
+ }
2885
+
2886
+ .org-new-content {
2887
+ max-height: 500px;
2888
+ overflow-y: auto;
2889
+ border: 1px solid #EAECF0;
2890
+ border-radius: 4px;
2891
+ padding: 16px;
2892
+ margin-bottom: 16px;
2893
+ }
2894
+
2895
+ .org-card {
2896
+ border: 1px solid #EAECF0;
2897
+ border-radius: 4px;
2898
+ margin-bottom: 16px;
2899
+ overflow: hidden;
2900
+
2901
+ .org-card-header {
2902
+ display: flex;
2903
+ justify-content: space-between;
2904
+ align-items: center;
2905
+ padding: 12px 16px;
2906
+ background-color: #f9fafb;
2907
+ border-bottom: 1px solid #EAECF0;
2908
+
2909
+ &:hover {
2910
+ background-color: #f3f4f6;
2911
+ }
2912
+
2913
+ .org-card-title {
2914
+ display: flex;
2915
+ align-items: center;
2916
+ font-weight: 500;
2917
+ cursor: pointer;
2918
+ }
2919
+
2920
+ .org-card-actions {
2921
+ display: flex;
2922
+ align-items: center;
2923
+ gap: 10px;
2924
+
2925
+ .ivu-btn-text {
2926
+ color: #1890ff;
2927
+ font-weight: 500;
2928
+ transition: all 0.2s;
2929
+ cursor: pointer;
2930
+
2931
+ &:hover {
2932
+ color: #40a9ff;
2933
+ text-decoration: underline;
2934
+ transform: translateY(-1px);
2935
+ }
2936
+
2937
+ &:active {
2938
+ color: #096dd9;
2939
+ transform: translateY(0);
2940
+ }
2941
+ }
2942
+ }
2943
+ }
2944
+
2945
+ .org-card-body {
2946
+ padding: 16px;
2947
+ }
2948
+ }
2949
+
2950
+ .city-card {
2951
+ border: 1px solid #EAECF0;
2952
+ border-radius: 4px;
2953
+ margin-bottom: 12px;
2954
+ overflow: hidden;
2955
+
2956
+ .city-card-header {
2957
+ display: flex;
2958
+ justify-content: space-between;
2959
+ align-items: center;
2960
+ padding: 10px 14px;
2961
+ background-color: #f9fafb;
2962
+ border-bottom: 1px solid #EAECF0;
2963
+
2964
+ &:hover {
2965
+ background-color: #f3f4f6;
2966
+ }
2967
+
2968
+ .city-card-title {
2969
+ display: flex;
2970
+ align-items: center;
2971
+ font-weight: 500;
2972
+ cursor: pointer;
2973
+ }
2974
+
2975
+ .city-card-actions {
2976
+ display: flex;
2977
+ align-items: center;
2978
+ gap: 10px;
2979
+
2980
+ .ivu-btn-text {
2981
+ color: #1890ff;
2982
+ font-weight: 500;
2983
+ transition: all 0.2s;
2984
+ cursor: pointer;
2985
+
2986
+ &:hover {
2987
+ color: #40a9ff;
2988
+ text-decoration: underline;
2989
+ transform: translateY(-1px);
2990
+ }
2991
+
2992
+ &:active {
2993
+ color: #096dd9;
2994
+ transform: translateY(0);
2995
+ }
2996
+ }
2997
+ }
2998
+ }
2999
+
3000
+ .city-card-body {
3001
+ padding: 14px;
3002
+ }
3003
+ }
3004
+
3005
+ .district-grid {
3006
+ display: grid;
3007
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
3008
+ gap: 12px;
3009
+ }
3010
+
3011
+ .district-item {
3012
+ border: 1px solid #EAECF0;
3013
+ border-radius: 4px;
3014
+ overflow: hidden;
3015
+
3016
+ .district-header {
3017
+ display: flex;
3018
+ justify-content: space-between;
3019
+ align-items: center;
3020
+ padding: 8px 12px;
3021
+ background-color: #f9fafb;
3022
+ border-bottom: 1px solid #EAECF0;
3023
+
3024
+ &:hover {
3025
+ background-color: #f3f4f6;
3026
+ }
3027
+
3028
+ .district-title {
3029
+ display: flex;
3030
+ align-items: center;
3031
+ font-weight: 500;
3032
+ cursor: pointer;
3033
+ }
3034
+
3035
+ .district-actions {
3036
+ display: flex;
3037
+ align-items: center;
3038
+ gap: 10px;
3039
+
3040
+ .ivu-btn-text {
3041
+ color: #1890ff;
3042
+ font-weight: 500;
3043
+ transition: all 0.2s;
3044
+ cursor: pointer;
3045
+
3046
+ &:hover {
3047
+ color: #40a9ff;
3048
+ text-decoration: underline;
3049
+ transform: translateY(-1px);
3050
+ }
3051
+
3052
+ &:active {
3053
+ color: #096dd9;
3054
+ transform: translateY(0);
3055
+ }
3056
+ }
3057
+ }
3058
+ }
3059
+
3060
+ .district-body {
3061
+ padding: 12px;
3062
+ }
3063
+ }
3064
+
3065
+ .org-unit-item {
3066
+ margin-bottom: 8px;
3067
+
3068
+ &:last-child {
3069
+ margin-bottom: 0;
3070
+ }
3071
+ }
3072
+
3073
+ .unit-title {
3074
+ display: flex;
3075
+ align-items: center;
3076
+ padding: 4px 0;
3077
+ cursor: pointer;
3078
+
3079
+ &:hover {
3080
+ color: #1890ff;
3081
+ }
3082
+ }
3083
+
3084
+ .org-card-title,
3085
+ .city-card-title,
3086
+ .district-title {
3087
+ cursor: pointer;
3088
+
3089
+ &:hover {
3090
+ color: #1890ff;
3091
+ }
3092
+ }
3093
+
3094
+ .org-new-footer {
3095
+ display: flex;
3096
+ justify-content: space-between;
3097
+ align-items: center;
3098
+ padding-top: 16px;
3099
+ border-top: 1px solid #f0f0f0;
3100
+ }
3101
+
3102
+ .include-level {
3103
+ display: flex;
3104
+ align-items: center;
3105
+ }
3106
+ }
3107
+
3108
+ .custom-select-wrapper {
3109
+ position: relative;
3110
+
3111
+ /deep/ .ivu-select-clear {
3112
+ right: 32px;
3113
+ color: #999;
3114
+ font-size: 16px;
3115
+ opacity: 1 !important;
3116
+ transition: all 0.2s ease;
3117
+
3118
+ &:hover {
3119
+ color: #ff4d4f;
3120
+ transform: scale(1.1);
3121
+ }
3122
+ }
3123
+
3124
+ /deep/ .ivu-select-arrow {
3125
+ right: 12px;
3126
+ }
3127
+
3128
+ /deep/ .ivu-select-input {
3129
+ padding-right: 45px !important;
3130
+ }
3131
+
3132
+ /deep/ .active-option {
3133
+ background-color: #f2f8ff;
3134
+ color: var(--primary-color);
3135
+ }
3136
+
3137
+ /deep/ .ivu-select-option-disabled {
3138
+ color: #ccc !important;
3139
+ background: #f5f5f5 !important;
3140
+ }
3141
+ }
3142
+
3143
+ .tag-select-container {
3144
+ margin-left: 10px;
3145
+
3146
+ /deep/ .ivu-select-dropdown {
3147
+ max-height: 200px;
3148
+ overflow-y: auto;
3149
+ }
3150
+
3151
+ /deep/ .active-option {
3152
+ background-color: #f2f8ff;
3153
+ color: var(--primary-color);
3154
+ }
3155
+
3156
+ /deep/ .ivu-select-selected-value {
3157
+ max-width: 150px;
3158
+ overflow: hidden;
3159
+ text-overflow: ellipsis;
3160
+ }
3161
+ }
3162
+
351
3163
  .modal-tree{
3164
+ // 全屏适配:移除固定宽度限制
3165
+ width: 100% !important;
3166
+ height: 100% !important;
3167
+ max-width: none !important;
3168
+ max-height: none !important;
3169
+
352
3170
  .header-text{
353
3171
  font-weight: bold;
354
3172
  font-size: 16px;
@@ -363,28 +3181,79 @@ export default {
363
3181
  line-height: 16px;
364
3182
  }
365
3183
  .content-container{
3184
+ width: 100%;
3185
+ height: 100%;
3186
+ padding: 10px;
3187
+ box-sizing: border-box;
366
3188
  }
367
3189
  .tree-orig{
368
3190
  width:100%;
369
3191
  display: flex;
370
- height: 700px;
3192
+ // 适配全屏高度
3193
+ height: calc(100vh - 80px) !important;
3194
+ min-height: 600px !important;
371
3195
  background: #fff;
3196
+ // 强制保留左右结构,仅超小屏幕 换行
3197
+ flex-wrap: nowrap !important;
3198
+
3199
+ // 只在当前组件内部隐藏 ink-bar,不影响全局
3200
+ /deep/.ivu-tabs-ink-bar {
3201
+ height: 0 !important;
3202
+ display: none !important;
3203
+ width: 0 !important;
3204
+ background: transparent !important;
3205
+ visibility: hidden !important;
3206
+ }
3207
+
372
3208
  /deep/.ivu-tabs-nav{
373
3209
  font-weight: bold;
374
3210
  font-size: 16px;
375
- }
376
- /deep/.ivu-tabs-nav{
377
3211
  width: 100%;
378
3212
  display: flex;
3213
+ margin: 0 !important;
3214
+ padding: 0 !important;
379
3215
  }
380
3216
  /deep/.ivu-tabs-nav .ivu-tabs-tab{
381
3217
  flex:1;
382
3218
  text-align: center;
3219
+ background: #F0F2F5 !important;
3220
+ margin: 0 !important;
3221
+ padding: 12px 0 !important;
3222
+ border: none !important;
3223
+ line-height: 1 !important;
3224
+ border-right: 2px solid #E5E6EB !important; // 页签右侧竖线
3225
+ line-height: 1 !important;
3226
+ // 最后一个页签去掉右侧竖线
3227
+ &:last-child {
3228
+ border-right: none !important;
3229
+ }
383
3230
  }
384
- .tab-content{
385
- width: 720px;
3231
+ /deep/.ivu-tabs-nav .ivu-tabs-tab-active {
3232
+ background: #1890FF !important;
3233
+ color: #fff !important;
3234
+ margin: 0 !important;
3235
+ border-right: 1px solid #E5E6EB !important;
3236
+ &:last-child {
3237
+ border-right: none !important;
3238
+ }
3239
+ }
3240
+ .tab-content-pro{
3241
+ // 6:4比例 - 减去gap的一半
3242
+ width: calc(60% - 10px) !important;
3243
+ max-width: none !important; // 取消固定最大宽度,按比例适配
3244
+ flex-shrink: 0 !important; // 禁止压缩
386
3245
  border-radius: 8px;
387
3246
  border: 1px solid #EAECF0;
3247
+ // 小屏幕保持6:4比例
3248
+ @media (max-width: 1200px) {
3249
+ width: calc(60% - 10px) !important;
3250
+ }
3251
+ // 超小屏幕兜底
3252
+ @media (max-width: 768px) {
3253
+ width: 100% !important;
3254
+ margin-bottom: 10px;
3255
+ }
3256
+
388
3257
  .tab{
389
3258
  padding:20px;
390
3259
  .tag-content{
@@ -393,9 +3262,32 @@ export default {
393
3262
  display:flex;
394
3263
  align-items: center;
395
3264
  }
396
- .tree{
397
- height:450px;
3265
+ .tree {
3266
+ height: calc(100vh - 340px) !important; // 缩减40px确保按钮可见
3267
+ min-height: 360px !important; // 降低最小高度
3268
+ max-height: none !important; // 移除最大高度限制
398
3269
  margin-top:20px;
3270
+ overflow-y: auto !important; // 纵向溢出时滚动,而非超出
3271
+ overflow-x: auto !important; // 横向溢出时滚动
3272
+ border: 1px solid #EAECF0; // 明确容器边界
3273
+ border-radius: 4px;
3274
+ box-sizing: border-box; // 包含边框/内边距计算
3275
+ // 滚动条样式统一(与人员Tab保持一致)
3276
+ &::-webkit-scrollbar {
3277
+ width: 8px !important;
3278
+ height: 8px !important;
3279
+ display: block !important;
3280
+ background: #f9f9f9 !important;
3281
+ }
3282
+ &::-webkit-scrollbar-thumb {
3283
+ background: #ccc !important;
3284
+ border-radius: 4px !important;
3285
+ }
3286
+ }
3287
+ &.post .right .tree {
3288
+ height: calc(100vh - 430px) !important; // 缩减30px确保按钮可见
3289
+ min-height: 270px !important; // 降低最小高度
3290
+ max-height: none !important;
399
3291
  }
400
3292
  .staff-content{
401
3293
  overflow:auto;
@@ -404,7 +3296,12 @@ export default {
404
3296
  display:flex;
405
3297
  align-items: center;
406
3298
  justify-content: space-between;
407
- margin-top:20px;
3299
+ margin-top:15px !important; // 增加间距
3300
+ padding: 8px 0; // 内边距提升可读性
3301
+ background: #fff !important; // 白色背景覆盖树溢出内容
3302
+ position: relative;
3303
+ z-index: 10; // 层级高于树容器
3304
+ border-top: 1px solid #f0f0f0; // 视觉分隔
408
3305
  .num{
409
3306
  color:var(--primary-color);
410
3307
  }
@@ -424,17 +3321,52 @@ export default {
424
3321
  border: 1px solid #EAECF0;
425
3322
  margin-left:10px;
426
3323
  font-weight: bold;
3324
+ cursor: pointer;
427
3325
  &.active{
428
3326
  background: var(--primary-color);
429
3327
  border-radius: 4px;
430
3328
  color: #fff;
431
3329
  }
432
3330
  }
3331
+ .pop-content{
3332
+ display: flex;
3333
+ flex-wrap: wrap;
3334
+ width:500px;
3335
+ .tag{
3336
+ margin-top: 10px;
3337
+ &.active{
3338
+ background: var(--primary-color);
3339
+ border-radius: 4px;
3340
+ color: #fff;
3341
+ }
3342
+ }
3343
+ }
433
3344
  .post{
434
3345
  display: flex;
435
3346
  height: 100%;
3347
+ // 小屏幕仅调整宽度,不改变左右结构
3348
+ @media (max-width: 1200px) {
3349
+ .left {
3350
+ width: calc(50% - 10px) !important; // 同步改为50%
3351
+ }
3352
+ }
3353
+ // 超小屏幕兜底
3354
+ @media (max-width: 768px) {
3355
+ flex-direction: column;
3356
+
3357
+ .left {
3358
+ width: 100%;
3359
+ margin-bottom: 10px;
3360
+ }
3361
+
3362
+ .right {
3363
+ margin-left: 0;
3364
+ width: 100%;
3365
+ }
3366
+ }
3367
+
436
3368
  .left{
437
- width:200px;
3369
+ width: calc(50% - 10px) !important;
438
3370
  overflow: auto;
439
3371
  >div{
440
3372
  background: #FFFFFF;
@@ -455,7 +3387,8 @@ export default {
455
3387
  }
456
3388
  .right{
457
3389
  margin-left:20px;
458
- flex: 1;
3390
+ width: calc(50% - 10px) !important;
3391
+ flex: none !important;
459
3392
  }
460
3393
  }
461
3394
  .gust-item{
@@ -465,7 +3398,7 @@ export default {
465
3398
  padding: 15px 10px;
466
3399
  margin-top:10px;
467
3400
  cursor: pointer;
468
- .left-panel{
3401
+ .left-panel-pro{
469
3402
  background:url("./assets/name-bg.png") no-repeat;
470
3403
  width:30px;
471
3404
  height:30px;
@@ -476,7 +3409,7 @@ export default {
476
3409
  line-height:30px;
477
3410
  text-align:center;
478
3411
  }
479
- .right-panel{
3412
+ .right-panel-pro{
480
3413
  margin-left:10px;
481
3414
  flex:1;
482
3415
  >p:first-child{
@@ -492,20 +3425,36 @@ export default {
492
3425
  font-size:18px;
493
3426
  }
494
3427
  }
495
- .staff-active{
496
- background:#E9F4FF;
497
- >p:first-child{
498
- color: var(--primary-color);
499
- }
500
- }
3428
+ //.staff-active {
3429
+ // background: transparent !important; // 彻底移除背景色
3430
+ // border: none !important; // 彻底移除边框高亮
3431
+ // border-radius: 0 !important; // 移除圆角
3432
+ //
3433
+ // > .right-panel > p:first-child {
3434
+ // color: inherit !important; // 可选:也移除文字颜色变化,仅保留Checkbox选中
3435
+ // // 如果需要保留文字变色,删除上面这行,保留下面注释的行
3436
+ // // color: var(--primary-color);
3437
+ // }
3438
+ //}
501
3439
  }
502
3440
  .form-content{
503
- flex:1;
3441
+ // 6:4比例 - 减去gap的一半
3442
+ width: calc(40% - 10px) !important;
3443
+ min-width: 300px !important; // 最小宽度限制
504
3444
  overflow: hidden;
505
3445
  border-radius: 8px;
506
3446
  border: 1px solid #EAECF0;
507
3447
  margin-left:20px;
508
3448
  padding:10px 20px;
3449
+ height: 100%;
3450
+ flex: none !important; // 取消弹性,固定比例
3451
+ // 超小屏幕
3452
+ @media (max-width: 768px) {
3453
+ margin-left: 0;
3454
+ width: 100%;
3455
+ margin-top: 10px;
3456
+ }
3457
+
509
3458
  >p{
510
3459
  font-weight: 500;
511
3460
  font-size: 16px;
@@ -539,6 +3488,48 @@ export default {
539
3488
  .clear-icon{
540
3489
  cursor:pointer;
541
3490
  }
3491
+ .org-group {
3492
+ margin-bottom: 12px;
3493
+ &:last-child {
3494
+ margin-bottom: 0;
3495
+ }
3496
+ .org-group-title {
3497
+ display: flex;
3498
+ align-items: center;
3499
+ padding: 6px 0;
3500
+ font-size: 13px;
3501
+ font-weight: 500;
3502
+ color: #666;
3503
+ border-bottom: 1px dashed #E5E6EB;
3504
+ margin-bottom: 8px;
3505
+ .folder-icon {
3506
+ margin-right: 6px;
3507
+ color: #faad14;
3508
+ font-size: 14px;
3509
+ }
3510
+ .org-group-count {
3511
+ margin-left: 4px;
3512
+ color: #999;
3513
+ font-weight: normal;
3514
+ font-size: 12px;
3515
+ }
3516
+ }
3517
+ .org-group-items {
3518
+ padding-left: 4px;
3519
+ .list-item {
3520
+ margin-top: 6px;
3521
+ &:first-child {
3522
+ margin-top: 0;
3523
+ }
3524
+ }
3525
+ }
3526
+ }
3527
+ .org-item-name {
3528
+ flex: 1;
3529
+ overflow: hidden;
3530
+ text-overflow: ellipsis;
3531
+ white-space: nowrap;
3532
+ }
542
3533
  }
543
3534
  .list-item{
544
3535
  background:#F4F6FA;
@@ -565,4 +3556,28 @@ export default {
565
3556
  }
566
3557
  }
567
3558
  }
3559
+
3560
+ // 超小屏幕适配(手机端)
3561
+ @media (max-width: 768px) {
3562
+ .modal-tree .tree-orig {
3563
+ flex-wrap: wrap !important; // 仅超小屏幕换行
3564
+ height: calc(100vh - 100px) !important;
3565
+ min-height: 400px !important;
3566
+ }
3567
+
3568
+ .tab-content-pro .tab .tree {
3569
+ height: calc(100vh - 380px) !important;
3570
+ min-height: 300px !important;
3571
+ }
3572
+
3573
+ .tab.post .left {
3574
+ height: calc(100vh - 480px) !important;
3575
+ min-height: 200px !important;
3576
+ }
3577
+
3578
+ .staff-left-right-layout {
3579
+ height: calc(100vh - 220px) !important;
3580
+ min-height: 400px !important;
3581
+ }
3582
+ }
568
3583
  </style>