@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.
@@ -1,129 +1,160 @@
1
1
  <template>
2
2
  <div class="content">
3
3
  <Spin fix v-if="loading"></Spin>
4
- <Tree :data="data" ref="tree" :load-data="loadData" :render="renderContent" check-directly
5
- @on-select-change ="handleChange" multiple></Tree>
4
+ <Tree
5
+ :data="data"
6
+ ref="tree"
7
+ :load-data="loadData"
8
+ :render="renderContent"
9
+ multiple
10
+ :check-strictly="true"
11
+ :cascade-check="false"
12
+ @on-select-change="handleChange"
13
+ ></Tree>
6
14
  </div>
7
15
  </template>
16
+
8
17
  <script>
9
18
  import { deepCopy } from '@lambo-design/core/src/utils/assist';
10
- import ajax from '@lambo-design/shared/utils/ajax'
19
+ import ajax from '@lambo-design/shared/utils/ajax';
20
+
11
21
  export default {
12
- props:{
13
- disabled:{
22
+ props: {
23
+ disabled: {
14
24
  type: Boolean,
15
25
  default: false
16
26
  },
17
- treeList:{
27
+ treeList: {
18
28
  type: Array,
19
29
  default: () => []
30
+ },
31
+ isSingleSelect: {
32
+ type: Boolean,
33
+ default: false
20
34
  }
21
35
  },
22
- data(){
36
+ data() {
23
37
  return {
24
- data:[],
25
- manageUnitId:'',
26
- loading:true
27
- }
38
+ data: [],
39
+ manageUnitId: '',
40
+ loading: true
41
+ };
28
42
  },
29
43
  mounted() {
30
- this.initData()
44
+ this.initData();
31
45
  },
32
- methods:{
33
- async initData(){
34
- let data = await this.getOrgChildren()
35
- this.loading = false
46
+ methods: {
47
+ async initData() {
48
+ let data = await this.getOrgChildren();
49
+ this.loading = false;
36
50
  this.data = data;
37
51
  },
52
+
38
53
  handleChange(data) {
39
- // 过滤出当前所有选中的节点(排除未选中的)
40
- const checkedData = Array.isArray(data)
54
+ let checkedData = Array.isArray(data)
41
55
  ? data.filter(item => item.checked)
42
56
  : (data.checked ? [data] : []);
43
- console.log(data,'checkedData')
44
- // 同步更新子组件内部所有节点的checked状态(防止状态不一致)
45
- this.syncAllNodeCheckedState(checkedData);
46
57
 
47
- // 关键:无论选中/取消,都将最新选中列表传给父组件
58
+ // 单选逻辑:仅保留最后一个选中的节点
59
+ if (this.isSingleSelect) {
60
+ checkedData = checkedData.length > 0 ? [checkedData[checkedData.length - 1]] : [];
61
+ // 清空其他节点的选中状态
62
+ this.clearOtherCheckedNodes(checkedData.map(item => item.orgUnitId));
63
+ }
64
+
65
+ this.syncAllNodeCheckedState(checkedData);
48
66
  this.$emit('handleChange', checkedData);
49
67
  },
68
+ clearOtherCheckedNodes(keepIds) {
69
+ if (!Array.isArray(this.data) || !Array.isArray(keepIds)) return;
70
+
71
+ const clearNodeState = (nodeList) => {
72
+ nodeList.forEach(node => {
73
+ if (!keepIds.includes(node.orgUnitId) && node.checked) {
74
+ this.$set(node, 'checked', false);
75
+ }
76
+ // 递归处理子节点(单选时也需要清空子节点的选中状态)
77
+ const childNodes = node.children || node.orgChildrenList || [];
78
+ if (childNodes.length) {
79
+ clearNodeState(childNodes);
80
+ }
81
+ });
82
+ };
83
+
84
+ clearNodeState(this.data);
85
+ },
50
86
 
51
- // 2. 新增:同步子组件所有节点的checked状态(核心方法)
52
87
  syncAllNodeCheckedState(checkedData) {
53
- if (!Array.isArray(this.data)) return;
88
+ if (!Array.isArray(this.data) || !Array.isArray(checkedData)) return;
54
89
 
55
- // 提取所有选中节点的ID,方便快速匹配
56
90
  const checkedIds = checkedData.map(item => item.orgUnitId);
57
-
58
- // 递归遍历所有节点,更新checked状态
59
91
  const updateNodeState = (nodeList) => {
60
92
  nodeList.forEach(node => {
61
- // 对比选中ID列表,更新当前节点状态
93
+ // 仅更新当前节点,跳过子节点
62
94
  const shouldBeChecked = checkedIds.includes(node.orgUnitId);
63
95
  if (node.checked !== shouldBeChecked) {
64
- this.$set(node, 'checked', shouldBeChecked); // 用$set确保响应式
65
- }
66
-
67
- // 递归处理子节点
68
- const childNodes = node.children || node.orgChildrenList || [];
69
- if (childNodes.length) {
70
- updateNodeState(childNodes);
96
+ this.$set(node, 'checked', shouldBeChecked);
71
97
  }
98
+ // 彻底删除子节点递归逻辑
72
99
  });
73
100
  };
74
-
75
101
  updateNodeState(this.data);
76
102
  },
77
103
 
78
- async loadData(item, callback){
79
- // 模拟异步获取数据
80
- let children = await this.getOrgChildren(item.orgUnitId)
81
- callback(children); // 调用callback传入子节点数据
82
- },
83
- checkNode(data){
84
- // console.log(data,'data')
104
+ async loadData(item, callback) {
105
+ let children = await this.getOrgChildren(item.orgUnitId);
106
+ callback(children);
85
107
  },
86
- getOrgChildren(orgUnitId=''){
87
- return new Promise((resolve,reject)=>{
88
- ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitList', { params:{
89
- containsCurLevel:true,
90
- orgUnitId:orgUnitId,
108
+
109
+ getOrgChildren(orgUnitId = '') {
110
+ return new Promise((resolve, reject) => {
111
+ ajax.get('/pub-manage-server/pub/personHelpBox/q/getOrgUnitList', {
112
+ params: {
113
+ containsCurLevel: true,
114
+ orgUnitId: orgUnitId
91
115
  }
92
- }).then((res)=>{
93
- if(res.data.code === 1){
94
- let treeList = res.data.data
95
- this.initTree(treeList)
96
- resolve(treeList)
97
- }else{
98
- resolve([])
116
+ }).then((res) => {
117
+ if (res.data.code === 1) {
118
+ let treeList = res.data.data;
119
+ this.initTree(treeList);
120
+ resolve(treeList);
121
+ } else {
122
+ resolve([]);
99
123
  }
100
- }).catch(err=>{
101
- console.log(err)
102
- })
103
- })
124
+ }).catch(err => {
125
+ console.log(err);
126
+ resolve([]);
127
+ });
128
+ });
104
129
  },
105
- /**
106
- * 手动设置树节点选中状态(供父组件调用)
107
- * @param {Array} nodes - 需要选中的节点数组(单个节点时传数组包裹)
108
- */
130
+
109
131
  setCheckedNodes(targets) {
110
132
  if (!this.$refs.tree || !Array.isArray(targets)) {
111
133
  this.clearAllChecked(this.data);
112
- this.$emit('handleChange', []); // 取消时通知父组件清空
134
+ this.$emit('handleChange', []);
113
135
  this.$nextTick(() => this.$refs.tree.$forceUpdate());
114
136
  return;
115
137
  }
116
138
 
139
+ // 单选逻辑强化:清空所有选中状态
117
140
  this.clearAllChecked(this.data);
118
- const targetIds = targets.map(item =>
119
- typeof item === 'string' ? item : (item.orgUnitId || '')
120
- ).filter(Boolean);
121
141
 
122
- // 递归选中目标节点
142
+ const targetList = this.isSingleSelect
143
+ ? (targets.length > 0 ? [targets[targets.length - 1]] : [])
144
+ : targets;
145
+
146
+ // 转换为有效orgUnitId数组
147
+ const targetIds = targetList.map(item => {
148
+ if (typeof item === 'string') return item.trim();
149
+ return (item.orgUnitId || item.id || '').trim();
150
+ }).filter(id => id); // 过滤空ID
151
+
152
+ // 查找并选中目标节点
123
153
  const findAndCheckNode = (treeData, targetId) => {
124
154
  for (const node of treeData) {
125
- if (node.orgUnitId === targetId) {
126
- this.$set(node, 'selected', true);
155
+ const nodeId = (node.orgUnitId || node.id || '').trim();
156
+ if (nodeId === targetId) {
157
+ this.$set(node, 'checked', true);
127
158
  this.expandToNode(node);
128
159
  return true;
129
160
  }
@@ -137,38 +168,32 @@ export default {
137
168
 
138
169
  targetIds.forEach(id => findAndCheckNode(this.data, id));
139
170
 
140
- // 关键:选中后主动收集选中节点,通知父组件
141
- const checkedNodes = this.collectAllCheckedNodes(this.data);
171
+ // 传递过滤后的有效节点
172
+ const checkedNodes = this.collectCurrentCheckedNodes(this.data);
142
173
  this.$nextTick(() => {
143
174
  this.$refs.tree.$forceUpdate();
144
- this.$emit('handleChange', checkedNodes); // 同步父组件暂存数据
175
+ this.$emit('handleChange', checkedNodes);
145
176
  });
146
177
  },
147
- /**
148
- * 【新增】递归展开到目标节点的所有父节点(确保选中节点可见)
149
- * @param {Object} node - 目标节点
150
- */
178
+
151
179
  expandToNode(node) {
152
180
  let current = node;
153
181
  while (current && current.parent) {
154
- // 修复:确保父节点是对象而非数组
155
182
  if (typeof current.parent === 'object' && !Array.isArray(current.parent)) {
156
183
  this.$set(current.parent, 'expand', true);
157
184
  }
158
- // 修复:正确获取上层父节点
159
185
  current = current.parent.orgUnitId
160
186
  ? this.findNodeInTree(this.data, current.parent.orgUnitId)
161
187
  : null;
162
188
  }
163
189
  },
164
- // 4. 新增:收集子组件内所有选中的节点(供同步给父组件)
190
+
165
191
  collectAllCheckedNodes(nodeList) {
166
192
  let checkedNodes = [];
167
193
  nodeList.forEach(node => {
168
194
  if (node.checked) {
169
195
  checkedNodes.push(node);
170
196
  }
171
- // 递归收集子节点
172
197
  const childNodes = node.children || node.orgChildrenList || [];
173
198
  if (childNodes.length) {
174
199
  checkedNodes = [...checkedNodes, ...this.collectAllCheckedNodes(childNodes)];
@@ -176,55 +201,40 @@ export default {
176
201
  });
177
202
  return checkedNodes;
178
203
  },
179
- /**
180
- * 递归清空所有节点的选中状态(辅助方法)
181
- * @param {Array} treeData - 树形数据
182
- */
204
+
183
205
  clearAllChecked(treeData) {
184
206
  if (!Array.isArray(treeData)) return;
185
207
  treeData.forEach(node => {
186
208
  if (node.checked) {
187
- this.$set(node, 'checked', false); // 响应式更新选中状态
209
+ this.$set(node, 'checked', false);
188
210
  }
189
- // 同时处理两种子节点字段
190
211
  const childNodes = node.children || node.orgChildrenList || [];
191
212
  if (childNodes.length) {
192
213
  this.clearAllChecked(childNodes);
193
214
  }
194
215
  });
195
216
  },
196
- /**
197
- * 【新增】供父组件调用,手动更新子组件数据(解决数据同步问题)
198
- * @param {Array} newTreeData - 父组件传递的新树形数据
199
- */
217
+
200
218
  updateTreeData(newTreeData) {
201
219
  if (!Array.isArray(newTreeData)) return;
202
220
  const tree = deepCopy(newTreeData);
203
- this.initTree(tree); // 确保新数据经过格式转换(添加children、checked等字段)
204
- this.data = tree; // 更新子组件内部数据
221
+ this.initTree(tree);
222
+ this.data = tree;
205
223
  this.$nextTick(() => {
206
- this.$refs.tree.$forceUpdate(); // 触发UI刷新
224
+ this.$refs.tree.$forceUpdate();
207
225
  });
208
226
  },
209
- /**
210
- * 递归查找树中的节点(辅助方法,与父组件逻辑一致,可复用)
211
- * @param {Array} treeData - 树形数据
212
- * @param {string} targetOrgUnitId - 目标节点ID
213
- * @returns {Object|null} 找到的节点
214
- */
227
+
215
228
  findNodeInTree(treeData, targetOrgUnitId) {
216
229
  if (!treeData || !targetOrgUnitId) return null;
217
230
 
218
231
  for (const node of treeData) {
219
- // 记录父节点引用(用于展开节点)
220
232
  if (!node.parent && treeData !== this.data) {
221
- node.parent = treeData; // 标记父节点,方便后续展开
233
+ node.parent = treeData;
222
234
  }
223
235
 
224
- // 找到目标节点,直接返回
225
236
  if (node.orgUnitId === targetOrgUnitId) return node;
226
237
 
227
- // 【关键】先遍历转换后的children,再遍历原始orgChildrenList(防止遗漏)
228
238
  const childNodes = node.children || node.orgChildrenList || [];
229
239
  if (childNodes.length) {
230
240
  const childNode = this.findNodeInTree(childNodes, targetOrgUnitId);
@@ -233,132 +243,183 @@ export default {
233
243
  }
234
244
  return null;
235
245
  },
236
- initTree(treeList){
237
- const defineTree = function(list){
238
- if(!list)return
246
+
247
+ initTree(treeList) {
248
+ const defineTree = function (list) {
249
+ if (!list) return;
239
250
  list.forEach((item) => {
240
- item.title=item.orgNodeName;
251
+ item.title = item.orgNodeName;
241
252
  item.loading = false;
242
253
  item.children = [];
243
254
  item.expand = false;
244
- item.checked = false;
245
- if(item.leafNode){
255
+ item.checked = false; // 确保checked属性初始化
256
+ if (item.leafNode) {
246
257
  delete item.loading;
247
258
  delete item.children;
248
259
  }
249
- if(item.orgChildrenList && item.orgChildrenList.length){
260
+ if (item.orgChildrenList && item.orgChildrenList.length) {
250
261
  item.children = defineTree(item.orgChildrenList);
251
262
  item.expand = true;
252
263
  }
253
- })
254
- return list
255
- }
256
- treeList.forEach(item=>{
257
- item.title=item.orgNodeName;
264
+ });
265
+ return list;
266
+ };
267
+ treeList.forEach(item => {
268
+ item.title = item.orgNodeName;
258
269
  item.loading = false;
259
270
  item.children = [];
260
271
  item.expand = false;
261
- item.checked = false;
262
- if(item.orgChildrenList && item.orgChildrenList.length){
272
+ item.checked = false; // 初始化Checkbox绑定的checked属性
273
+ if (item.orgChildrenList && item.orgChildrenList.length) {
263
274
  item.children = defineTree(item.orgChildrenList);
264
275
  item.expand = true;
265
276
  }
266
- if(this.disabled){
277
+ if (this.disabled) {
267
278
  item.disabled = true;
268
279
  }
269
- if(item.leafNode){
280
+ if (item.leafNode) {
270
281
  delete item.loading;
271
282
  delete item.children;
272
283
  }
273
- })
284
+ });
274
285
  },
275
- // 子组件 methods 中的 renderContent 方法(修复点击节点选中逻辑)
276
- renderContent(h, { root, node, data }) {
286
+
287
+ //renderContent添加Checkbox组件
288
+ renderContent(h, {root, node, data}) {
277
289
  return h('div', {
278
290
  style: {
279
291
  width: '100%',
280
292
  overflow: 'hidden',
281
293
  display: 'flex',
282
294
  alignItems: 'center',
295
+ cursor: 'pointer'
283
296
  }
284
297
  }, [
285
- h('img', {
286
- attrs: {
287
- src: require('./assets/icon.png'),
298
+ h('Checkbox', {
299
+ props: {
300
+ value: data.checked,
301
+ disabled: data.disabled || this.disabled,
302
+ indeterminate: false
288
303
  },
289
304
  style: {
290
- marginRight: '8px',
291
- width: '14px',
292
- height: '14px',
293
- }
294
- }),
295
- h('span', {
296
- style: {
297
- overflow: 'hidden',
298
- textOverflow: 'ellipsis',
299
- flex: 1,
300
- // 新增:添加选中状态样式(与直接选择保持一致)
301
- color: data.checked ? 'var(--primary-color)' : 'inherit',
302
- fontWeight: data.checked ? 'bold' : 'normal'
305
+ marginRight: '8px'
303
306
  },
304
307
  on: {
305
- click: () => {
306
- // 修复:点击节点时同步更新checked状态
307
- this.$set(data, 'checked', !data.checked);
308
- this.checkNode(data);
309
- // 触发Tree组件内置选中事件
310
- this.handleChange([data]);
308
+ 'on-change': (checked) => {
309
+ this.$set(data, 'checked', checked);
310
+
311
+ // 单选逻辑:勾选当前节点时,清空其他所有节点
312
+ if (this.isSingleSelect && checked) {
313
+ this.clearOtherCheckedNodes([data.orgUnitId]);
314
+ }
315
+
316
+ // 收集选中节点(单选时仅返回当前节点)
317
+ const checkedNodes = this.isSingleSelect
318
+ ? (checked ? [data] : [])
319
+ : this.collectCurrentCheckedNodes(this.data);
320
+
321
+ this.handleChange(checkedNodes);
311
322
  }
312
323
  }
313
- }, data.title),
314
- // 新增:显示选中勾选图标(与直接选择保持视觉一致)
315
- h('Icon', {
316
- attrs: {
317
- type: 'md-checkmark',
318
- },
319
- style: {
320
- color: 'var(--primary-color)',
321
- display: data.checked ? 'inline-block' : 'none',
322
- }
323
- })
324
+ }),
325
+ h('img', {
326
+ attrs: { src: require('./assets/icon.png') },
327
+ style: { marginRight: '8px', width: '14px', height: '14px' }
328
+ }),
329
+ h('span', {
330
+ style: { overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }
331
+ }, data.title)
324
332
  ]);
325
333
  },
326
- upDataTree(){
327
-
328
- const unCheck = (list)=>{
329
- list.forEach((item)=>{
330
- this.$set(item, 'selected', false);
331
- if(item.children && item.children.length){
332
- unCheck(item.children)
334
+ collectCurrentCheckedNodes(nodeList) {
335
+ let checkedNodes = [];
336
+ nodeList.forEach(node => {
337
+ if (node.checked) {
338
+ // 强制补全orgUnitId,过滤无效节点
339
+ const nodeId = node.orgUnitId || node.id || '';
340
+ if (nodeId && typeof nodeId === 'string' && nodeId.trim()) {
341
+ const validNode = {
342
+ ...node,
343
+ orgUnitId: nodeId.trim(),
344
+ orgNodeName: node.orgNodeName || node.title || node.name || `未命名组织(${nodeId.trim()})`
345
+ };
346
+ checkedNodes.push(validNode);
333
347
  }
334
- })
335
- }
336
- unCheck(this.data)
348
+ }
349
+ // 递归处理子节点
350
+ const childNodes = node.children || node.orgChildrenList || [];
351
+ if (childNodes.length) {
352
+ checkedNodes = [...checkedNodes, ...this.collectCurrentCheckedNodes(childNodes)];
353
+ }
354
+ });
355
+ return checkedNodes;
356
+ },
357
+ upDataTree() {
358
+ const unCheck = (list) => {
359
+ list.forEach((item) => {
360
+ this.$set(item, 'checked', false);
361
+ });
362
+ };
363
+ unCheck(this.data);
337
364
  }
338
365
  },
339
- watch:{
340
- 'treeList':{
341
- handler:function(val,oldVal){
342
- let tree = deepCopy(val)
343
- this.initTree(tree)
344
- this.data = tree
366
+ watch: {
367
+ 'treeList': {
368
+ handler(val) {
369
+ let tree = deepCopy(val);
370
+ this.initTree(tree);
371
+ this.data = tree;
345
372
  },
346
- deep:true
373
+ deep: true
347
374
  }
348
375
  }
349
- }
376
+ };
350
377
  </script>
378
+
351
379
  <style lang="less" scoped>
352
- .content{
380
+ .content {
353
381
  width: 100%;
354
382
  height: 100%;
355
383
  overflow: auto;
356
384
  }
357
- /deep/.ivu-tree ul li{
358
- overflow: hidden;
385
+
386
+ /deep/ .ivu-tree-node,
387
+ /deep/ .ivu-tree-node *,
388
+ /deep/ .ivu-tree-node-content,
389
+ /deep/ .ivu-tree-title {
390
+ box-shadow: none !important;
391
+ text-shadow: none !important;
392
+ outline: none !important;
393
+ border: none !important;
394
+ background: transparent !important;
395
+ }
396
+
397
+ /deep/ .ivu-tree-node-selected,
398
+ /deep/ .ivu-tree-node-selected *,
399
+ /deep/ .ivu-tree-node-focus,
400
+ /deep/ .ivu-tree-node-focus *,
401
+ /deep/ .ivu-tree-node-hover,
402
+ /deep/ .ivu-tree-node-hover * {
403
+ box-shadow: none !important;
404
+ text-shadow: none !important;
405
+ background: transparent !important;
406
+ color: inherit !important;
359
407
  }
360
- /deep/.ivu-tree-title{
361
- width: calc(100% - 20px);
408
+
409
+ /deep/ .ivu-tree-node-content {
410
+ padding-left: 0 !important;
362
411
  }
363
412
 
364
- </style>
413
+ /deep/ .ivu-tree-indent {
414
+ margin-right: 4px;
415
+ }
416
+
417
+ /deep/ .ivu-tree-node::before,
418
+ /deep/ .ivu-tree-node::after,
419
+ /deep/ .ivu-tree-title::before,
420
+ /deep/ .ivu-tree-title::after {
421
+ box-shadow: none !important;
422
+ text-shadow: none !important;
423
+ background: none !important;
424
+ }
425
+ </style>