|
|
@@ -130,39 +130,44 @@
|
|
130
|
130
|
<div class="card radar-card">
|
|
131
|
131
|
<div class="sec-title">● 个人能力</div>
|
|
132
|
132
|
<div class="radar-body">
|
|
133
|
|
- <!-- 扣分明细浮窗 -->
|
|
134
|
|
- <div class="score-panel deduct-panel">
|
|
135
|
|
- <div class="panel-header deduct-header">
|
|
136
|
|
- 扣分明细 <span class="panel-total">合计 {{ deductTotal }}</span>
|
|
137
|
|
- </div>
|
|
138
|
|
- <div class="panel-list">
|
|
139
|
|
- <div v-for="(item, i) in deductList" :key="i" class="panel-row">
|
|
140
|
|
- <span class="p-dim">{{ item.dimensionName }}</span>
|
|
141
|
|
- <span class="p-name">{{ item.level2Name }}{{ item.level3Name ? '·'+item.level3Name : '' }}</span>
|
|
142
|
|
- <span class="p-score deduct-score">{{ item.totalScore }}</span>
|
|
|
133
|
+ <!-- 扣分明细浮窗(点击维度后显示,绝对定位左侧) -->
|
|
|
134
|
+ <transition name="panel-fade">
|
|
|
135
|
+ <div class="score-panel deduct-panel" v-if="activeDimName">
|
|
|
136
|
+ <div class="panel-header deduct-header">
|
|
|
137
|
+ 扣分明细<span class="panel-dim-name">{{ activeDimName }}</span>
|
|
|
138
|
+ <span class="panel-total">合计 {{ deductTotal }}</span>
|
|
|
139
|
+ </div>
|
|
|
140
|
+ <div class="panel-list">
|
|
|
141
|
+ <div v-for="(item, i) in deductList" :key="i" class="panel-row">
|
|
|
142
|
+ <span class="p-name">{{ item.level3Name || item.level2Name }}</span>
|
|
|
143
|
+ <span class="p-score deduct-score">{{ item.totalScore }}</span>
|
|
|
144
|
+ </div>
|
|
|
145
|
+ <div v-if="!deductList.length" class="p-empty">暂无扣分记录</div>
|
|
143
|
146
|
</div>
|
|
144
|
|
- <div v-if="!deductList.length" class="p-empty">暂无扣分记录</div>
|
|
145
|
147
|
</div>
|
|
146
|
|
- </div>
|
|
|
148
|
+ </transition>
|
|
147
|
149
|
|
|
148
|
150
|
<!-- 雷达图 -->
|
|
149
|
151
|
<div ref="abilityChart" class="chart-box"></div>
|
|
150
|
152
|
|
|
151
|
|
- <!-- 加分明细浮窗 -->
|
|
152
|
|
- <div class="score-panel add-panel">
|
|
153
|
|
- <div class="panel-header add-header">
|
|
154
|
|
- 加分明细 <span class="panel-total">合计 {{ addTotal }}</span>
|
|
155
|
|
- </div>
|
|
156
|
|
- <div class="panel-list">
|
|
157
|
|
- <div v-for="(item, i) in addList" :key="i" class="panel-row">
|
|
158
|
|
- <span class="p-dim">{{ item.dimensionName }}</span>
|
|
159
|
|
- <span class="p-name">{{ item.level2Name }}{{ item.level3Name ? '·'+item.level3Name : '' }}</span>
|
|
160
|
|
- <span class="p-score add-score">+{{ item.totalScore }}</span>
|
|
|
153
|
+ <!-- 加分明细浮窗(点击维度后显示,绝对定位右侧) -->
|
|
|
154
|
+ <transition name="panel-fade">
|
|
|
155
|
+ <div class="score-panel add-panel" v-if="activeDimName">
|
|
|
156
|
+ <div class="panel-header add-header">
|
|
|
157
|
+ 加分明细<span class="panel-dim-name">{{ activeDimName }}</span>
|
|
|
158
|
+ <span class="panel-total">合计 {{ addTotal }}</span>
|
|
|
159
|
+ </div>
|
|
|
160
|
+ <div class="panel-list">
|
|
|
161
|
+ <div v-for="(item, i) in addList" :key="i" class="panel-row">
|
|
|
162
|
+ <span class="p-name">{{ item.level3Name || item.level2Name }}</span>
|
|
|
163
|
+ <span class="p-score add-score">+{{ item.totalScore }}</span>
|
|
|
164
|
+ </div>
|
|
|
165
|
+ <div v-if="!addList.length" class="p-empty">暂无加分记录</div>
|
|
161
|
166
|
</div>
|
|
162
|
|
- <div v-if="!addList.length" class="p-empty">暂无加分记录</div>
|
|
163
|
167
|
</div>
|
|
164
|
|
- </div>
|
|
|
168
|
+ </transition>
|
|
165
|
169
|
</div>
|
|
|
170
|
+ <div class="radar-hint" v-if="!activeDimName">点击雷达图维度查看明细</div>
|
|
166
|
171
|
</div>
|
|
167
|
172
|
</div>
|
|
168
|
173
|
|
|
|
@@ -341,14 +346,18 @@ const positionList = computed(() => {
|
|
341
|
346
|
return pos.split(/[,,、/]/).map(s => s.trim()).filter(Boolean)
|
|
342
|
347
|
})
|
|
343
|
348
|
|
|
|
349
|
+const activeDimName = ref(null)
|
|
|
350
|
+
|
|
344
|
351
|
const scoreDetails = computed(() => portrait.value?.scoreDetails || [])
|
|
345
|
352
|
|
|
346
|
|
-const addList = computed(() =>
|
|
347
|
|
- scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) > 0)
|
|
348
|
|
-)
|
|
349
|
|
-const deductList = computed(() =>
|
|
350
|
|
- scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) < 0)
|
|
351
|
|
-)
|
|
|
353
|
+const addList = computed(() => {
|
|
|
354
|
+ const all = scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) > 0)
|
|
|
355
|
+ return activeDimName.value ? all.filter(d => d.dimensionName === activeDimName.value) : all
|
|
|
356
|
+})
|
|
|
357
|
+const deductList = computed(() => {
|
|
|
358
|
+ const all = scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) < 0)
|
|
|
359
|
+ return activeDimName.value ? all.filter(d => d.dimensionName === activeDimName.value) : all
|
|
|
360
|
+})
|
|
352
|
361
|
const addTotal = computed(() => {
|
|
353
|
362
|
const s = addList.value.reduce((acc, d) => acc + Number(d.totalScore), 0)
|
|
354
|
363
|
return (s > 0 ? '+' : '') + s.toFixed(2)
|
|
|
@@ -388,7 +397,6 @@ const initChart = () => {
|
|
388
|
397
|
const c = radarColor.value
|
|
389
|
398
|
chart.setOption({
|
|
390
|
399
|
radar: {
|
|
391
|
|
- // 指标名显示加权前分值
|
|
392
|
400
|
indicator: dims.map(d => ({ name: d.name + '\n' + d.score, max: 100 })),
|
|
393
|
401
|
center: ['50%', '52%'],
|
|
394
|
402
|
radius: '65%',
|
|
|
@@ -419,9 +427,29 @@ const initChart = () => {
|
|
419
|
427
|
}],
|
|
420
|
428
|
tooltip: { trigger: 'item' }
|
|
421
|
429
|
})
|
|
|
430
|
+
|
|
|
431
|
+ // 点击雷达图按角度判断最近维度
|
|
|
432
|
+ chart.getZr().on('click', (event) => {
|
|
|
433
|
+ if (!dims.length) return
|
|
|
434
|
+ const el = abilityChart.value
|
|
|
435
|
+ const cx = el.offsetWidth * 0.5
|
|
|
436
|
+ const cy = el.offsetHeight * 0.52
|
|
|
437
|
+ const dx = event.offsetX - cx
|
|
|
438
|
+ const dy = event.offsetY - cy
|
|
|
439
|
+ let angle = Math.atan2(dx, -dy) * 180 / Math.PI
|
|
|
440
|
+ if (angle < 0) angle += 360
|
|
|
441
|
+ const step = 360 / dims.length
|
|
|
442
|
+ const idx = Math.round(angle / step) % dims.length
|
|
|
443
|
+ const name = dims[idx]?.name
|
|
|
444
|
+ if (!name) return
|
|
|
445
|
+ activeDimName.value = activeDimName.value === name ? null : name
|
|
|
446
|
+ })
|
|
422
|
447
|
}
|
|
423
|
448
|
|
|
424
|
|
-watch(portrait, (val) => { if (val) nextTick(() => initChart()) })
|
|
|
449
|
+watch(portrait, (val) => {
|
|
|
450
|
+ activeDimName.value = null
|
|
|
451
|
+ if (val) nextTick(() => initChart())
|
|
|
452
|
+})
|
|
425
|
453
|
|
|
426
|
454
|
const handleResize = () => { chart?.resize() }
|
|
427
|
455
|
|
|
|
@@ -775,7 +803,8 @@ onBeforeUnmount(() => {
|
|
775
|
803
|
}
|
|
776
|
804
|
|
|
777
|
805
|
.chart-box {
|
|
778
|
|
- flex: 1;
|
|
|
806
|
+ width: 100%;
|
|
|
807
|
+ height: 100%;
|
|
779
|
808
|
min-height: 400px;
|
|
780
|
809
|
}
|
|
781
|
810
|
}
|
|
|
@@ -943,24 +972,30 @@ onBeforeUnmount(() => {
|
|
943
|
972
|
/* ── 雷达图主体(含两侧浮窗) ── */
|
|
944
|
973
|
.radar-body {
|
|
945
|
974
|
flex: 1;
|
|
946
|
|
- display: flex;
|
|
947
|
|
- gap: 8px;
|
|
|
975
|
+ position: relative;
|
|
948
|
976
|
min-height: 0;
|
|
949
|
977
|
}
|
|
950
|
978
|
|
|
951
|
|
-/* ── 加/扣分浮窗 ── */
|
|
|
979
|
+/* ── 加/扣分浮窗(绝对定位,叠在 chart 上方) ── */
|
|
952
|
980
|
.score-panel {
|
|
|
981
|
+ position: absolute;
|
|
|
982
|
+ top: 0;
|
|
953
|
983
|
width: 180px;
|
|
954
|
|
- flex-shrink: 0;
|
|
|
984
|
+ max-height: 100%;
|
|
955
|
985
|
display: flex;
|
|
956
|
986
|
flex-direction: column;
|
|
957
|
987
|
border-radius: 12px;
|
|
958
|
988
|
overflow: hidden;
|
|
959
|
989
|
border: 1px solid #e0e7f0;
|
|
960
|
990
|
font-size: 12px;
|
|
961
|
|
- background: #fff;
|
|
|
991
|
+ background: rgba(255,255,255,0.92);
|
|
|
992
|
+ box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
|
|
993
|
+ z-index: 10;
|
|
962
|
994
|
}
|
|
963
|
995
|
|
|
|
996
|
+.deduct-panel { left: 0; }
|
|
|
997
|
+.add-panel { right: 0; }
|
|
|
998
|
+
|
|
964
|
999
|
.panel-header {
|
|
965
|
1000
|
padding: 6px 10px;
|
|
966
|
1001
|
font-size: 13px;
|
|
|
@@ -972,12 +1007,34 @@ onBeforeUnmount(() => {
|
|
972
|
1007
|
.deduct-header { background: rgba(254,233,232,1); color: #e03030; }
|
|
973
|
1008
|
.add-header { background: rgba(206,248,228,1); color: #1a9944; }
|
|
974
|
1009
|
|
|
|
1010
|
+.panel-dim-name {
|
|
|
1011
|
+ font-size: 11px;
|
|
|
1012
|
+ font-weight: normal;
|
|
|
1013
|
+ margin-left: 4px;
|
|
|
1014
|
+ opacity: 0.8;
|
|
|
1015
|
+ max-width: 60px;
|
|
|
1016
|
+ overflow: hidden;
|
|
|
1017
|
+ text-overflow: ellipsis;
|
|
|
1018
|
+ white-space: nowrap;
|
|
|
1019
|
+}
|
|
|
1020
|
+
|
|
975
|
1021
|
.panel-total {
|
|
976
|
1022
|
font-size: 12px;
|
|
977
|
1023
|
font-weight: bold;
|
|
978
|
1024
|
margin-left: auto;
|
|
979
|
1025
|
}
|
|
980
|
1026
|
|
|
|
1027
|
+.radar-hint {
|
|
|
1028
|
+ text-align: center;
|
|
|
1029
|
+ font-size: 12px;
|
|
|
1030
|
+ color: #aaa;
|
|
|
1031
|
+ margin-top: 4px;
|
|
|
1032
|
+ flex-shrink: 0;
|
|
|
1033
|
+}
|
|
|
1034
|
+
|
|
|
1035
|
+.panel-fade-enter-active, .panel-fade-leave-active { transition: opacity 0.2s; }
|
|
|
1036
|
+.panel-fade-enter-from, .panel-fade-leave-to { opacity: 0; }
|
|
|
1037
|
+
|
|
981
|
1038
|
.panel-list {
|
|
982
|
1039
|
flex: 1;
|
|
983
|
1040
|
overflow-y: auto;
|