@sy-common/organize-select-help 1.0.0-beta.25 → 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
- this.loadMore()
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
+ }));
612
740
 
741
+ // 安全深拷贝,确保数据响应式
742
+ this.proOrgList = this.safeDeepCopy(completeNodes);
613
743
  },
614
- getPostList(data){
615
- if (this.proPostList){
616
- this.proPostList = this.proPostList.concat(data)
617
- }else {
618
- this.proPostList = data
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);
765
+ },
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,14 +940,48 @@ 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);
945
+ // val 为 true 时表示帮助框打开,此时发起请求
946
+ if (val) {
947
+ this.initStaffList() // 新增初始化方法,统一处理打开后的请求逻辑
948
+ } else {
949
+ // 可选:关闭帮助框时重置人员列表相关状态,避免下次打开残留旧数据
950
+ this.staffEnding = false;
951
+ this.offset = 0;
952
+ this.staffAllList = [];
953
+ // 关闭时重置选中状态
954
+ this.$set(this, 'selectedStaffOrgId', '');
955
+ }
956
+ },
957
+ initStaffList() {
958
+ this.staffEnding = false;
959
+ this.offset = 0;
960
+ this.staffAllList = [];
961
+ // 初始化时确保selectedStaffOrgId状态正确
962
+ if (this.tabName === 'staff' && (!this.selectedStaffOrgId || this.selectedStaffOrgId.trim())) {
963
+ this.$set(this, 'selectedStaffOrgId', '');
964
+ }
965
+ this.loadMore(); // 打开帮助框后,再发起第一次请求
731
966
  },
732
967
  // 处理Tab切换,同步更新tabName
733
968
  handleTabChange(tabName) {
734
969
  this.tabName = tabName;
735
- // 可选:切换到岗位Tab时,初始化岗位树选中状态(避免残留组织Tab数据)
736
- 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逻辑,不修改
737
985
  this.proPostList = [];
738
986
  this.$refs.postTree.initData();
739
987
  }
@@ -1084,9 +1332,14 @@ export default {
1084
1332
  return obj;
1085
1333
  }
1086
1334
 
1087
- // 检测到循环引用时,仅保留基础字段
1335
+ // 检测到循环引用时,保留核心字段(orgUnitId必选,其他字段尽可能保留)
1088
1336
  if (hash.has(obj)) {
1089
- 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
+ };
1090
1343
  hash.set(obj, safeObj);
1091
1344
  return safeObj;
1092
1345
  }
@@ -1103,14 +1356,15 @@ export default {
1103
1356
  hash.set(obj, copy);
1104
1357
  for (let key in obj) {
1105
1358
  if (obj.hasOwnProperty(key)) {
1106
- // 严格过滤可能导致循环的字段
1107
- if (['parent', '__parent', 'parentNode', '__vue__', '__ob__', '$parent'].includes(key)) {
1359
+ // 仅过滤vue内部字段,保留业务字段
1360
+ if (['__vue__', '__ob__', '$parent', '$children'].includes(key)) {
1108
1361
  continue;
1109
1362
  }
1110
- // 对子节点数组进行特殊处理
1363
+ // 子节点数组特殊处理
1111
1364
  if (['orgChildrenList', 'children'].includes(key) && Array.isArray(obj[key])) {
1112
1365
  copy[key] = obj[key].map(child => this.safeDeepCopy(child, hash));
1113
1366
  } else {
1367
+ // 保留所有业务字段,不额外过滤
1114
1368
  copy[key] = this.safeDeepCopy(obj[key], hash);
1115
1369
  }
1116
1370
  }
@@ -1161,25 +1415,20 @@ export default {
1161
1415
  return null;
1162
1416
  }
1163
1417
 
1164
- // 用队列实现广度优先搜索(BFS),避免递归栈溢出
1165
1418
  const queue = [...treeData];
1166
1419
  while (queue.length > 0) {
1167
- const currentNode = queue.shift(); // 取出队列头部节点
1168
- // 找到目标节点,直接返回
1169
- if (currentNode.orgUnitId === targetOrgUnitId) {
1420
+ const currentNode = queue.shift();
1421
+ // 兼容子组件可能的字段映射(orgUnitId可能存储在id字段)
1422
+ const nodeId = currentNode.orgUnitId || currentNode.id || '';
1423
+ if (nodeId === targetOrgUnitId) {
1170
1424
  return currentNode;
1171
1425
  }
1172
- // 将当前节点的子节点加入队列(继续查找)
1173
- if (Array.isArray(currentNode.orgChildrenList) && currentNode.orgChildrenList.length > 0) {
1174
- queue.push(...currentNode.orgChildrenList);
1175
- }
1176
- // 兼容子组件可能使用的children字段
1177
- if (Array.isArray(currentNode.children) && currentNode.children.length > 0 && !currentNode.orgChildrenList) {
1178
- queue.push(...currentNode.children);
1426
+ // 兼容children和orgChildrenList两种子节点字段
1427
+ const childNodes = currentNode.orgChildrenList || currentNode.children || [];
1428
+ if (childNodes.length > 0) {
1429
+ queue.push(...childNodes);
1179
1430
  }
1180
1431
  }
1181
-
1182
- // 遍历完所有节点未找到
1183
1432
  return null;
1184
1433
  },
1185
1434
 
@@ -1301,6 +1550,146 @@ export default {
1301
1550
  }
1302
1551
  </script>
1303
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
+ // 其他原有样式保持不变
1304
1693
  .tab.post .left {
1305
1694
  width: 200px;
1306
1695
  height: 450px !important;
@@ -1310,38 +1699,35 @@ export default {
1310
1699
  }
1311
1700
 
1312
1701
  .scroll-container {
1313
- height: 100% !important; /* 完全填充父容器高度 */
1314
- overflow-y: auto !important; /* 内容超出时显示滚动条,不足时隐藏(也可设为scroll强制显示) */
1702
+ height: 100% !important;
1703
+ overflow-y: auto !important;
1315
1704
  overflow-x: hidden !important;
1316
1705
  padding: 8px 0;
1317
- /* 消除可能的内边距/外边距影响 */
1318
1706
  margin: 0;
1319
1707
  box-sizing: border-box;
1320
1708
  }
1321
1709
 
1322
- /* 3. 强化滚动条样式,确保可见性(兼容webkit浏览器) */
1323
1710
  .scroll-container::-webkit-scrollbar {
1324
- width: 8px !important; /* 滚动条宽度,确保肉眼可见 */
1711
+ width: 8px !important;
1325
1712
  height: 8px;
1326
- display: block !important; /* 强制显示滚动条容器,避免被隐藏 */
1713
+ display: block !important;
1327
1714
  }
1328
1715
 
1329
1716
  .scroll-container::-webkit-scrollbar-thumb {
1330
- background-color: #ccc !important; /* 滑块颜色,与背景区分明显 */
1717
+ background-color: #ccc !important;
1331
1718
  border-radius: 4px !important;
1332
1719
  transition: background-color 0.2s;
1333
1720
  }
1334
1721
 
1335
1722
  .scroll-container::-webkit-scrollbar-thumb:hover {
1336
- background-color: #999 !important; /* hover时加深,增强交互感 */
1723
+ background-color: #999 !important;
1337
1724
  }
1338
1725
 
1339
1726
  .scroll-container::-webkit-scrollbar-track {
1340
- background-color: #f9f9f9 !important; /* 轨道颜色,与容器背景区分 */
1727
+ background-color: #f9f9f9 !important;
1341
1728
  border-radius: 4px !important;
1342
1729
  }
1343
1730
 
1344
- /* 4. 保持position-item原有样式,确保布局正常 */
1345
1731
  .position-item {
1346
1732
  border: 1px solid #ddd;
1347
1733
  margin: 5px 0;
@@ -1349,13 +1735,12 @@ export default {
1349
1735
  border-radius: 2px;
1350
1736
  cursor: pointer;
1351
1737
  transition: background-color 0.2s;
1352
- /* 取消文字截断,允许自动换行 */
1353
- white-space: normal !important; /* 关键:取消强制不换行,恢复默认自动换行 */
1354
- overflow: visible !important; /* 关键:取消内容隐藏,展示全部文字 */
1355
- text-overflow: clip !important; /* 取消省略号,显示完整文字 */
1356
- line-height: 1.4; /* 优化行高,换行后文字更易读 */
1357
- min-height: 40px; /* 给个最小高度,避免内容过少时容器过扁 */
1358
- 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;
1359
1744
  }
1360
1745
 
1361
1746
  .position-item.active {
@@ -1366,45 +1751,42 @@ export default {
1366
1751
  .position-item:hover:not(.active) {
1367
1752
  background-color: #f5f5f5;
1368
1753
  }
1754
+
1369
1755
  .custom-select-wrapper {
1370
1756
  position: relative;
1371
1757
 
1372
- /* 优化清空按钮样式 */
1373
1758
  /deep/ .ivu-select-clear {
1374
- right: 32px; /* 调整位置,避免与下拉箭头重叠 */
1759
+ right: 32px;
1375
1760
  color: #999;
1376
1761
  font-size: 16px;
1377
- opacity: 1 !important; /* 始终显示(有值时) */
1762
+ opacity: 1 !important;
1378
1763
  transition: all 0.2s ease;
1379
1764
 
1380
1765
  &:hover {
1381
- color: #ff4d4f; /* hover时变红 */
1382
- transform: scale(1.1); /* 轻微放大效果 */
1766
+ color: #ff4d4f;
1767
+ transform: scale(1.1);
1383
1768
  }
1384
1769
  }
1385
1770
 
1386
- /* 调整下拉箭头位置 */
1387
1771
  /deep/ .ivu-select-arrow {
1388
1772
  right: 12px;
1389
1773
  }
1390
1774
 
1391
- /* 优化输入框内边距,避免文字被按钮遮挡 */
1392
1775
  /deep/ .ivu-select-input {
1393
1776
  padding-right: 45px !important;
1394
1777
  }
1395
1778
 
1396
- /* 选中选项样式 */
1397
1779
  /deep/ .active-option {
1398
1780
  background-color: #f2f8ff;
1399
1781
  color: var(--primary-color);
1400
1782
  }
1401
1783
 
1402
- /* 无数据选项样式 */
1403
1784
  /deep/ .ivu-select-option-disabled {
1404
1785
  color: #ccc !important;
1405
1786
  background: #f5f5f5 !important;
1406
1787
  }
1407
1788
  }
1789
+
1408
1790
  .tag-select-container {
1409
1791
  margin-left: 10px;
1410
1792
 
@@ -1424,6 +1806,7 @@ export default {
1424
1806
  text-overflow: ellipsis;
1425
1807
  }
1426
1808
  }
1809
+
1427
1810
  .modal-tree{
1428
1811
  .header-text{
1429
1812
  font-weight: bold;
@@ -1582,10 +1965,15 @@ export default {
1582
1965
  font-size:18px;
1583
1966
  }
1584
1967
  }
1585
- .staff-active{
1586
- background:#E9F4FF;
1587
- >p:first-child{
1588
- 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);
1589
1977
  }
1590
1978
  }
1591
1979
  }