@sy-common/organize-select-help 1.0.0-beta.26 → 1.0.0-beta.29

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
@@ -121,32 +121,54 @@
121
121
  </div>
122
122
  </TabPane>
123
123
  <TabPane label="选择人员" name="staff" v-if="name.includes('staff')">
124
- <div class="tab">
125
- <Input v-model="staffSearch" @on-enter="searchStaff" @on-search="searchStaff" search placeholder="搜索人员"/>
126
- <div style="position:relative;">
127
- <div class="tree staff-content" @scroll="handleScroll">
128
- <!-- 关键修改:用“index + 下划线 + item.id”做key,确保唯一且不影响数据 -->
129
- <div :class="['gust-item',item.checked && 'staff-active']"
130
- v-for="(item, index) in staffAllList"
131
- :key="`staff_${index}_${item.id}`"
132
- @click="handlestaff(item)">
133
- <div class="left-panel">{{item.name && item.name.slice(0,1) || ''}}</div>
134
- <div class="right-panel">
135
- <p>{{item.name}}</p>
136
- <p>{{item.orgNodeName || item.orgUnitName}}</p>
124
+ <div class="tab staff-left-right-layout">
125
+ <!-- 左侧:组织选择区域(限制高度) -->
126
+ <div class="staff-left-panel">
127
+ <div class="panel-title">组织选择</div>
128
+ <div class="tree staff-org-tree">
129
+ <organizeTree
130
+ @handleChange="getStaffList"
131
+ ref="staffTree"
132
+ :treeList="staffTree"
133
+ :showCheckbox="true"
134
+ :checkStrictly="true"
135
+ :autoExpandParent="true"
136
+ :isSingleSelect="true"
137
+ ></organizeTree>
138
+ </div>
139
+ </div>
140
+
141
+ <div class="staff-right-panel">
142
+ <Input v-model="staffSearch" @on-enter="searchStaff" @on-search="searchStaff" search placeholder="搜索人员"/>
143
+ <div style="position:relative;">
144
+ <div class="tree staff-content" @scroll="handleScroll">
145
+ <div :class="['gust-item',item.checked && 'staff-active']"
146
+ v-for="(item, index) in staffAllList"
147
+ :key="`staff_${index}_${item.id}`"
148
+ @click="handlestaff(item)">
149
+ <Checkbox
150
+ v-model="item.checked"
151
+ class="staff-checkbox"
152
+ @click.stop="() => handlestaff(item)"
153
+ ></Checkbox>
154
+ <div class="left-panel">{{item.name && item.name.slice(0,1) || ''}}</div>
155
+ <div class="right-panel">
156
+ <p>{{item.name}}</p>
157
+ <p>{{item.orgNodeName || item.orgUnitName}}</p>
158
+ </div>
159
+ <!-- <div class="checked-icon" v-show="item.checked">✔</div>-->
160
+ </div>
161
+ <p v-if="staffEnding" style="color:#CCCCCC;text-align: center">---我也是有底线的---</p>
137
162
  </div>
138
- <div class="checked-icon" v-show="item.checked">✔</div>
163
+ <Spin v-if="loadingStaff" size="large" fix />
164
+ </div>
165
+ <div class="bottom-select">
166
+ <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>
167
+ <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>
139
168
  </div>
140
- <p v-if="staffEnding" style="color:#CCCCCC;text-align: center">---我也是有底线的---</p>
141
169
  </div>
142
- <Spin v-if="loadingStaff" size="large" fix />
143
- </div>
144
- <div class="bottom-select">
145
- <div>当前已选择 <span class="num">{{getCheckedStaff}}</span>人</div>
146
- <Button type="primary" icon="md-add" @click="addStaffList">添加人员</Button>
147
170
  </div>
148
- </div>
149
- </TabPane>
171
+ </TabPane>
150
172
  </Tabs>
151
173
  </div>
152
174
  <div class="form-content">
@@ -264,14 +286,69 @@ export default {
264
286
  selectedPositionId: null, // 新增:岗位单选的选中状态存储
265
287
  selectedOrgTagKey: '', // 存储选中标签的唯一标识
266
288
  selectedPostTagKey: '', // 存储选中标签的唯一标识
289
+ //人员选择
290
+ staffTree:[],
291
+ staffTagList:[],
292
+ staffOrgList:[],
293
+ staffSearchList:[],
294
+ selectedStaffOrgId: '', // 新增:选中的组织节点ID
295
+ proStaffOrgList: [] // 新增:暂存选中的组织节点
267
296
  }
268
297
  },
269
298
  mounted() {
270
299
  this.queryTagList()
271
300
  this.queryPositionList()
272
301
  // this.loadMore()
302
+ this.initStaffOrgTree()
273
303
  },
274
304
  methods:{
305
+ async initStaffOrgTree() {
306
+ try {
307
+ // 关键修复:初始化时重置选中状态
308
+ this.$set(this, 'selectedStaffOrgId', '');
309
+ this.proStaffOrgList = [];
310
+
311
+ const rootOrgId = this.defaultOrgUnitId || '';
312
+ let rootNode = null;
313
+
314
+ if (rootOrgId) {
315
+ const leafNode = await this.judgeNodeLeafe(rootOrgId);
316
+ const orgDetail = await this.queryOrgNodeDetail(rootOrgId);
317
+ rootNode = this.safeDeepCopy({
318
+ ...orgDetail,
319
+ orgNodeName: orgDetail.orgNodeName || orgDetail.name,
320
+ leafNode,
321
+ expand: true,
322
+ parentOrgUnitId: orgDetail.parentOrgUnitId || orgDetail.parentId
323
+ });
324
+ } else {
325
+ const res = await ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitList', {
326
+ params: { containsCurLevel: true }
327
+ });
328
+ if (res.data.code === 1) {
329
+ rootNode = res.data.data[0] || null;
330
+ if (rootNode) {
331
+ rootNode.expand = true;
332
+ rootNode.parentOrgUnitId = rootNode.parentId;
333
+ }
334
+ }
335
+ }
336
+
337
+ if (rootNode) {
338
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(rootNode.orgUnitId);
339
+ const safeParents = this.safeDeepCopy(parentsList);
340
+ safeParents.forEach(node => {
341
+ node.expand = true;
342
+ node.parentOrgUnitId = node.parentId;
343
+ });
344
+ const tree = this.buildTree([...safeParents, rootNode]);
345
+ this.staffTree = this.safeDeepCopy(tree);
346
+ }
347
+ } catch (e) {
348
+ this.$Message.error("人员选择组织树初始化失败!");
349
+ console.error(e);
350
+ }
351
+ },
275
352
  handlePostTagSelect(quickPickKey) {
276
353
  // 清空暂存列表
277
354
  this.proPostList = [];
@@ -490,6 +567,30 @@ export default {
490
567
  this.$Message.error("获取组织节点列表失败!");
491
568
  }
492
569
  },
570
+ async getStaffOption(val) {
571
+ this.staffOrgList = [];
572
+ if(!val) return this.$refs.staffTree.initData();
573
+ let item = this.staffSearchList.filter((item)=> item.orgUnitId === val).shift();
574
+ if (!item) return;
575
+
576
+ const leafNode = await this.judgeNodeLeafe(item.orgUnitId);
577
+ const ftem = this.safeDeepCopy({
578
+ ...item,
579
+ orgNodeName: item.name,
580
+ parentOrgUnitId: item.parentId,
581
+ leafNode: leafNode
582
+ });
583
+
584
+ try{
585
+ let parentsList = await this.getParentOrgNodesByOrgUnitId(val);
586
+ // 对父节点列表进行安全拷贝
587
+ const safeParents = this.safeDeepCopy(parentsList);
588
+ let tree = this.buildTree([...safeParents, ftem]);
589
+ this.staffTree = this.safeDeepCopy(tree);
590
+ }catch(e){
591
+ this.$Message.error("获取组织节点列表失败!");
592
+ }
593
+ },
493
594
  getParentOrgNodesByOrgUnitId(val){
494
595
  return new Promise((resolve,reject)=>{
495
596
  ajax.get('/pub-manage-server/pub/personHelpBox/q/getParentOrgNodesByOrgUnitId?orgUnitId='+val).then((res)=>{
@@ -552,28 +653,44 @@ export default {
552
653
 
553
654
  searchStaff(){
554
655
  this.staffEnding = false;
555
- this.offset = 0
556
- this.staffAllList = []
557
- this.loadMore()
656
+ this.offset = 0;
657
+ this.staffAllList = [];
658
+ // 关键修复:50ms延迟,解决极端情况下的异步更新问题
659
+ setTimeout(() => {
660
+ this.loadMore();
661
+ }, 50);
558
662
  },
559
- queryAllStaffList(){
560
- return new Promise((resolve,reject)=>{
561
- ajax.get('/pub-manage-server/pub/personHelpBox/q/queryAllStaffList',{
562
- params:{
563
- search:this.staffSearch,
564
- offset:this.offset,
565
- limit:10,
566
- baComOrgCode:this.defaultOrgUnitId
567
- }
568
- }).then((res)=>{
569
- if(res.data.code === 1){
570
- let resp = res.data.data
571
- resolve(resp)
572
- }else{
573
- reject(false)
574
- }
575
- })
576
- })
663
+ queryAllStaffList() {
664
+ const params = {
665
+ search: this.staffSearch,
666
+ offset: this.offset,
667
+ limit: 10
668
+ };
669
+
670
+ // 关键优化:实时监测选中状态,未选中时显式传递空字符串(而非不传递)
671
+ if (this.selectedStaffOrgId && typeof this.selectedStaffOrgId === 'string' && this.selectedStaffOrgId.trim()) {
672
+ params.orgUnitId = this.selectedStaffOrgId.trim();
673
+ } else {
674
+ // 未选中任何节点时,显式设置orgUnitId为空字符串
675
+ params.orgUnitId = '';
676
+ }
677
+
678
+ // 调试日志:查看最终传递的参数
679
+ console.log('人员查询接口参数:', params);
680
+
681
+ return new Promise((resolve, reject) => {
682
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/queryAllStaffList', {
683
+ params: params
684
+ }).then((res) => {
685
+ if (res.data.code === 1) {
686
+ resolve(res.data.data);
687
+ } else {
688
+ reject(false);
689
+ }
690
+ }).catch((err) => {
691
+ reject(err);
692
+ });
693
+ });
577
694
  },
578
695
  handleScroll(e){
579
696
  const { scrollTop, clientHeight, scrollHeight } = e.target;
@@ -603,45 +720,135 @@ export default {
603
720
  console.log(e)
604
721
  }
605
722
  },
606
- getOrgList(data){
607
- if (this.proOrgList){
608
- this.proOrgList = this.proOrgList.concat(data)
609
- }else {
610
- this.proOrgList = data
611
- }
723
+ getOrgList(data) {
724
+ // 过滤有效节点:仅要求orgUnitId(orgNodeName可选,用默认值兜底)
725
+ const validNodes = Array.isArray(data)
726
+ ? data.filter(node => node.orgUnitId) // 仅校验orgUnitId(必选)
727
+ : [];
728
+
729
+ // 去重:基于orgUnitId避免重复
730
+ const uniqueNodes = validNodes.filter((node, index, self) =>
731
+ self.findIndex(item => item.orgUnitId === node.orgUnitId) === index
732
+ );
733
+
734
+ // 补全缺失字段,避免后续处理报错
735
+ const completeNodes = uniqueNodes.map(node => ({
736
+ ...node,
737
+ orgNodeName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`,
738
+ orgUnitName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`
739
+ }));
740
+
741
+ // 安全深拷贝,确保数据响应式
742
+ this.proOrgList = this.safeDeepCopy(completeNodes);
743
+ },
612
744
 
745
+ getPostList(data) {
746
+ // 过滤有效节点:仅要求orgUnitId
747
+ const validNodes = Array.isArray(data)
748
+ ? data.filter(node => node.orgUnitId)
749
+ : [];
750
+
751
+ // 去重:基于orgUnitId
752
+ const uniqueNodes = validNodes.filter((node, index, self) =>
753
+ self.findIndex(item => item.orgUnitId === node.orgUnitId) === index
754
+ );
755
+
756
+ // 补全缺失字段
757
+ const completeNodes = uniqueNodes.map(node => ({
758
+ ...node,
759
+ orgNodeName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`,
760
+ orgUnitName: node.orgNodeName || node.name || `未命名组织(${node.orgUnitId})`
761
+ }));
762
+
763
+ // 覆盖暂存列表(而非concat,避免累积无效数据)
764
+ this.proPostList = this.safeDeepCopy(completeNodes);
613
765
  },
614
- getPostList(data){
615
- if (this.proPostList){
616
- this.proPostList = this.proPostList.concat(data)
617
- }else {
618
- this.proPostList = data
766
+ getStaffList(data) {
767
+ if (!Array.isArray(data) || data.length === 0) {
768
+ this.proStaffOrgList = [];
769
+ // 强制清空并触发响应式更新
770
+ this.$set(this, 'selectedStaffOrgId', '');
771
+ // 立即触发搜索,确保orgUnitId参数实时更新为空
772
+ this.$nextTick(() => this.searchStaff());
773
+ return;
619
774
  }
775
+
776
+ // 关键修复1:过滤仅含有效orgUnitId的节点,排除无效数据
777
+ const validNodes = data.filter(node =>
778
+ node.orgUnitId && typeof node.orgUnitId === 'string' // 确保orgUnitId存在且为字符串
779
+ );
780
+
781
+ if (validNodes.length === 0) {
782
+ this.$Message.warning("所选节点无效,请重新选择");
783
+ this.proStaffOrgList = [];
784
+ this.$set(this, 'selectedStaffOrgId', '');
785
+ // 立即触发搜索,更新参数为空
786
+ this.$nextTick(() => this.searchStaff());
787
+ return;
788
+ }
789
+
790
+ // 关键修复2:单选逻辑强化,仅保留最后一个有效选中节点
791
+ const currentNode = validNodes[validNodes.length - 1];
792
+ const currentOrgUnitId = currentNode.orgUnitId.trim(); // 去除空格,避免无效字符串
793
+
794
+ // 补全节点字段,确保orgUnitId绝对存在
795
+ const pureNode = this.safeDeepCopy({
796
+ ...currentNode,
797
+ orgNodeName: currentNode.orgNodeName || currentNode.name || `未命名组织(${currentOrgUnitId})`,
798
+ orgUnitName: currentNode.orgNodeName || currentNode.name || `未命名组织(${currentOrgUnitId})`
799
+ });
800
+
801
+ // 清除子节点引用,避免循环引用导致的更新延迟
802
+ if (pureNode.orgChildrenList) pureNode.orgChildrenList = [];
803
+ if (pureNode.children) pureNode.children = [];
804
+
805
+ // 关键修复3:强制响应式更新,避免Vue数据延迟
806
+ this.$set(this, 'proStaffOrgList', [pureNode]);
807
+ this.$set(this, 'selectedStaffOrgId', currentOrgUnitId);
808
+
809
+ // 关键修复4:双重nextTick确保数据完全更新后再查询
810
+ this.$nextTick(() => {
811
+ if (this.selectedStaffOrgId !== currentOrgUnitId) {
812
+ this.$set(this, 'selectedStaffOrgId', currentOrgUnitId);
813
+ this.$nextTick(() => this.searchStaff());
814
+ } else {
815
+ this.searchStaff();
816
+ }
817
+ console.log('人员选择-当前选中orgUnitId:', this.selectedStaffOrgId); // 调试日志
818
+ });
620
819
  },
820
+
621
821
  addOrgList() {
622
- if(!this.proOrgList.length) return this.$Message.error("请先选择组织节点!")
623
- let proOrgList = deepCopy(this.proOrgList)
624
- proOrgList.forEach(item=>{
625
- item.title = this.includeLevelOrg.includes('01')?`${item.orgNodeName}(包含下级组织节点)`:item.orgNodeName
626
- item.id = this.includeLevelOrg.includes('01')?('01'+'-' + item.orgUnitId):('00'+'-'+item.orgUnitId)
627
- item.includeLevel = this.includeLevelOrg.includes('01')
628
- })
629
- let list = this.orgList.concat(proOrgList)
822
+ // 过滤空节点(仅校验orgUnitId)
823
+ const validOrgList = this.proOrgList.filter(node => node.orgUnitId);
824
+ if (!validOrgList.length) return this.$Message.error("请先选择组织节点!");
825
+
826
+ let proOrgList = deepCopy(validOrgList);
827
+ proOrgList.forEach(item => {
828
+ // 用补全后的orgNodeName,避免空显示
829
+ item.title = this.includeLevelOrg.includes('01')
830
+ ? `${item.orgNodeName}(包含下级组织节点)`
831
+ : item.orgNodeName;
832
+ item.id = this.includeLevelOrg.includes('01')
833
+ ? (`01-${item.orgUnitId}`)
834
+ : (`00-${item.orgUnitId}`);
835
+ item.includeLevel = this.includeLevelOrg.includes('01');
836
+ });
837
+
838
+ // 去重合并
839
+ let list = this.orgList.concat(proOrgList);
630
840
  let uniqueArray = list.filter((item, index, self) =>
631
- index === self.findIndex(t => (
632
- t.id === item.id // 基于id属性去重
633
- ))
841
+ index === self.findIndex(t => t.id === item.id)
634
842
  );
635
- this.orgList = uniqueArray
843
+ this.orgList = uniqueArray;
636
844
 
637
- // 新增:清空组织树节点选中状态(关键优化)
845
+ // 清空选中状态
638
846
  if (this.$refs.orgTree && this.$refs.orgTree.clearAllChecked) {
639
847
  this.$refs.orgTree.clearAllChecked(this.$refs.orgTree.data);
640
- this.$refs.orgTree.$emit('handleChange', []); // 通知父组件清空暂存列表
848
+ this.$refs.orgTree.$emit('handleChange', []);
641
849
  }
642
-
643
- this.$refs.orgTree.upDataTree()
644
- this.proOrgList = []
850
+ this.$refs.orgTree.upDataTree();
851
+ this.proOrgList = [];
645
852
  },
646
853
  clearGroup(){
647
854
  this.orgList = []
@@ -655,39 +862,42 @@ export default {
655
862
  this.selectedPositionId = item.positionId
656
863
  },
657
864
  addPostList() {
658
- if(!this.proPostList.length) return this.$Message.error("请选择组织节点!")
659
- let proPostList = deepCopy(this.proPostList)
865
+ // 过滤空节点
866
+ const validPostList = this.proPostList.filter(node => node.orgUnitId);
867
+ if (!validPostList.length) return this.$Message.error("请选择组织节点!");
868
+
660
869
  let checkedPosition = this.positiontList.find(item => item.positionId === this.selectedPositionId);
661
- if(!checkedPosition){return this.$Message.error("请选择岗位!!")}
662
- // summary data
663
- let totalList = []
664
- proPostList.forEach(item=>{
870
+ if (!checkedPosition) { return this.$Message.error("请选择岗位!"); }
871
+
872
+ let totalList = [];
873
+ validPostList.forEach(item => {
665
874
  totalList.push({
666
875
  ...item,
667
876
  ...checkedPosition,
668
- title:`${this.includeLevelPost.length?(item.orgNodeName+'(包含下级组织节点)'):item.orgNodeName}`,
669
- includeLevel:this.includeLevelPost.includes('01'),
670
- id:this.includeLevelPost.includes('01')?('01'+'-'+checkedPosition.positionId + '-' + item.orgUnitId):('00'+'-'+checkedPosition.positionId + '-' + item.orgUnitId),
671
- })
672
- })
673
- let list = this.postList.concat(totalList)
877
+ title: `${this.includeLevelPost.length ? (item.orgNodeName + '(包含下级组织节点)') : item.orgNodeName}`,
878
+ includeLevel: this.includeLevelPost.includes('01'),
879
+ id: this.includeLevelPost.includes('01')
880
+ ? (`01-${checkedPosition.positionId}-${item.orgUnitId}`)
881
+ : (`00-${checkedPosition.positionId}-${item.orgUnitId}`),
882
+ });
883
+ });
884
+
885
+ // 去重合并
886
+ let list = this.postList.concat(totalList);
674
887
  let uniqueArray = list.filter((item, index, self) =>
675
- index === self.findIndex(t => (
676
- t.id === item.id // 基于id属性去重
677
- ))
888
+ index === self.findIndex(t => t.id === item.id)
678
889
  );
679
- this.postList = uniqueArray
890
+ this.postList = uniqueArray;
680
891
 
681
- // 新增:清空岗位树节点选中状态(关键优化)
892
+ // 清空选中状态
682
893
  if (this.$refs.postTree && this.$refs.postTree.clearAllChecked) {
683
894
  this.$refs.postTree.clearAllChecked(this.$refs.postTree.data);
684
- this.$refs.postTree.$emit('handleChange', []); // 通知父组件清空暂存列表
895
+ this.$refs.postTree.$emit('handleChange', []);
685
896
  }
686
-
687
- this.$refs.postTree.upDataTree()
688
- this.proPostList = []
689
- this.selectedPositionId = null; // 清空岗位单选状态
690
- this.$refs.postTree.initData()
897
+ this.$refs.postTree.upDataTree();
898
+ this.proPostList = [];
899
+ this.selectedPositionId = null;
900
+ // this.$refs.postTree.initData();
691
901
  this.selectedPostTagKey = '';
692
902
  },
693
903
  clearPost(){
@@ -699,14 +909,18 @@ export default {
699
909
  //staff
700
910
  addStaffList(){
701
911
  let staffList = this.staffAllList.filter((item)=>item.checked===true);
912
+ if (!staffList.length) {
913
+ this.$Message.error("请选择人员");
914
+ return;
915
+ }
702
916
  // 关键优化:基于item.id去重,不修改原数据字段,仅过滤重复项
703
917
  let uniqueStaffList = staffList.filter(newItem =>
704
918
  // 检查右侧条件区域(staffList)是否已有该人员,避免重复添加
705
919
  !this.staffList.some(existItem => existItem.userId === newItem.userId)
706
920
  );
707
- // 合并去重后的列表,右侧条件区域数据结构完全不变
921
+ // 合并去重后的列表
708
922
  this.staffList = this.staffList.concat(uniqueStaffList);
709
- // 清空选中状态(保持原有逻辑)
923
+ // 清空选中状态
710
924
  this.staffAllList.forEach(item => item.checked = false);
711
925
  },
712
926
  handlestaff(item){
@@ -726,8 +940,8 @@ export default {
726
940
  }
727
941
  this.$emit('confirm',deepCopy(v))
728
942
  },
729
- visibleChange(val){
730
- this.$emit('input',val);
943
+ visibleChange(val) {
944
+ this.$emit('input', val);
731
945
  // val 为 true 时表示帮助框打开,此时发起请求
732
946
  if (val) {
733
947
  this.initStaffList() // 新增初始化方法,统一处理打开后的请求逻辑
@@ -736,19 +950,38 @@ export default {
736
950
  this.staffEnding = false;
737
951
  this.offset = 0;
738
952
  this.staffAllList = [];
953
+ // 关闭时重置选中状态
954
+ this.$set(this, 'selectedStaffOrgId', '');
739
955
  }
740
956
  },
741
957
  initStaffList() {
742
958
  this.staffEnding = false;
743
959
  this.offset = 0;
744
960
  this.staffAllList = [];
961
+ // 初始化时确保selectedStaffOrgId状态正确
962
+ if (this.tabName === 'staff' && (!this.selectedStaffOrgId || this.selectedStaffOrgId.trim())) {
963
+ this.$set(this, 'selectedStaffOrgId', '');
964
+ }
745
965
  this.loadMore(); // 打开帮助框后,再发起第一次请求
746
966
  },
747
967
  // 处理Tab切换,同步更新tabName
748
968
  handleTabChange(tabName) {
749
969
  this.tabName = tabName;
750
- // 可选:切换到岗位Tab时,初始化岗位树选中状态(避免残留组织Tab数据)
751
- if (tabName === 'post' && this.$refs.postTree && this.proPostList.length === 0) {
970
+
971
+ // 仅修改人员Tab相关逻辑,不影响其他Tab
972
+ if (tabName === 'staff') {
973
+ // 重置人员选择相关状态
974
+ this.$set(this, 'selectedStaffOrgId', '');
975
+ this.proStaffOrgList = [];
976
+ // 清空树组件选中状态
977
+ if (this.$refs.staffTree) {
978
+ this.$refs.staffTree.clearAllChecked(this.$refs.staffTree.data);
979
+ this.$refs.staffTree.$emit('handleChange', []);
980
+ }
981
+ // 切换到人员tab时,立即触发一次搜索,确保orgUnitId参数正确
982
+ this.$nextTick(() => this.searchStaff());
983
+ } else if (tabName === 'post' && this.$refs.postTree && this.proPostList.length === 0) {
984
+ // 保留原有岗位Tab逻辑,不修改
752
985
  this.proPostList = [];
753
986
  this.$refs.postTree.initData();
754
987
  }
@@ -1099,9 +1332,14 @@ export default {
1099
1332
  return obj;
1100
1333
  }
1101
1334
 
1102
- // 检测到循环引用时,仅保留基础字段
1335
+ // 检测到循环引用时,保留核心字段(orgUnitId必选,其他字段尽可能保留)
1103
1336
  if (hash.has(obj)) {
1104
- const safeObj = { orgUnitId: obj.orgUnitId, name: obj.name || obj.orgNodeName };
1337
+ const safeObj = {
1338
+ orgUnitId: obj.orgUnitId,
1339
+ orgNodeName: obj.orgNodeName || obj.name || `未命名组织(${obj.orgUnitId || 'unknown'})`,
1340
+ orgUnitName: obj.orgUnitName || obj.orgNodeName || obj.name || `未命名组织(${obj.orgUnitId || 'unknown'})`,
1341
+ parentOrgUnitId: obj.parentOrgUnitId || obj.parentId || null
1342
+ };
1105
1343
  hash.set(obj, safeObj);
1106
1344
  return safeObj;
1107
1345
  }
@@ -1118,14 +1356,15 @@ export default {
1118
1356
  hash.set(obj, copy);
1119
1357
  for (let key in obj) {
1120
1358
  if (obj.hasOwnProperty(key)) {
1121
- // 严格过滤可能导致循环的字段
1122
- if (['parent', '__parent', 'parentNode', '__vue__', '__ob__', '$parent'].includes(key)) {
1359
+ // 仅过滤vue内部字段,保留业务字段
1360
+ if (['__vue__', '__ob__', '$parent', '$children'].includes(key)) {
1123
1361
  continue;
1124
1362
  }
1125
- // 对子节点数组进行特殊处理
1363
+ // 子节点数组特殊处理
1126
1364
  if (['orgChildrenList', 'children'].includes(key) && Array.isArray(obj[key])) {
1127
1365
  copy[key] = obj[key].map(child => this.safeDeepCopy(child, hash));
1128
1366
  } else {
1367
+ // 保留所有业务字段,不额外过滤
1129
1368
  copy[key] = this.safeDeepCopy(obj[key], hash);
1130
1369
  }
1131
1370
  }
@@ -1176,25 +1415,20 @@ export default {
1176
1415
  return null;
1177
1416
  }
1178
1417
 
1179
- // 用队列实现广度优先搜索(BFS),避免递归栈溢出
1180
1418
  const queue = [...treeData];
1181
1419
  while (queue.length > 0) {
1182
- const currentNode = queue.shift(); // 取出队列头部节点
1183
- // 找到目标节点,直接返回
1184
- if (currentNode.orgUnitId === targetOrgUnitId) {
1420
+ const currentNode = queue.shift();
1421
+ // 兼容子组件可能的字段映射(orgUnitId可能存储在id字段)
1422
+ const nodeId = currentNode.orgUnitId || currentNode.id || '';
1423
+ if (nodeId === targetOrgUnitId) {
1185
1424
  return currentNode;
1186
1425
  }
1187
- // 将当前节点的子节点加入队列(继续查找)
1188
- if (Array.isArray(currentNode.orgChildrenList) && currentNode.orgChildrenList.length > 0) {
1189
- queue.push(...currentNode.orgChildrenList);
1190
- }
1191
- // 兼容子组件可能使用的children字段
1192
- if (Array.isArray(currentNode.children) && currentNode.children.length > 0 && !currentNode.orgChildrenList) {
1193
- queue.push(...currentNode.children);
1426
+ // 兼容children和orgChildrenList两种子节点字段
1427
+ const childNodes = currentNode.orgChildrenList || currentNode.children || [];
1428
+ if (childNodes.length > 0) {
1429
+ queue.push(...childNodes);
1194
1430
  }
1195
1431
  }
1196
-
1197
- // 遍历完所有节点未找到
1198
1432
  return null;
1199
1433
  },
1200
1434
 
@@ -1316,6 +1550,146 @@ export default {
1316
1550
  }
1317
1551
  </script>
1318
1552
  <style lang="less" scoped>
1553
+ // 新增checkbox样式
1554
+ .staff-checkbox {
1555
+ margin-right: 12px;
1556
+ flex-shrink: 0;
1557
+
1558
+ /deep/ .ivu-checkbox-wrapper {
1559
+ display: flex;
1560
+ align-items: center;
1561
+ cursor: pointer;
1562
+ }
1563
+
1564
+ /deep/ .ivu-checkbox {
1565
+ width: 18px;
1566
+ height: 18px;
1567
+
1568
+ &:checked {
1569
+ /deep/ .ivu-checkbox-inner {
1570
+ background-color: var(--primary-color);
1571
+ border-color: var(--primary-color);
1572
+ }
1573
+ }
1574
+ }
1575
+ }
1576
+ .staff-left-right-layout {
1577
+ display: flex;
1578
+ height: 600px;
1579
+ gap: 15px;
1580
+ padding: 0 5px;
1581
+ width: 100%;
1582
+ box-sizing: border-box;
1583
+
1584
+ .staff-left-panel {
1585
+ min-width: 300px;
1586
+ max-width: 350px;
1587
+ width: 320px;
1588
+ display: flex;
1589
+ flex-direction: column;
1590
+ box-sizing: border-box;
1591
+ flex-shrink: 0;
1592
+
1593
+ .panel-title {
1594
+ font-weight: 600;
1595
+ font-size: 15px;
1596
+ color: #1f2937;
1597
+ margin-bottom: 10px;
1598
+ height: 28px;
1599
+ display: flex;
1600
+ align-items: center;
1601
+ position: relative;
1602
+ padding-left: 18px; // 内边距同步加宽2px,保持视觉平衡
1603
+ // 调整竖线宽度为6px,更醒目且仍符合整体简约风格
1604
+ &::before {
1605
+ content: '';
1606
+ position: absolute;
1607
+ left: 0;
1608
+ top: 50%;
1609
+ transform: translateY(-50%);
1610
+ width: 6px; // 从3px调整为6px,加宽竖线
1611
+ height: 18px; // 高度同步微调至18px,比例更协调
1612
+ background-color: var(--primary-color);
1613
+ border-radius: 3px; // 圆角保持3px,视觉更柔和
1614
+ }
1615
+ }
1616
+
1617
+ .staff-org-tree {
1618
+ height: calc(100% - 38px) !important; // 对应title高度+margin-bottom调整
1619
+ overflow: auto;
1620
+ border: 1px solid #EAECF0;
1621
+ border-radius: 4px;
1622
+ padding: 8px;
1623
+ box-sizing: border-box;
1624
+ // 保持原有树节点样式
1625
+ /deep/ .ivu-tree-node-content {
1626
+ white-space: normal !important;
1627
+ line-height: 1.4;
1628
+ padding: 4px 0;
1629
+ }
1630
+ /deep/ .ivu-tree-node {
1631
+ padding-left: 4px !important;
1632
+ }
1633
+ /deep/ .ivu-tree-child-tree {
1634
+ padding-left: 16px !important;
1635
+ }
1636
+ }
1637
+ }
1638
+
1639
+ .staff-right-panel {
1640
+ flex: 1 !important;
1641
+ min-width: 350px;
1642
+ width: calc(100% - 335px) !important;
1643
+ display: flex;
1644
+ flex-direction: column;
1645
+ box-sizing: border-box;
1646
+ gap: 8px;
1647
+ padding: 0 5px;
1648
+ flex-shrink: 0;
1649
+
1650
+ // 其他右侧样式保持不变...
1651
+ }
1652
+ }
1653
+
1654
+ // 其他原有样式保持不变
1655
+ /deep/ .ivu-tree {
1656
+ width: 100% !important;
1657
+ box-sizing: border-box;
1658
+ }
1659
+
1660
+ /deep/ .ivu-tree-node-content {
1661
+ width: 100% !important;
1662
+ box-sizing: border-box;
1663
+ }
1664
+
1665
+ .gust-item {
1666
+ display: flex;
1667
+ align-items: center;
1668
+ width: 100% !important;
1669
+ padding: 12px 12px;
1670
+ margin-top: 8px;
1671
+ cursor: pointer;
1672
+ box-sizing: border-box;
1673
+ border: none !important; // 强制移除默认边框
1674
+ }
1675
+
1676
+ // 恢复滚动条样式(确保滚动可见)
1677
+ .staff-content::-webkit-scrollbar {
1678
+ width: 8px !important;
1679
+ height: 8px;
1680
+ display: block !important;
1681
+ }
1682
+
1683
+ .staff-content::-webkit-scrollbar-thumb {
1684
+ background-color: #ccc !important;
1685
+ border-radius: 4px !important;
1686
+ }
1687
+
1688
+ .staff-content::-webkit-scrollbar-track {
1689
+ background-color: #f9f9f9 !important;
1690
+ }
1691
+
1692
+ // 其他原有样式保持不变
1319
1693
  .tab.post .left {
1320
1694
  width: 200px;
1321
1695
  height: 450px !important;
@@ -1325,38 +1699,35 @@ export default {
1325
1699
  }
1326
1700
 
1327
1701
  .scroll-container {
1328
- height: 100% !important; /* 完全填充父容器高度 */
1329
- overflow-y: auto !important; /* 内容超出时显示滚动条,不足时隐藏(也可设为scroll强制显示) */
1702
+ height: 100% !important;
1703
+ overflow-y: auto !important;
1330
1704
  overflow-x: hidden !important;
1331
1705
  padding: 8px 0;
1332
- /* 消除可能的内边距/外边距影响 */
1333
1706
  margin: 0;
1334
1707
  box-sizing: border-box;
1335
1708
  }
1336
1709
 
1337
- /* 3. 强化滚动条样式,确保可见性(兼容webkit浏览器) */
1338
1710
  .scroll-container::-webkit-scrollbar {
1339
- width: 8px !important; /* 滚动条宽度,确保肉眼可见 */
1711
+ width: 8px !important;
1340
1712
  height: 8px;
1341
- display: block !important; /* 强制显示滚动条容器,避免被隐藏 */
1713
+ display: block !important;
1342
1714
  }
1343
1715
 
1344
1716
  .scroll-container::-webkit-scrollbar-thumb {
1345
- background-color: #ccc !important; /* 滑块颜色,与背景区分明显 */
1717
+ background-color: #ccc !important;
1346
1718
  border-radius: 4px !important;
1347
1719
  transition: background-color 0.2s;
1348
1720
  }
1349
1721
 
1350
1722
  .scroll-container::-webkit-scrollbar-thumb:hover {
1351
- background-color: #999 !important; /* hover时加深,增强交互感 */
1723
+ background-color: #999 !important;
1352
1724
  }
1353
1725
 
1354
1726
  .scroll-container::-webkit-scrollbar-track {
1355
- background-color: #f9f9f9 !important; /* 轨道颜色,与容器背景区分 */
1727
+ background-color: #f9f9f9 !important;
1356
1728
  border-radius: 4px !important;
1357
1729
  }
1358
1730
 
1359
- /* 4. 保持position-item原有样式,确保布局正常 */
1360
1731
  .position-item {
1361
1732
  border: 1px solid #ddd;
1362
1733
  margin: 5px 0;
@@ -1364,13 +1735,12 @@ export default {
1364
1735
  border-radius: 2px;
1365
1736
  cursor: pointer;
1366
1737
  transition: background-color 0.2s;
1367
- /* 取消文字截断,允许自动换行 */
1368
- white-space: normal !important; /* 关键:取消强制不换行,恢复默认自动换行 */
1369
- overflow: visible !important; /* 关键:取消内容隐藏,展示全部文字 */
1370
- text-overflow: clip !important; /* 取消省略号,显示完整文字 */
1371
- line-height: 1.4; /* 优化行高,换行后文字更易读 */
1372
- min-height: 40px; /* 给个最小高度,避免内容过少时容器过扁 */
1373
- word-wrap: break-word; /* 兼容长英文/数字,强制换行(防止横向溢出) */
1738
+ white-space: normal !important;
1739
+ overflow: visible !important;
1740
+ text-overflow: clip !important;
1741
+ line-height: 1.4;
1742
+ min-height: 40px;
1743
+ word-wrap: break-word;
1374
1744
  }
1375
1745
 
1376
1746
  .position-item.active {
@@ -1381,45 +1751,42 @@ export default {
1381
1751
  .position-item:hover:not(.active) {
1382
1752
  background-color: #f5f5f5;
1383
1753
  }
1754
+
1384
1755
  .custom-select-wrapper {
1385
1756
  position: relative;
1386
1757
 
1387
- /* 优化清空按钮样式 */
1388
1758
  /deep/ .ivu-select-clear {
1389
- right: 32px; /* 调整位置,避免与下拉箭头重叠 */
1759
+ right: 32px;
1390
1760
  color: #999;
1391
1761
  font-size: 16px;
1392
- opacity: 1 !important; /* 始终显示(有值时) */
1762
+ opacity: 1 !important;
1393
1763
  transition: all 0.2s ease;
1394
1764
 
1395
1765
  &:hover {
1396
- color: #ff4d4f; /* hover时变红 */
1397
- transform: scale(1.1); /* 轻微放大效果 */
1766
+ color: #ff4d4f;
1767
+ transform: scale(1.1);
1398
1768
  }
1399
1769
  }
1400
1770
 
1401
- /* 调整下拉箭头位置 */
1402
1771
  /deep/ .ivu-select-arrow {
1403
1772
  right: 12px;
1404
1773
  }
1405
1774
 
1406
- /* 优化输入框内边距,避免文字被按钮遮挡 */
1407
1775
  /deep/ .ivu-select-input {
1408
1776
  padding-right: 45px !important;
1409
1777
  }
1410
1778
 
1411
- /* 选中选项样式 */
1412
1779
  /deep/ .active-option {
1413
1780
  background-color: #f2f8ff;
1414
1781
  color: var(--primary-color);
1415
1782
  }
1416
1783
 
1417
- /* 无数据选项样式 */
1418
1784
  /deep/ .ivu-select-option-disabled {
1419
1785
  color: #ccc !important;
1420
1786
  background: #f5f5f5 !important;
1421
1787
  }
1422
1788
  }
1789
+
1423
1790
  .tag-select-container {
1424
1791
  margin-left: 10px;
1425
1792
 
@@ -1439,6 +1806,7 @@ export default {
1439
1806
  text-overflow: ellipsis;
1440
1807
  }
1441
1808
  }
1809
+
1442
1810
  .modal-tree{
1443
1811
  .header-text{
1444
1812
  font-weight: bold;
@@ -1597,10 +1965,15 @@ export default {
1597
1965
  font-size:18px;
1598
1966
  }
1599
1967
  }
1600
- .staff-active{
1601
- background:#E9F4FF;
1602
- >p:first-child{
1603
- color: var(--primary-color);
1968
+ .staff-active {
1969
+ background: transparent !important; // 彻底移除背景色
1970
+ border: none !important; // 彻底移除边框高亮
1971
+ border-radius: 0 !important; // 移除圆角
1972
+
1973
+ > .right-panel > p:first-child {
1974
+ color: inherit !important; // 可选:也移除文字颜色变化,仅保留Checkbox选中
1975
+ // 如果需要保留文字变色,删除上面这行,保留下面注释的行
1976
+ // color: var(--primary-color);
1604
1977
  }
1605
1978
  }
1606
1979
  }