|
|
@@ -239,9 +239,21 @@
|
|
239
|
239
|
</view>
|
|
240
|
240
|
<view class="search-box">
|
|
241
|
241
|
<u-input v-model="employeeSearchKeyword" placeholder="搜索员工" @confirm="onEmployeeSearch"
|
|
242
|
|
- @input="onEmployeeSearch" />
|
|
|
242
|
+ @input="onEmployeeSearch"></u-input>
|
|
243
|
243
|
</view>
|
|
244
|
|
- <scroll-view scroll-y class="employee-list">
|
|
|
244
|
+ <scroll-view v-if="!employeeSearchKeyword.trim()" scroll-y class="tree-list">
|
|
|
245
|
+ <template v-for="(node, index) in deptTreeData">
|
|
|
246
|
+ <employee-tree-node
|
|
|
247
|
+ :key="node.id"
|
|
|
248
|
+ :node="node"
|
|
|
249
|
+ :expanded-ids="expandedDeptIds"
|
|
|
250
|
+ :selected-id="selectedEmployeeId"
|
|
|
251
|
+ @toggle="toggleDeptExpand"
|
|
|
252
|
+ @select="onEmployeeSelect"
|
|
|
253
|
+ />
|
|
|
254
|
+ </template>
|
|
|
255
|
+ </scroll-view>
|
|
|
256
|
+ <scroll-view v-else scroll-y class="employee-list">
|
|
245
|
257
|
<view class="employee-item" v-for="item in filteredEmployeeList" :key="item.userId"
|
|
246
|
258
|
@click="onEmployeeSelect(item)">
|
|
247
|
259
|
<text class="employee-item-name">{{ item.nickName }}</text>
|
|
|
@@ -265,6 +277,7 @@ import * as echarts from 'echarts'
|
|
265
|
277
|
import { getEmployeePortrait, countTagScore } from '@/api/portraitManagement/portraitManagement'
|
|
266
|
278
|
import { listAllUser, getDeptUserTree } from '@/api/system/user'
|
|
267
|
279
|
import SectionTitle from '@/components/SectionTitle.vue'
|
|
|
280
|
+import EmployeeTreeNode from '@/pages/components/EmployeeTreeNode.vue'
|
|
268
|
281
|
|
|
269
|
282
|
const honorColors = ['#60A5FA', '#34D399', '#FBBF24', '#EF4444', '#A78BFA', '#F472B6', '#6EE7B7', '#FB923C']
|
|
270
|
283
|
|
|
|
@@ -286,7 +299,8 @@ function getRandomHexColor() {
|
|
286
|
299
|
export default {
|
|
287
|
300
|
name: 'EmployeeProfile',
|
|
288
|
301
|
components: {
|
|
289
|
|
- SectionTitle
|
|
|
302
|
+ SectionTitle,
|
|
|
303
|
+ EmployeeTreeNode
|
|
290
|
304
|
},
|
|
291
|
305
|
data() {
|
|
292
|
306
|
return {
|
|
|
@@ -308,10 +322,12 @@ export default {
|
|
308
|
322
|
showEmployeePicker: false,
|
|
309
|
323
|
selectedEmployeeId: null,
|
|
310
|
324
|
selectedEmployeeName: '',
|
|
|
325
|
+ deptTreeData: [],
|
|
|
326
|
+ pickerTreeData: [],
|
|
311
|
327
|
employeeList: [],
|
|
312
|
|
- filteredEmployeeList: [],
|
|
313
|
328
|
employeeSearchKeyword: '',
|
|
314
|
329
|
employeeLoading: false,
|
|
|
330
|
+ expandedDeptIds: [],
|
|
315
|
331
|
isSecurityCheck: false,
|
|
316
|
332
|
userInfo: null
|
|
317
|
333
|
}
|
|
|
@@ -353,6 +369,13 @@ export default {
|
|
353
|
369
|
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
|
354
|
370
|
const weekDay = weekDays[now.getDay()]
|
|
355
|
371
|
return `${year}年${month}月${day}日 ${weekDay}`
|
|
|
372
|
+ },
|
|
|
373
|
+ filteredEmployeeList() {
|
|
|
374
|
+ const keyword = this.employeeSearchKeyword.trim().toLowerCase()
|
|
|
375
|
+ if (!keyword) return this.employeeList
|
|
|
376
|
+ return this.employeeList.filter(item =>
|
|
|
377
|
+ (item.nickName || '').toLowerCase().includes(keyword)
|
|
|
378
|
+ )
|
|
356
|
379
|
}
|
|
357
|
380
|
},
|
|
358
|
381
|
mounted() {
|
|
|
@@ -382,6 +405,58 @@ export default {
|
|
382
|
405
|
this.currentTime = `${hours}:${minutes}:${seconds}`
|
|
383
|
406
|
},
|
|
384
|
407
|
// 员工相关方法
|
|
|
408
|
+ // 扁平化部门树,提取人员
|
|
|
409
|
+ flattenDeptTree(tree) {
|
|
|
410
|
+ const result = []
|
|
|
411
|
+ const traverse = (nodes) => {
|
|
|
412
|
+ nodes.forEach(node => {
|
|
|
413
|
+ const hasChildren = node.children && node.children.length > 0
|
|
|
414
|
+ if (!hasChildren && node.nodeType !== 'dept') {
|
|
|
415
|
+ result.push({
|
|
|
416
|
+ nodeType: node.nodeType || '',
|
|
|
417
|
+ userId: node.userId || node.id,
|
|
|
418
|
+ nickName: node.nickName || node.label || node.userName || ''
|
|
|
419
|
+ })
|
|
|
420
|
+ }
|
|
|
421
|
+ if (hasChildren) {
|
|
|
422
|
+ traverse(node.children)
|
|
|
423
|
+ }
|
|
|
424
|
+ })
|
|
|
425
|
+ }
|
|
|
426
|
+ traverse(tree)
|
|
|
427
|
+ return result
|
|
|
428
|
+ },
|
|
|
429
|
+
|
|
|
430
|
+ // 从树中查找第一个用户节点
|
|
|
431
|
+ findFirstUser(nodes) {
|
|
|
432
|
+ for (const node of nodes) {
|
|
|
433
|
+ const hasChildren = node.children && node.children.length > 0
|
|
|
434
|
+ if (!hasChildren && node.nodeType !== 'dept') {
|
|
|
435
|
+ return node
|
|
|
436
|
+ }
|
|
|
437
|
+ if (hasChildren) {
|
|
|
438
|
+ const found = this.findFirstUser(node.children)
|
|
|
439
|
+ if (found) return found
|
|
|
440
|
+ }
|
|
|
441
|
+ }
|
|
|
442
|
+ return null
|
|
|
443
|
+ },
|
|
|
444
|
+ // 展开所有部门节点
|
|
|
445
|
+ expandAllDepts(nodes) {
|
|
|
446
|
+ this.expandedDeptIds = []
|
|
|
447
|
+ const traverse = (list) => {
|
|
|
448
|
+ list.forEach(node => {
|
|
|
449
|
+ const hasChildren = node.children && node.children.length > 0
|
|
|
450
|
+ if (hasChildren || node.nodeType === 'dept') {
|
|
|
451
|
+ this.expandedDeptIds.push(node.id)
|
|
|
452
|
+ if (hasChildren) {
|
|
|
453
|
+ traverse(node.children)
|
|
|
454
|
+ }
|
|
|
455
|
+ }
|
|
|
456
|
+ })
|
|
|
457
|
+ }
|
|
|
458
|
+ traverse(nodes)
|
|
|
459
|
+ },
|
|
385
|
460
|
fetchEmployeeList() {
|
|
386
|
461
|
this.employeeLoading = true
|
|
387
|
462
|
const user = this.$store.state.user?.userInfo || this.$store.state.user?.user
|
|
|
@@ -396,11 +471,8 @@ export default {
|
|
396
|
471
|
this.selectedEmployeeId = user.userId || user.id
|
|
397
|
472
|
this.selectedEmployeeName = user.nickName || user.userName || ''
|
|
398
|
473
|
this.searchKeyword = this.selectedEmployeeName
|
|
399
|
|
- this.employeeList = [{
|
|
400
|
|
- userId: user.userId || user.id,
|
|
401
|
|
- nickName: user.nickName || user.userName || ''
|
|
402
|
|
- }]
|
|
403
|
|
- this.filteredEmployeeList = [...this.employeeList]
|
|
|
474
|
+ this.deptTreeData = []
|
|
|
475
|
+ this.pickerTreeData = []
|
|
404
|
476
|
this.employeeLoading = false
|
|
405
|
477
|
// 自动加载画像数据
|
|
406
|
478
|
this.fetchEmployeePortrait()
|
|
|
@@ -409,14 +481,16 @@ export default {
|
|
409
|
481
|
if (user && user.deptId) {
|
|
410
|
482
|
getDeptUserTree({ deptId: user.deptId }).then(res => {
|
|
411
|
483
|
if (res.code === 200) {
|
|
412
|
|
- let allUsers = this.flattenDeptTree(res.data || [])
|
|
413
|
|
- this.employeeList = allUsers.filter(user => user.nodeType == 'user')
|
|
414
|
|
-
|
|
415
|
|
- this.filteredEmployeeList = [...this.employeeList]
|
|
416
|
|
- // 默认选中第一个
|
|
417
|
|
- if (this.employeeList.length > 0) {
|
|
418
|
|
- this.selectedEmployeeId = this.employeeList[0].userId
|
|
419
|
|
- this.selectedEmployeeName = this.employeeList[0].nickName
|
|
|
484
|
+ this.deptTreeData = res.data || []
|
|
|
485
|
+ const allUsers = this.flattenDeptTree(this.deptTreeData)
|
|
|
486
|
+ this.employeeList = allUsers.filter(user => user.nodeType === 'user')
|
|
|
487
|
+ // 默认展开所有部门
|
|
|
488
|
+ this.expandAllDepts(this.deptTreeData)
|
|
|
489
|
+ // 默认选中第一个用户
|
|
|
490
|
+ const firstUser = this.findFirstUser(this.deptTreeData)
|
|
|
491
|
+ if (firstUser) {
|
|
|
492
|
+ this.selectedEmployeeId = firstUser.userId || firstUser.id
|
|
|
493
|
+ this.selectedEmployeeName = firstUser.nickName || firstUser.label || firstUser.userName || ''
|
|
420
|
494
|
this.searchKeyword = this.selectedEmployeeName
|
|
421
|
495
|
this.fetchEmployeePortrait()
|
|
422
|
496
|
}
|
|
|
@@ -430,36 +504,15 @@ export default {
|
|
430
|
504
|
}
|
|
431
|
505
|
}
|
|
432
|
506
|
},
|
|
433
|
|
- // 扁平化部门树,提取人员
|
|
434
|
|
- flattenDeptTree(tree) {
|
|
435
|
|
- const result = []
|
|
436
|
|
- const traverse = (nodes) => {
|
|
437
|
|
- nodes.forEach(node => {
|
|
438
|
|
- // 判断是否是人员节点,这里假设人员有 userId 或者特定字段
|
|
439
|
|
- // 根据实际接口返回调整判断条件
|
|
440
|
|
- if (node.userId || (node.id && !node.children)) {
|
|
441
|
|
- result.push({
|
|
442
|
|
- nodeType: node.nodeType || '',
|
|
443
|
|
- userId: node.userId || node.id,
|
|
444
|
|
- nickName: node.nickName || node.label || node.userName || ''
|
|
445
|
|
- })
|
|
446
|
|
- }
|
|
447
|
|
- if (node.children && node.children.length > 0) {
|
|
448
|
|
- traverse(node.children)
|
|
449
|
|
- }
|
|
450
|
|
- })
|
|
451
|
|
- }
|
|
452
|
|
- traverse(tree)
|
|
453
|
|
- return result
|
|
454
|
|
- },
|
|
455
|
507
|
onEmployeeSearch() {
|
|
456
|
|
- const keyword = this.employeeSearchKeyword.trim()
|
|
457
|
|
- if (!keyword) {
|
|
458
|
|
- this.filteredEmployeeList = [...this.employeeList]
|
|
|
508
|
+ // 搜索由 computed 自动处理
|
|
|
509
|
+ },
|
|
|
510
|
+ toggleDeptExpand(id) {
|
|
|
511
|
+ const index = this.expandedDeptIds.indexOf(id)
|
|
|
512
|
+ if (index > -1) {
|
|
|
513
|
+ this.expandedDeptIds.splice(index, 1)
|
|
459
|
514
|
} else {
|
|
460
|
|
- this.filteredEmployeeList = this.employeeList.filter(item =>
|
|
461
|
|
- (item.nickName || '').includes(keyword)
|
|
462
|
|
- )
|
|
|
515
|
+ this.expandedDeptIds.push(id)
|
|
463
|
516
|
}
|
|
464
|
517
|
},
|
|
465
|
518
|
onEmployeeSelect(item) {
|
|
|
@@ -853,6 +906,13 @@ export default {
|
|
853
|
906
|
flex-shrink: 0;
|
|
854
|
907
|
}
|
|
855
|
908
|
|
|
|
909
|
+.tree-list {
|
|
|
910
|
+ flex: 1;
|
|
|
911
|
+ padding: 0;
|
|
|
912
|
+ height: 0;
|
|
|
913
|
+ overflow: hidden;
|
|
|
914
|
+}
|
|
|
915
|
+
|
|
856
|
916
|
.employee-list {
|
|
857
|
917
|
flex: 1;
|
|
858
|
918
|
padding: 0 32rpx;
|