|
|
@@ -18,15 +18,15 @@
|
|
18
|
18
|
</view>
|
|
19
|
19
|
</scroll-view>
|
|
20
|
20
|
<view v-if="selectedTimeTag === 4" class="date-range-picker">
|
|
21
|
|
- <picker mode="date" :value="startDate" @change="onStartDateChange">
|
|
22
|
|
- <view class="date-input" :class="{ filled: startDate }">
|
|
23
|
|
- {{ startDate || '开始日期' }}
|
|
|
21
|
+ <picker mode="date" :value="beginTime" @change="onBeginTimeChange">
|
|
|
22
|
+ <view class="date-input" :class="{ filled: beginTime }">
|
|
|
23
|
+ {{ beginTime || '开始日期' }}
|
|
24
|
24
|
</view>
|
|
25
|
25
|
</picker>
|
|
26
|
26
|
<text class="date-separator">至</text>
|
|
27
|
|
- <picker mode="date" :value="endDate" @change="onEndDateChange">
|
|
28
|
|
- <view class="date-input" :class="{ filled: endDate }">
|
|
29
|
|
- {{ endDate || '结束日期' }}
|
|
|
27
|
+ <picker mode="date" :value="endTime" @change="onEndTimeChange">
|
|
|
28
|
+ <view class="date-input" :class="{ filled: endTime }">
|
|
|
29
|
+ {{ endTime || '结束日期' }}
|
|
30
|
30
|
</view>
|
|
31
|
31
|
</picker>
|
|
32
|
32
|
</view>
|
|
|
@@ -98,7 +98,8 @@
|
|
98
|
98
|
<view class="info-item">
|
|
99
|
99
|
<view class="info-label">标签:</view>
|
|
100
|
100
|
<view class="info-value" v-if="portrait.userTags">
|
|
101
|
|
- <text class="tag" v-for="tag in portrait.userTags.split(',')" :key="tag">{{ tag }}</text>
|
|
|
101
|
+ <text class="tag" v-for="tag in portrait.userTags.split(',')" :key="tag">{{ tag
|
|
|
102
|
+ }}</text>
|
|
102
|
103
|
</view>
|
|
103
|
104
|
</view>
|
|
104
|
105
|
</view>
|
|
|
@@ -106,13 +107,13 @@
|
|
106
|
107
|
<view class="score-section">
|
|
107
|
108
|
<div class="score-circle" ref="scoreCircle"></div>
|
|
108
|
109
|
<view class="score-box">
|
|
109
|
|
- <view class="score-row">
|
|
|
110
|
+ <!-- <view class="score-row">
|
|
110
|
111
|
<text class="score-label">评分:</text>
|
|
111
|
112
|
<text class="score-val">{{ portrait.totalScore || 0 }}</text>
|
|
112
|
|
- </view>
|
|
|
113
|
+ </view> -->
|
|
113
|
114
|
<view class="score-row">
|
|
114
|
|
- <text class="score-label">标签得分:</text>
|
|
115
|
|
- <text class="score-val">{{ tagScoreDisplay }}</text>
|
|
|
115
|
+ <text class="score-label">附加分:</text>
|
|
|
116
|
+ <text class="score-val">{{ tagScoreDisplay }}分</text>
|
|
116
|
117
|
</view>
|
|
117
|
118
|
</view>
|
|
118
|
119
|
</view>
|
|
|
@@ -146,7 +147,13 @@
|
|
146
|
147
|
</SectionTitle>
|
|
147
|
148
|
|
|
148
|
149
|
<SectionTitle v-if="activeTab === 'profile'" title="个人能力">
|
|
149
|
|
- <div class="chart-container" ref="radarChart"></div>
|
|
|
150
|
+ <div class="chart-legend">
|
|
|
151
|
+ <div class="legend-item legend-warning"><span></span>预警线(低于75分)</div>
|
|
|
152
|
+ <!-- <div class="legend-item legend-normal"><span></span>正常线(75~90分)</div> -->
|
|
|
153
|
+ <div class="legend-item legend-excellent"><span></span>优秀线(高于90分)</div>
|
|
|
154
|
+ <div class="legend-item legend-current"><span></span>当前员工分值</div>
|
|
|
155
|
+ </div>
|
|
|
156
|
+ <div class="chart-container" ref="radarChart" ></div>
|
|
150
|
157
|
</SectionTitle>
|
|
151
|
158
|
|
|
152
|
159
|
<SectionTitle v-if="activeTab === 'profile'" title="补充信息">
|
|
|
@@ -211,7 +218,7 @@
|
|
211
|
218
|
<view class="warning-score-item">
|
|
212
|
219
|
<text class="warning-score-label">综合得分</text>
|
|
213
|
220
|
<text class="warning-score-value" :class="scoreLevelClass">{{ portrait.totalScore || 0
|
|
214
|
|
- }}</text>
|
|
|
221
|
+ }}</text>
|
|
215
|
222
|
</view>
|
|
216
|
223
|
</view>
|
|
217
|
224
|
<view class="warning-detail" v-if="scoreDetails.length">
|
|
|
@@ -243,14 +250,8 @@
|
|
243
|
250
|
</view>
|
|
244
|
251
|
<scroll-view v-if="!employeeSearchKeyword.trim()" scroll-y class="tree-list">
|
|
245
|
252
|
<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
|
|
- />
|
|
|
253
|
+ <employee-tree-node :key="node.id" :node="node" :expanded-ids="expandedDeptIds"
|
|
|
254
|
+ :selected-id="selectedEmployeeId" @toggle="toggleDeptExpand" @select="onEmployeeSelect" />
|
|
254
|
255
|
</template>
|
|
255
|
256
|
</scroll-view>
|
|
256
|
257
|
<scroll-view v-else scroll-y class="employee-list">
|
|
|
@@ -269,6 +270,54 @@
|
|
269
|
270
|
</scroll-view>
|
|
270
|
271
|
</view>
|
|
271
|
272
|
</u-popup>
|
|
|
273
|
+
|
|
|
274
|
+ <!-- 雷达图tooltip -->
|
|
|
275
|
+ <u-popup :show="showRadarTooltipPopup" mode="center" :round="12"
|
|
|
276
|
+ :mask-close-able="true" @close="closeRadarPopup">
|
|
|
277
|
+
|
|
|
278
|
+ <div class="radar-tooltip-popup">
|
|
|
279
|
+ <div class="tooltip-card">
|
|
|
280
|
+ <!-- 标题 -->
|
|
|
281
|
+ <view class="tooltip-header">
|
|
|
282
|
+ <text class="tooltip-title">{{ activeDimName }}</text>
|
|
|
283
|
+ <u-icon name="close" color="#666666" size="20" @click="closeRadarPopup" />
|
|
|
284
|
+ </view>
|
|
|
285
|
+
|
|
|
286
|
+ <!-- 加分区域 -->
|
|
|
287
|
+ <div class="tooltip-section">
|
|
|
288
|
+ <div class="section-list">
|
|
|
289
|
+ <template v-if="addGroupList.length">
|
|
|
290
|
+ <div class="list-item" v-for="(item, i) in addGroupList" :key="i">
|
|
|
291
|
+ <span>{{ item.name }}:</span>
|
|
|
292
|
+ <span class="add-text">+{{ item.total }}分</span>
|
|
|
293
|
+ </div>
|
|
|
294
|
+ </template>
|
|
|
295
|
+ <div v-else class="p-empty">暂无加分记录</div>
|
|
|
296
|
+ </div>
|
|
|
297
|
+ <div class="section-total add">
|
|
|
298
|
+ <span>合计加分:</span>
|
|
|
299
|
+ <span>{{ addTotal }}分</span>
|
|
|
300
|
+ </div>
|
|
|
301
|
+ </div>
|
|
|
302
|
+ <!-- 扣分区域 -->
|
|
|
303
|
+ <div class="tooltip-section">
|
|
|
304
|
+ <div class="section-list">
|
|
|
305
|
+ <template v-if="deductGroupList.length">
|
|
|
306
|
+ <div class="list-item" v-for="(item, i) in deductGroupList" :key="i">
|
|
|
307
|
+ <span>{{ item.name }}:</span>
|
|
|
308
|
+ <span class="deduct-text">{{ item.total }}分</span>
|
|
|
309
|
+ </div>
|
|
|
310
|
+ </template>
|
|
|
311
|
+ <div v-else class="p-empty">暂无扣分记录</div>
|
|
|
312
|
+ </div>
|
|
|
313
|
+ <div class="section-total deduct">
|
|
|
314
|
+ <span>合计扣分:</span>
|
|
|
315
|
+ <span>{{ deductTotal }}分</span>
|
|
|
316
|
+ </div>
|
|
|
317
|
+ </div>
|
|
|
318
|
+ </div>
|
|
|
319
|
+ </div>
|
|
|
320
|
+ </u-popup>
|
|
272
|
321
|
</view>
|
|
273
|
322
|
</template>
|
|
274
|
323
|
|
|
|
@@ -309,8 +358,8 @@ export default {
|
|
309
|
358
|
timeTags: ['近一周', '近一月', '近三月', '近一年', '自定义时间范围'],
|
|
310
|
359
|
currentTime: '',
|
|
311
|
360
|
timer: null,
|
|
312
|
|
- startDate: '',
|
|
313
|
|
- endDate: '',
|
|
|
361
|
+ beginTime: '',
|
|
|
362
|
+ endTime: '',
|
|
314
|
363
|
searchKeyword: '',
|
|
315
|
364
|
portrait: { dimensions: [], awards: [] },
|
|
316
|
365
|
tagScoreData: null,
|
|
|
@@ -329,7 +378,14 @@ export default {
|
|
329
|
378
|
employeeLoading: false,
|
|
330
|
379
|
expandedDeptIds: [],
|
|
331
|
380
|
isSecurityCheck: false,
|
|
332
|
|
- userInfo: null
|
|
|
381
|
+ userInfo: null,
|
|
|
382
|
+ // 雷达图tooltip相关
|
|
|
383
|
+ activeDimName: null,
|
|
|
384
|
+ radarTooltipPosition: { x: 0, y: 0 },
|
|
|
385
|
+ // 控制雷达图 tooltip popup 显示
|
|
|
386
|
+ showRadarTooltipPopup: false,
|
|
|
387
|
+ // 评分明细数据
|
|
|
388
|
+ allScoreDetails: []
|
|
333
|
389
|
}
|
|
334
|
390
|
},
|
|
335
|
391
|
computed: {
|
|
|
@@ -350,11 +406,7 @@ export default {
|
|
350
|
406
|
return age + '岁'
|
|
351
|
407
|
},
|
|
352
|
408
|
tagScoreDisplay() {
|
|
353
|
|
- if (this.tagScoreData == null) return '0'
|
|
354
|
|
- if (typeof this.tagScoreData === 'object') {
|
|
355
|
|
- return this.tagScoreData.totalScore ?? this.tagScoreData.score ?? this.tagScoreData ?? '0'
|
|
356
|
|
- }
|
|
357
|
|
- return this.tagScoreData
|
|
|
409
|
+ return this.portrait?.userTags?.split(',').length || 0
|
|
358
|
410
|
},
|
|
359
|
411
|
scoreLevelClass() {
|
|
360
|
412
|
if ((this.portrait.totalScore || 0) < 75) return 'score-danger'
|
|
|
@@ -376,6 +428,39 @@ export default {
|
|
376
|
428
|
return this.employeeList.filter(item =>
|
|
377
|
429
|
(item.nickName || '').toLowerCase().includes(keyword)
|
|
378
|
430
|
)
|
|
|
431
|
+ },
|
|
|
432
|
+ // 雷达图tooltip相关计算
|
|
|
433
|
+ addList() {
|
|
|
434
|
+ const all = (this.allScoreDetails || []).filter(d => d.totalScore != null && Number(d.totalScore) > 0)
|
|
|
435
|
+ return this.activeDimName ? all.filter(d => d.dimensionName === this.activeDimName) : all
|
|
|
436
|
+ },
|
|
|
437
|
+ deductList() {
|
|
|
438
|
+ const all = (this.allScoreDetails || []).filter(d => d.totalScore != null && Number(d.totalScore) < 0)
|
|
|
439
|
+ return this.activeDimName ? all.filter(d => d.dimensionName === this.activeDimName) : all
|
|
|
440
|
+ },
|
|
|
441
|
+ addGroupList() {
|
|
|
442
|
+ const groups = {}
|
|
|
443
|
+ this.addList.forEach(d => {
|
|
|
444
|
+ const key = d.level2Name || '其他'
|
|
|
445
|
+ groups[key] = (groups[key] || 0) + Number(d.totalScore)
|
|
|
446
|
+ })
|
|
|
447
|
+ return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
|
|
|
448
|
+ },
|
|
|
449
|
+ deductGroupList() {
|
|
|
450
|
+ const groups = {}
|
|
|
451
|
+ this.deductList.forEach(d => {
|
|
|
452
|
+ const key = d.level2Name || '其他'
|
|
|
453
|
+ groups[key] = (groups[key] || 0) + Number(d.totalScore)
|
|
|
454
|
+ })
|
|
|
455
|
+ return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
|
|
|
456
|
+ },
|
|
|
457
|
+ addTotal() {
|
|
|
458
|
+ const s = this.addList.reduce((acc, d) => acc + Number(d.totalScore), 0)
|
|
|
459
|
+ return (s > 0 ? '+' : '') + s.toFixed(2)
|
|
|
460
|
+ },
|
|
|
461
|
+ deductTotal() {
|
|
|
462
|
+ const s = this.deductList.reduce((acc, d) => acc + Number(d.totalScore), 0)
|
|
|
463
|
+ return s.toFixed(2)
|
|
379
|
464
|
}
|
|
380
|
465
|
},
|
|
381
|
466
|
mounted() {
|
|
|
@@ -383,6 +468,8 @@ export default {
|
|
383
|
468
|
this.timer = setInterval(() => {
|
|
384
|
469
|
this.updateTime()
|
|
385
|
470
|
}, 1000)
|
|
|
471
|
+ // 默认计算时间范围(近一年)
|
|
|
472
|
+ this.onTimeTagClick(this.selectedTimeTag)
|
|
386
|
473
|
this.fetchEmployeeList()
|
|
387
|
474
|
},
|
|
388
|
475
|
beforeDestroy() {
|
|
|
@@ -562,16 +649,55 @@ export default {
|
|
562
|
649
|
onTimeTagClick(index) {
|
|
563
|
650
|
this.selectedTimeTag = index
|
|
564
|
651
|
if (index === 4) {
|
|
565
|
|
- this.startDate = ''
|
|
566
|
|
- this.endDate = ''
|
|
|
652
|
+ this.beginTime = ''
|
|
|
653
|
+ this.endTime = ''
|
|
567
|
654
|
return
|
|
568
|
655
|
}
|
|
|
656
|
+ // 自动计算时间范围
|
|
|
657
|
+ const now = new Date()
|
|
|
658
|
+ // 设置结束时间为今天
|
|
|
659
|
+ const endTime = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
|
|
660
|
+ // 设置开始时间
|
|
|
661
|
+ let beginTime
|
|
|
662
|
+ switch (index) {
|
|
|
663
|
+ case 0: // 近一周
|
|
|
664
|
+ beginTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
|
665
|
+ break
|
|
|
666
|
+ case 1: // 近一月
|
|
|
667
|
+ beginTime = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())
|
|
|
668
|
+ break
|
|
|
669
|
+ case 2: // 近三月
|
|
|
670
|
+ beginTime = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate())
|
|
|
671
|
+ break
|
|
|
672
|
+ case 3: // 近一年
|
|
|
673
|
+ beginTime = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
|
|
|
674
|
+ break
|
|
|
675
|
+ default:
|
|
|
676
|
+ beginTime = now
|
|
|
677
|
+ }
|
|
|
678
|
+ // 格式化日期为 YYYY-MM-DD
|
|
|
679
|
+ this.beginTime = this.formatDate(beginTime)
|
|
|
680
|
+ this.endTime = this.formatDate(endTime)
|
|
|
681
|
+ // 选择时间标签后立即查询
|
|
|
682
|
+ if (this.selectedEmployeeId) {
|
|
|
683
|
+ this.fetchEmployeePortrait()
|
|
|
684
|
+ }
|
|
569
|
685
|
},
|
|
570
|
|
- onStartDateChange(e) {
|
|
571
|
|
- this.startDate = e.detail.value
|
|
|
686
|
+ onBeginTimeChange(e) {
|
|
|
687
|
+ const dateStr = e.detail.value
|
|
|
688
|
+ this.beginTime = dateStr
|
|
|
689
|
+ // 自定义时间选择后立即查询
|
|
|
690
|
+ if (this.selectedEmployeeId && this.endTime) {
|
|
|
691
|
+ this.fetchEmployeePortrait()
|
|
|
692
|
+ }
|
|
572
|
693
|
},
|
|
573
|
|
- onEndDateChange(e) {
|
|
574
|
|
- this.endDate = e.detail.value
|
|
|
694
|
+ onEndTimeChange(e) {
|
|
|
695
|
+ const dateStr = e.detail.value
|
|
|
696
|
+ this.endTime = dateStr
|
|
|
697
|
+ // 自定义时间选择后立即查询
|
|
|
698
|
+ if (this.selectedEmployeeId && this.beginTime) {
|
|
|
699
|
+ this.fetchEmployeePortrait()
|
|
|
700
|
+ }
|
|
575
|
701
|
},
|
|
576
|
702
|
formatWorkDate(d) {
|
|
577
|
703
|
if (!d) return '-'
|
|
|
@@ -589,6 +715,13 @@ export default {
|
|
589
|
715
|
if (this.searchKeyword.trim()) {
|
|
590
|
716
|
params.personName = this.searchKeyword.trim()
|
|
591
|
717
|
}
|
|
|
718
|
+ // 添加时间参数
|
|
|
719
|
+ if (this.beginTime) {
|
|
|
720
|
+ params.beginTime = this.beginTime
|
|
|
721
|
+ }
|
|
|
722
|
+ if (this.endTime) {
|
|
|
723
|
+ params.endTime = this.endTime
|
|
|
724
|
+ }
|
|
592
|
725
|
|
|
593
|
726
|
const portraitPromise = getEmployeePortrait(params).then(res => {
|
|
594
|
727
|
if (res.code === 200 && res.data) {
|
|
|
@@ -596,12 +729,15 @@ export default {
|
|
596
|
729
|
this.portrait.awards.forEach(item => {
|
|
597
|
730
|
item.color = getRandomHexColor()
|
|
598
|
731
|
})
|
|
599
|
|
- this.scoreDetails = res.data.scoreDetails || []
|
|
|
732
|
+ // 只取数组中最后一个元素的 scoreDetails
|
|
|
733
|
+ const allScoreDetails = res.data.scoreDetails || []
|
|
|
734
|
+ this.allScoreDetails = allScoreDetails
|
|
|
735
|
+ this.scoreDetails = allScoreDetails.length > 0 ? allScoreDetails[allScoreDetails.length - 1] : []
|
|
600
|
736
|
}
|
|
601
|
737
|
}).catch(() => { })
|
|
602
|
738
|
|
|
603
|
739
|
const tagPromise = countTagScore(params).then(res => {
|
|
604
|
|
-
|
|
|
740
|
+
|
|
605
|
741
|
const data = res.data
|
|
606
|
742
|
if (Array.isArray(data)) {
|
|
607
|
743
|
const found = data.find(item => item.userId === this.selectedEmployeeId)
|
|
|
@@ -613,21 +749,21 @@ export default {
|
|
613
|
749
|
|
|
614
|
750
|
Promise.all([portraitPromise, tagPromise]).finally(() => {
|
|
615
|
751
|
this.$nextTick(() => {
|
|
616
|
|
-
|
|
|
752
|
+
|
|
617
|
753
|
this.initRadarChart()
|
|
618
|
754
|
this.initScoreChart()
|
|
619
|
755
|
})
|
|
620
|
756
|
})
|
|
621
|
757
|
},
|
|
622
|
758
|
initScoreChart() {
|
|
623
|
|
-
|
|
|
759
|
+
|
|
624
|
760
|
if (!this.$refs.scoreCircle) return
|
|
625
|
761
|
if (this.scoreChartInstance) {
|
|
626
|
762
|
this.scoreChartInstance.dispose()
|
|
627
|
763
|
}
|
|
628
|
764
|
this.scoreChartInstance = echarts.init(this.$refs.scoreCircle)
|
|
629
|
765
|
const score = this.portrait.totalScore || 0
|
|
630
|
|
-
|
|
|
766
|
+
|
|
631
|
767
|
const option = {
|
|
632
|
768
|
series: [
|
|
633
|
769
|
{
|
|
|
@@ -697,58 +833,113 @@ export default {
|
|
697
|
833
|
this.scoreChartInstance.setOption(option)
|
|
698
|
834
|
},
|
|
699
|
835
|
initRadarChart() {
|
|
700
|
|
-
|
|
|
836
|
+
|
|
701
|
837
|
if (!this.$refs.radarChart) return
|
|
702
|
838
|
if (this.radarChartInstance) {
|
|
703
|
839
|
this.radarChartInstance.dispose()
|
|
704
|
840
|
}
|
|
705
|
841
|
this.radarChartInstance = echarts.init(this.$refs.radarChart)
|
|
706
|
842
|
const dimensions = this.portrait.dimensions || []
|
|
707
|
|
- const maxScore = dimensions.length > 0 ? Math.max(...dimensions.map(d => d.score || 0)) : 100
|
|
708
|
|
- const indicator = dimensions.map(d => ({
|
|
709
|
|
- name: d.name + '\n' + d.score,
|
|
710
|
|
- max: maxScore
|
|
711
|
|
- }))
|
|
|
843
|
+
|
|
|
844
|
+ // 仿照 index copy.vue 的配置
|
|
|
845
|
+ const radarData = {
|
|
|
846
|
+ grounp: dimensions.map(d => ({ name: d.name + '\n\n' + d.score, max: 100 })),
|
|
|
847
|
+ data: [{
|
|
|
848
|
+ name: '个人能力',
|
|
|
849
|
+ value: dimensions.map(attr => attr.score),
|
|
|
850
|
+ symbolSize: 10,
|
|
|
851
|
+ areaStyle: {
|
|
|
852
|
+ show: false,
|
|
|
853
|
+ opacity: 0
|
|
|
854
|
+ },
|
|
|
855
|
+ lineStyle: {
|
|
|
856
|
+ color: '#4DC8FE',
|
|
|
857
|
+ width: 1
|
|
|
858
|
+ },
|
|
|
859
|
+ itemStyle: { color: '#fff', borderWidth: 1, borderColor: '#00C8DA', borderJoin: 'round' }
|
|
|
860
|
+ }]
|
|
|
861
|
+ }
|
|
|
862
|
+
|
|
712
|
863
|
const option = {
|
|
713
|
864
|
radar: {
|
|
714
|
|
- indicator: indicator,
|
|
|
865
|
+ indicator: radarData.grounp,
|
|
715
|
866
|
center: ['50%', '52%'],
|
|
716
|
|
- radius: '60%',
|
|
|
867
|
+ radius: '50%',
|
|
717
|
868
|
splitNumber: 8,
|
|
718
|
|
- axisLine: {
|
|
719
|
|
- lineStyle: { color: 'rgba(0,0,0,0.15)' }
|
|
720
|
|
- },
|
|
721
|
|
- splitLine: {
|
|
722
|
|
- lineStyle: {
|
|
723
|
|
- color: ['rgba(0,0,0,0.1)', 'rgba(0,0,0,0.1)', 'rgba(0,0,0,0.1)', 'rgba(0,0,0,0.1)', 'rgba(0,0,0,0.1)', 'rgba(0,0,0,0.1)', '#fe4322', '#8EC742', 'rgba(0,0,0,0.1)']
|
|
724
|
|
- }
|
|
725
|
|
- },
|
|
|
869
|
+ axisLine: { lineStyle: { color: '#ccc' } },
|
|
|
870
|
+ splitLine: { lineStyle: { color: ['#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#fe4322', '#8EC742', '#ccc'], width: 1 } },
|
|
726
|
871
|
splitArea: { show: false },
|
|
727
|
872
|
axisName: {
|
|
728
|
873
|
color: '#333',
|
|
729
|
874
|
fontSize: 12
|
|
730
|
875
|
}
|
|
731
|
876
|
},
|
|
732
|
|
- series: [{
|
|
733
|
|
- type: 'radar',
|
|
734
|
|
- data: [{
|
|
735
|
|
- name: '个人能力',
|
|
736
|
|
- value: dimensions.map(d => d.score),
|
|
737
|
|
- areaStyle: { color: 'rgba(77, 200, 254, 0.2)' },
|
|
738
|
|
- lineStyle: { color: '#4DC8FE', width: 2 },
|
|
739
|
|
- itemStyle: {
|
|
740
|
|
- color: '#333',
|
|
741
|
|
- borderWidth: 1,
|
|
742
|
|
- borderColor: '#00C8DA'
|
|
743
|
|
- },
|
|
|
877
|
+ series: [
|
|
|
878
|
+ {
|
|
|
879
|
+ type: 'radar',
|
|
|
880
|
+ data: radarData.data,
|
|
744
|
881
|
symbol: 'circle',
|
|
745
|
|
- symbolSize: 8
|
|
746
|
|
- }]
|
|
747
|
|
- }]
|
|
|
882
|
+ symbolSize: 13,
|
|
|
883
|
+ label: {
|
|
|
884
|
+ show: false,
|
|
|
885
|
+ formatter: (p) => p.value,
|
|
|
886
|
+ color: '#333',
|
|
|
887
|
+ fontSize: 16,
|
|
|
888
|
+ fontWeight: 'bold'
|
|
|
889
|
+ }
|
|
|
890
|
+ }
|
|
|
891
|
+ ]
|
|
748
|
892
|
}
|
|
|
893
|
+
|
|
749
|
894
|
this.radarChartInstance.setOption(option)
|
|
|
895
|
+
|
|
|
896
|
+ // 绑定鼠标事件
|
|
|
897
|
+ this.bindRadarEvents()
|
|
|
898
|
+ },
|
|
|
899
|
+ bindRadarEvents() {
|
|
|
900
|
+ if (!this.radarChartInstance || !this.$refs.radarChart) return
|
|
|
901
|
+
|
|
|
902
|
+ this.radarChartInstance.off('click')
|
|
|
903
|
+
|
|
|
904
|
+ this.radarChartInstance.on('click', (params) => {
|
|
|
905
|
+ if (!Array.isArray(this.portrait.dimensions) || !this.portrait.dimensions.length) return
|
|
|
906
|
+
|
|
|
907
|
+ const rect = this.$refs.radarChart.getBoundingClientRect()
|
|
|
908
|
+ if (!rect) return
|
|
|
909
|
+
|
|
|
910
|
+ const cx = rect.width / 2
|
|
|
911
|
+ const cy = rect.height / 2
|
|
|
912
|
+ const dx = params.event.offsetX - cx
|
|
|
913
|
+ const dy = params.event.offsetY - cy
|
|
|
914
|
+ const distance = Math.sqrt(dx * dx + dy * dy)
|
|
|
915
|
+ const radarRadius = rect.width * 0.35 // 对应 radius: 65%
|
|
|
916
|
+
|
|
|
917
|
+ if (distance > radarRadius) {
|
|
|
918
|
+ return
|
|
|
919
|
+ }
|
|
|
920
|
+
|
|
|
921
|
+ // 角度计算
|
|
|
922
|
+ let angle = Math.atan2(dx, -dy) * (180 / Math.PI)
|
|
|
923
|
+ if (angle < 0) angle += 360
|
|
|
924
|
+ const count = this.portrait.dimensions.length
|
|
|
925
|
+ const step = 360 / count
|
|
|
926
|
+ let idx = Math.abs(Math.round(angle / step) % count - count)
|
|
|
927
|
+ if (idx < 0) idx += count
|
|
|
928
|
+ if (idx >= count) idx = 0
|
|
|
929
|
+
|
|
|
930
|
+ // 显示tooltip popup - 加上小延迟避免 u-popup 立即关闭的问题
|
|
|
931
|
+ this.activeDimName = this.portrait.dimensions[idx].name
|
|
|
932
|
+ setTimeout(() => {
|
|
|
933
|
+ this.showRadarTooltipPopup = true
|
|
|
934
|
+ }, 50)
|
|
|
935
|
+ })
|
|
|
936
|
+ },
|
|
|
937
|
+ // 关闭雷达图 tooltip popup
|
|
|
938
|
+ closeRadarPopup() {
|
|
|
939
|
+ this.showRadarTooltipPopup = false
|
|
750
|
940
|
}
|
|
751
|
941
|
}
|
|
|
942
|
+
|
|
752
|
943
|
}
|
|
753
|
944
|
</script>
|
|
754
|
945
|
|
|
|
@@ -947,7 +1138,7 @@ export default {
|
|
947
|
1138
|
border-bottom: 2rpx solid transparent;
|
|
948
|
1139
|
transition: all 0.3s;
|
|
949
|
1140
|
|
|
950
|
|
- &.active {
|
|
|
1141
|
+ &.active {
|
|
951
|
1142
|
color: #333;
|
|
952
|
1143
|
border-bottom-color: #333;
|
|
953
|
1144
|
}
|
|
|
@@ -1147,6 +1338,63 @@ export default {
|
|
1147
|
1338
|
height: 500rpx;
|
|
1148
|
1339
|
}
|
|
1149
|
1340
|
|
|
|
1341
|
+.chart-legend {
|
|
|
1342
|
+ display: flex;
|
|
|
1343
|
+ justify-content: center;
|
|
|
1344
|
+ align-items: center;
|
|
|
1345
|
+ flex-wrap: wrap;
|
|
|
1346
|
+ gap: 10px 18px;
|
|
|
1347
|
+ padding-top: 8px;
|
|
|
1348
|
+ color: #fff;
|
|
|
1349
|
+ font-size: 14px;
|
|
|
1350
|
+
|
|
|
1351
|
+}
|
|
|
1352
|
+
|
|
|
1353
|
+.legend-item {
|
|
|
1354
|
+ display: flex;
|
|
|
1355
|
+ align-items: center;
|
|
|
1356
|
+ gap: 6rpx;
|
|
|
1357
|
+
|
|
|
1358
|
+ span {
|
|
|
1359
|
+ width: 24rpx;
|
|
|
1360
|
+ height: 10rpx;
|
|
|
1361
|
+ border: 2rpx solid currentColor;
|
|
|
1362
|
+ border-radius: 2rpx;
|
|
|
1363
|
+ }
|
|
|
1364
|
+}
|
|
|
1365
|
+
|
|
|
1366
|
+.legend-warning {
|
|
|
1367
|
+ color: #fe4322;
|
|
|
1368
|
+
|
|
|
1369
|
+ span {
|
|
|
1370
|
+ background-color: #fe4322;
|
|
|
1371
|
+ }
|
|
|
1372
|
+}
|
|
|
1373
|
+
|
|
|
1374
|
+.legend-normal {
|
|
|
1375
|
+ color: black;
|
|
|
1376
|
+
|
|
|
1377
|
+ span {
|
|
|
1378
|
+ background-color: gray;
|
|
|
1379
|
+ }
|
|
|
1380
|
+}
|
|
|
1381
|
+
|
|
|
1382
|
+.legend-excellent {
|
|
|
1383
|
+ color: #8EC742;
|
|
|
1384
|
+
|
|
|
1385
|
+ span {
|
|
|
1386
|
+ background-color: #8EC742;
|
|
|
1387
|
+ }
|
|
|
1388
|
+}
|
|
|
1389
|
+
|
|
|
1390
|
+.legend-current {
|
|
|
1391
|
+ color: #1890ff;
|
|
|
1392
|
+
|
|
|
1393
|
+ span {
|
|
|
1394
|
+ background-color: #1890ff;
|
|
|
1395
|
+ }
|
|
|
1396
|
+}
|
|
|
1397
|
+
|
|
1150
|
1398
|
.supp-grid {
|
|
1151
|
1399
|
display: grid;
|
|
1152
|
1400
|
grid-template-columns: 1fr 1fr;
|
|
|
@@ -1318,4 +1566,72 @@ export default {
|
|
1318
|
1566
|
}
|
|
1319
|
1567
|
}
|
|
1320
|
1568
|
}
|
|
|
1569
|
+
|
|
|
1570
|
+.radar-tooltip-popup {
|
|
|
1571
|
+ padding: 20rpx;
|
|
|
1572
|
+ min-width: 300px;
|
|
|
1573
|
+
|
|
|
1574
|
+ .p-empty {
|
|
|
1575
|
+ text-align: center;
|
|
|
1576
|
+ margin-top: 10rpx;
|
|
|
1577
|
+ font-size: 24rpx;
|
|
|
1578
|
+ color: #999;
|
|
|
1579
|
+ }
|
|
|
1580
|
+
|
|
|
1581
|
+ .tooltip-header {
|
|
|
1582
|
+ display: flex;
|
|
|
1583
|
+ justify-content: space-between;
|
|
|
1584
|
+ align-items: center;
|
|
|
1585
|
+ padding: 0 10rpx 20rpx;
|
|
|
1586
|
+ border-bottom: 1rpx solid #eee;
|
|
|
1587
|
+ margin-bottom: 20rpx;
|
|
|
1588
|
+
|
|
|
1589
|
+ .tooltip-title {
|
|
|
1590
|
+ font-size: 32rpx;
|
|
|
1591
|
+ font-weight: bold;
|
|
|
1592
|
+ color: #333;
|
|
|
1593
|
+ }
|
|
|
1594
|
+ }
|
|
|
1595
|
+
|
|
|
1596
|
+ .tooltip-section {
|
|
|
1597
|
+ padding: 0 10rpx;
|
|
|
1598
|
+ margin-bottom: 16rpx;
|
|
|
1599
|
+ }
|
|
|
1600
|
+
|
|
|
1601
|
+ .list-item {
|
|
|
1602
|
+ margin-top: 8rpx;
|
|
|
1603
|
+ display: flex;
|
|
|
1604
|
+ align-items: center;
|
|
|
1605
|
+ justify-content: space-between;
|
|
|
1606
|
+ font-size: 26rpx;
|
|
|
1607
|
+ color: #333;
|
|
|
1608
|
+
|
|
|
1609
|
+ .deduct-text {
|
|
|
1610
|
+ color: #ff4d4f;
|
|
|
1611
|
+ }
|
|
|
1612
|
+
|
|
|
1613
|
+ .add-text {
|
|
|
1614
|
+ color: #00b42a;
|
|
|
1615
|
+ }
|
|
|
1616
|
+ }
|
|
|
1617
|
+
|
|
|
1618
|
+ .section-total {
|
|
|
1619
|
+ margin-top: 12rpx;
|
|
|
1620
|
+ padding-top: 12rpx;
|
|
|
1621
|
+ border-top: 1rpx solid #f0f0f0;
|
|
|
1622
|
+ display: flex;
|
|
|
1623
|
+ align-items: center;
|
|
|
1624
|
+ justify-content: space-between;
|
|
|
1625
|
+ font-weight: 500;
|
|
|
1626
|
+ font-size: 28rpx;
|
|
|
1627
|
+
|
|
|
1628
|
+ &.deduct {
|
|
|
1629
|
+ color: #ff4d4f;
|
|
|
1630
|
+ }
|
|
|
1631
|
+
|
|
|
1632
|
+ &.add {
|
|
|
1633
|
+ color: #00b42a;
|
|
|
1634
|
+ }
|
|
|
1635
|
+ }
|
|
|
1636
|
+}
|
|
1321
|
1637
|
</style>
|