index.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  1. <template>
  2. <Page @click="visible = false" v-loading="loading" element-loading-background="rgba(22, 22, 22, 0.8)">
  3. <div class="content">
  4. <div class="content-search">
  5. <SearchBar v-model:visible="visible" @search="searchHandler" :deptType="'user'" />
  6. </div>
  7. <div v-show="portrait.personName" class="content-info">
  8. <Card title="个人基本信息">
  9. <div class="userInfo">
  10. <div class="userInfo-name">
  11. <div class="avatar-area">
  12. <img v-if="portrait.avatar" :src="portrait.avatar" class="avatar" alt="头像" />
  13. <div v-else class="avatar">{{ portrait.personName?.charAt(0) }}</div>
  14. </div>
  15. <div class="basic-name">
  16. <div class="basic-label">姓名:</div>
  17. <div class="basic-value">{{ portrait.personName }}</div>
  18. </div>
  19. </div>
  20. <div class="userInfo-info">
  21. <div class="info-item">
  22. <div class="info-item-icon">
  23. <img src="/src/assets/dataBigScreen/img01.png" alt="" />
  24. </div>
  25. <div class="info-item-content">
  26. <div class="info-item-label">所属部门及队室:</div>
  27. <div class="info-item-value">{{ portrait.deptPath || '-' }}</div>
  28. </div>
  29. </div>
  30. <div class="info-item">
  31. <div class="info-item-icon">
  32. <img src="/src/assets/dataBigScreen/img04.png" alt="" />
  33. </div>
  34. <div class="info-item-content">
  35. <div class="info-item-label">学历:</div>
  36. <div class="info-item-value">{{ getSchooling(portrait.schooling) }}</div>
  37. </div>
  38. </div>
  39. </div>
  40. <div class="userInfo-info">
  41. <div class="info-item">
  42. <div class="info-item-icon">
  43. <img src="/src/assets/dataBigScreen/img02.png" alt="" />
  44. </div>
  45. <div class="info-item-content">
  46. <div class="info-item-label">出生日期:</div>
  47. <div class="info-item-value">{{ portrait.birthday || '-' }}</div>
  48. </div>
  49. </div>
  50. <div class="info-item">
  51. <div class="info-item-icon">
  52. <img src="/src/assets/dataBigScreen/img05.png" alt="" />
  53. </div>
  54. <div class="info-item-content">
  55. <div class="info-item-label">专业:</div>
  56. <div class="info-item-value">{{ portrait.major || '-' }}</div>
  57. </div>
  58. </div>
  59. </div>
  60. <div class="userInfo-info">
  61. <div class="info-item">
  62. <div class="info-item-icon">
  63. <img src="/src/assets/dataBigScreen/img03.png" alt="" />
  64. </div>
  65. <div class="info-item-content">
  66. <div class="info-item-label">技能等级:</div>
  67. <div class="info-item-value">
  68. {{ portrait.qualificationLevelText || '-' }}
  69. <span class="info-item-tag" style="margin-left: 10px;margin-top: 2px;" v-if="!!portrait.xrayOperatorStarttime">X射线安检仪操作岗位</span>
  70. </div>
  71. </div>
  72. </div>
  73. <div class="info-item">
  74. <div class="info-item-icon">
  75. <img src="/src/assets/dataBigScreen/img06.png" alt="" />
  76. </div>
  77. <div class="info-item-content">
  78. <div class="info-item-label">标签:</div>
  79. <div class="info-item-value" v-if="portrait.userTags">
  80. <span class="info-item-tag" v-for="tag in portrait.userTags.split(',')" :key="tag">{{ tag }}</span>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. <div class="userInfo-score">
  86. <div class="score-progress">
  87. <el-progress type="circle" :width="160" :stroke-width="18" color="#5BE39E" :percentage="(portrait.totalScore || -2) + 2">
  88. <div class="percentage-content">
  89. <span class="percentage-value">{{ (portrait.totalScore || 0) }}</span>
  90. <span class="percentage-text">综合得分</span>
  91. </div>
  92. </el-progress>
  93. </div>
  94. <div class="score-box">
  95. <!-- <div class="score-row">
  96. <span class="score-col">评分:</span>
  97. <span class="score-col-2">{{ portrait.totalScore || 0 }}</span>
  98. </div> -->
  99. <div class="score-row">
  100. <span class="score-col">附加分:</span>
  101. <span class="score-col-2">{{ portrait?.userTags?.split(',').length || 0 }}分</span>
  102. <!-- <span class="score-col-2">{{ tagScoreData != null ? (typeof tagScoreData === 'object' ? (tagScoreData.totalScore ?? tagScoreData.score ?? tagScoreData) : tagScoreData) : 0 }}</span> -->
  103. </div>
  104. </div>
  105. </div>
  106. </div>
  107. </Card>
  108. </div>
  109. <div v-show="portrait.personName" class="content-bottom">
  110. <div class="content-bottom-left">
  111. <Card title="工作履历">
  112. <div class="work-history">
  113. <span v-if="portrait.entryDate">
  114. {{ formatWorkDate(portrait.entryDate) }}入职 | 司龄{{ portrait.companyYears != null ? portrait.companyYears : '-' }}年 | 开机年限{{ portrait.xrayOperatorYears != null ? portrait.xrayOperatorYears : '-'
  115. }}年 | 现任职{{ portrait.roleNames || '-' }}
  116. </span>
  117. <span v-else>暂无数据</span>
  118. </div>
  119. </Card>
  120. <Card title="获奖记录">
  121. <div class="card-content">
  122. <div class="honor-item" v-for="(value, index) in portrait.awards" :key="index">
  123. <div :style="{'--indexBgColor': value.color}">
  124. <div :data-index="index + 1"></div>
  125. {{ value.level2Name }} {{ value.level4Name }}
  126. </div>
  127. <div>{{ value.score || '-' }}分</div>
  128. </div>
  129. </div>
  130. </Card>
  131. </div>
  132. <div class="content-bottom-center">
  133. <Card title="个人能力">
  134. <div class="chart-legend">
  135. <div class="legend-item legend-warning"><span></span>预警线(低于75分)</div>
  136. <!-- <div class="legend-item legend-normal"><span></span>正常线(75~90分)</div> -->
  137. <div class="legend-item legend-excellent"><span></span>优秀线(高于90分)</div>
  138. <div class="legend-item legend-current"><span></span>当前员工分值</div>
  139. </div>
  140. <div ref="abilityChart" class="chart-box" @click="goToWarningPage" />
  141. </Card>
  142. </div>
  143. <div class="content-bottom-right">
  144. <Card title="补充信息">
  145. <div class="card-content">
  146. <div class="supp-item">
  147. <span class="s-lbl">政治面貌:</span>
  148. <span class="s-val">{{ portrait.politicalStatusText || '-' }}</span>
  149. </div>
  150. <div class="supp-item supp-item-2">
  151. <span class="s-lbl">性别:</span>
  152. <span class="s-val">{{ portrait.sexText || '-' }}</span>
  153. </div>
  154. <div class="supp-item">
  155. <span class="s-lbl">籍贯:</span>
  156. <span class="s-val">{{ portrait.nativePlace || '-' }}</span>
  157. </div>
  158. <div class="supp-item supp-item-2">
  159. <span class="s-lbl">民族:</span>
  160. <span class="s-val">{{ portrait.nation || '-' }}</span>
  161. </div>
  162. <div class="supp-item">
  163. <span class="s-lbl">年龄:</span>
  164. <span class="s-val">{{ getAge(portrait.birthday) }}</span>
  165. </div>
  166. <div class="supp-item supp-item-2">
  167. <span class="s-lbl">司龄:</span>
  168. <span class="s-val">{{ portrait.companyYears != null ? portrait.companyYears+'年' : '-' }}</span>
  169. </div>
  170. <div class="supp-item">
  171. <span class="s-lbl">性格特征:</span>
  172. <span class="s-val">{{ portrait.characterCharacteristics || '-' }}</span>
  173. </div>
  174. <div class="supp-item supp-item-2">
  175. <span class="s-lbl">工作风格:</span>
  176. <span class="s-val">{{ portrait.workingStyle || '-' }}</span>
  177. </div>
  178. <div :class="i % 2 ? 'supp-item' : 'supp-item supp-item-2'" v-for="(p, i) in (portrait.postNames || '').split('、')" :key="i">
  179. <span class="s-lbl">{{ i === 0 ? '业务岗位:' : ''}}</span>
  180. <span class="s-val">{{ p }}</span>
  181. </div>
  182. </div>
  183. </Card>
  184. </div>
  185. </div>
  186. <div v-show="!portrait.personName" class="ep-empty">
  187. <el-empty description="搜索员工姓名以查看画像" />
  188. </div>
  189. </div>
  190. <div class="radar-tooltip" ref="tipRef">
  191. <Card>
  192. <!-- 标题 -->
  193. <div class="tooltip-title">{{ activeDimName }}</div>
  194. <!-- 加分区域 -->
  195. <div class="tooltip-section">
  196. <div class="section-list">
  197. <div v-if="addGroupList.length" class="list-item" v-for="(item, i) in addGroupList" :key="i">
  198. <span>{{ item.name }}:</span>
  199. <span class="add-text">+{{ item.total }}分</span>
  200. </div>
  201. <div v-else class="p-empty">暂无加分记录</div>
  202. </div>
  203. <div class="section-total add">
  204. <span>合计加分:</span>
  205. <span>{{ addTotal }}分</span>
  206. </div>
  207. </div>
  208. <!-- 扣分区域 -->
  209. <div class="tooltip-section">
  210. <div class="section-list">
  211. <div v-if="deductGroupList.length" class="list-item" v-for="(item, i) in deductGroupList" :key="i">
  212. <span>{{ item.name }}:</span>
  213. <span class="deduct-text">{{ item.total }}分</span>
  214. </div>
  215. <div v-if="!deductGroupList.length" class="p-empty">暂无扣分记录</div>
  216. </div>
  217. <div class="section-total deduct">
  218. <span>合计扣分:</span>
  219. <span>{{ deductTotal }}分</span>
  220. </div>
  221. </div>
  222. </Card>
  223. </div>
  224. </Page>
  225. </template>
  226. <script setup>
  227. import Page from '../components/page.vue'
  228. import Card from '../components/card.vue'
  229. import SearchBar from '../components/SearchBar.vue'
  230. import { getEmployeePortrait } from '@/api/score/index'
  231. import { countTagScore } from '@/api/portraitManagement/portraitManagement'
  232. import { onMounted, onUnmounted, reactive, ref, computed, watch } from 'vue'
  233. import { useDict } from '@/utils/dict'
  234. import { useECharts } from '@/hooks/useEcharts'
  235. import useUserStore from '@/store/modules/user'
  236. import { useRouter } from 'vue-router'
  237. defineOptions({ name: 'ProfileEmployeeProfile' })
  238. const router = useRouter()
  239. let radarChartInstance = null
  240. const { sys_user_schooling } = useDict('sys_user_schooling')
  241. const userStore = useUserStore()
  242. const visible = ref(false)
  243. const loading = ref(false)
  244. const portrait = ref({ dimensions: [] })
  245. const abilityChart = ref(null)
  246. const activeDimName = ref(null)
  247. const tagScoreData = ref(null)
  248. const currentQuery = ref(null)
  249. const scoreDetails = computed(() => portrait.value?.scoreDetails || [])
  250. const addList = computed(() => {
  251. const all = scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) > 0)
  252. return activeDimName.value ? all.filter(d => d.dimensionName === activeDimName.value) : all
  253. })
  254. const deductList = computed(() => {
  255. const all = scoreDetails.value.filter(d => d.totalScore != null && Number(d.totalScore) < 0)
  256. return activeDimName.value ? all.filter(d => d.dimensionName === activeDimName.value) : all
  257. })
  258. const addGroupList = computed(() => {
  259. const groups = {}
  260. addList.value.forEach(d => {
  261. const key = d.level2Name || '其他'
  262. groups[key] = (groups[key] || 0) + Number(d.totalScore)
  263. })
  264. return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
  265. })
  266. const deductGroupList = computed(() => {
  267. const groups = {}
  268. deductList.value.forEach(d => {
  269. const key = d.level2Name || '其他'
  270. groups[key] = (groups[key] || 0) + Number(d.totalScore)
  271. })
  272. return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
  273. })
  274. const addTotal = computed(() => {
  275. const s = addList.value.reduce((acc, d) => acc + Number(d.totalScore), 0)
  276. return (s > 0 ? '+' : '') + s.toFixed(2)
  277. })
  278. const deductTotal = computed(() => {
  279. const s = deductList.value.reduce((acc, d) => acc + Number(d.totalScore), 0)
  280. return s.toFixed(2)
  281. })
  282. const getSchooling = (schooling) => {
  283. const result = (sys_user_schooling.value || []).find(item => item.value === schooling) || { label: schooling }
  284. return result.label || '-'
  285. }
  286. const getAge = (birthday) => {
  287. const birthDate = new Date(birthday);
  288. if (isNaN(birthDate.getTime())) {
  289. return '-'
  290. }
  291. const today = new Date();
  292. // 计算年份差
  293. let age = today.getFullYear() - birthDate.getFullYear();
  294. // 计算月份差
  295. const monthDiff = today.getMonth() - birthDate.getMonth();
  296. // 如果还没到今年生日,年龄减1
  297. if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
  298. age--;
  299. }
  300. return `${age}岁`;
  301. }
  302. const getRandomHexColor = () => {
  303. return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0');
  304. }
  305. const formatWorkDate = (d) => {
  306. if (!d) return '-'
  307. const str = String(d)
  308. const parts = str.substring(0, 10).split('-')
  309. if (parts.length < 3) return str
  310. return `${parts[0]}.${Number(parts[1])}.${Number(parts[2])}`
  311. }
  312. const invokerEmployeePortrait = (query) => {
  313. loading.value = true
  314. const userId = query.id
  315. const queryData = {
  316. ...query
  317. }
  318. delete queryData.id
  319. const portraitPromise = getEmployeePortrait(queryData).then(res => {
  320. portrait.value = res.data || { dimensions: [], awards: [] }
  321. portrait.value.awards.forEach(item => {
  322. item.color = getRandomHexColor()
  323. })
  324. permutationRadarDataHandler(res.data.dimensions)
  325. })
  326. const tagPromise = countTagScore(queryData).then(res => {
  327. const data = res.data
  328. if (Array.isArray(data)) {
  329. const found = data.find(item => (item.userId == userId) )
  330. if (found) {
  331. tagScoreData.value = found.totalScore ?? found.score
  332. } else {
  333. tagScoreData.value = null
  334. }
  335. } else {
  336. tagScoreData.value = data
  337. }
  338. })
  339. return Promise.all([portraitPromise, tagPromise]).finally(() => {
  340. loading.value = false
  341. })
  342. }
  343. const searchHandler = (query) => {
  344. visible.value = false
  345. currentQuery.value = query
  346. invokerEmployeePortrait(query)
  347. }
  348. const goToWarningPage = () => {
  349. if (currentQuery.value) {
  350. router.push({
  351. path: '/score/event',
  352. query: { id: currentQuery.value.id, startDate: currentQuery.value.beginTime, endDate: currentQuery.value.endTime },
  353. })
  354. } else {
  355. router.push('/score/event')
  356. }
  357. }
  358. const radarData = reactive({
  359. grounp: [],
  360. data: []
  361. })
  362. const permutationRadarDataHandler = (result = []) => {
  363. radarData.grounp = result.map(d => ({ name: d.name + '\n\n' + d.score, max: 100 })),
  364. radarData.data = [{
  365. name: '个人能力',
  366. value: result.map(attr => attr.score),
  367. symbolSize: 10,
  368. areaStyle: {
  369. show: false,
  370. opacity: 0,
  371. // color: {
  372. // type: 'linear',
  373. // x: 0,
  374. // y: 0,
  375. // x2: 0,
  376. // y2: 1,
  377. // colorStops: [{
  378. // offset: 0, color: '#4DC8FE'
  379. // }, {
  380. // offset: 1, color: '#6C26F3'
  381. // }],
  382. // global: false
  383. // },
  384. },
  385. lineStyle: {
  386. color: '#4DC8FE',
  387. width: 2
  388. },
  389. // lineStyle: {
  390. // color: {
  391. // type: 'linear',
  392. // x: 0,
  393. // y: 0,
  394. // x2: 0,
  395. // y2: 1,
  396. // colorStops: [{
  397. // offset: 0, color: '#4DC8FE'
  398. // }, {
  399. // offset: 1, color: '#6C26F3'
  400. // }],
  401. // global: false
  402. // },
  403. // width: 5
  404. // },
  405. itemStyle: { color: '#fff', borderWidth: 1, borderColor: '#00C8DA', borderJoin: 'round' }
  406. }]
  407. }
  408. const setRadarOptions = computed(() => {
  409. return {
  410. radar: {
  411. indicator: radarData.grounp,
  412. center: ['50%', '52%'],
  413. radius: '65%',
  414. splitNumber: 8,
  415. axisLine: { lineStyle: { color: '#ccc' } },
  416. splitLine: { lineStyle: { color: ['#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#fe4322', '#8EC742', '#ccc'], width: 3 } },
  417. splitArea: { show: false },
  418. axisName: {
  419. color: '#fff',
  420. fontSize: 18
  421. },
  422. },
  423. series: [
  424. {
  425. type: 'radar',
  426. data: radarData.data,
  427. symbol: 'circle',
  428. symbolSize: 13,
  429. label: {
  430. show: false,
  431. formatter: (p) => p.value,
  432. color: '#fff',
  433. fontSize: 16,
  434. fontWeight: 'bold'
  435. }
  436. }
  437. ]
  438. }
  439. })
  440. let pisition = {
  441. x: 0,
  442. y: 0
  443. }
  444. const tipRef = ref(null)
  445. radarChartInstance = useECharts(abilityChart, setRadarOptions).bindEvent('mousemove', (e) => {
  446. if (!Array.isArray(portrait.value.dimensions) || !portrait.value.dimensions.length) return
  447. const rect = abilityChart.value.getBoundingClientRect()
  448. if (!tipRef.value) return
  449. const cx = rect.width / 2
  450. const cy = rect.height / 2
  451. const dx = e.offsetX - cx
  452. const dy = e.offsetY - cy
  453. const distance = Math.sqrt(dx * dx + dy * dy)
  454. const radarRadius = rect.width * 0.35 // 对应 radius: 65%
  455. if (distance > radarRadius) {
  456. tipRef.value.style.display = 'none'
  457. return
  458. }
  459. // 角度计算
  460. let angle = Math.atan2(dx, -dy) * (180 / Math.PI)
  461. if (angle < 0) angle += 360
  462. const count = portrait.value.dimensions.length
  463. const step = 360 / count
  464. let idx = Math.abs(Math.round(angle / step) % count - count)
  465. if (idx < 0) idx += count
  466. if (idx >= count) idx = 0
  467. // 显示 tooltip
  468. tipRef.value.style.display = 'block'
  469. tipRef.value.style.left = pisition.x + 30 + 'px'
  470. tipRef.value.style.top = pisition.y + 'px'
  471. activeDimName.value = portrait.value.dimensions[idx].name
  472. }).bindEvent('mouseout', () => {
  473. if (tipRef.value) tipRef.value.style.display = 'none'
  474. })
  475. const handleMouseMove = (eve) => {
  476. pisition.x = eve.pageX
  477. pisition.y = eve.pageY
  478. }
  479. onMounted(() => {
  480. window.addEventListener('mousemove', handleMouseMove)
  481. })
  482. onUnmounted(() => {
  483. window.removeEventListener('mousemove', handleMouseMove)
  484. })
  485. </script>
  486. <style lang="scss" scoped>
  487. .content {
  488. height: calc(100vh - 90px);
  489. display: flex;
  490. flex-direction: column;
  491. row-gap: 15px;
  492. padding: 15px;
  493. box-sizing: border-box;
  494. overflow: hidden;
  495. .ep-empty {
  496. background: #fff;
  497. height: 100%;
  498. display: flex;
  499. align-items: center;
  500. justify-content: center;
  501. background: #21213a;
  502. opacity: 0.8;
  503. border-radius: 30px;
  504. }
  505. .content-search {}
  506. .content-info {
  507. & > * {
  508. height: 100%;
  509. }
  510. .userInfo {
  511. display: flex;
  512. width: 100%;
  513. height: 180px;
  514. box-sizing: border-box;
  515. column-gap: 5px;
  516. .userInfo-name {
  517. flex: 1;
  518. position: relative;
  519. display: flex;
  520. align-items: center;
  521. justify-content: center;
  522. column-gap: 30px;
  523. &::after {
  524. content: '';
  525. display: block;
  526. width: 1px;
  527. background:linear-gradient(180deg, transparent, #6f6c98, transparent);
  528. height: 100%;
  529. position: absolute;
  530. top: 0;
  531. right: -2px;
  532. }
  533. .avatar-area {
  534. height: 140px;
  535. width: 140px;
  536. background: #fff;
  537. border-radius: 50%;
  538. display: flex;
  539. align-items: center;
  540. justify-content: center;
  541. .avatar {
  542. width: 100%;
  543. height: 100%;
  544. display: flex;
  545. align-items: center;
  546. justify-content: center;
  547. color: #6f6c98;
  548. font-size: 48px;
  549. font-weight: 900;
  550. }
  551. }
  552. .basic-name {
  553. display: flex;
  554. flex-direction: column;
  555. justify-content: center;
  556. row-gap: 10px;
  557. font-weight: 500;
  558. .basic-label {
  559. font-size: 18px;
  560. color: #8675AE;
  561. }
  562. .basic-value {
  563. font-size: 26px;
  564. color: #fff;
  565. }
  566. }
  567. }
  568. .userInfo-info {
  569. flex: 1;
  570. position: relative;
  571. display: flex;
  572. flex-direction: column;
  573. justify-content: space-between;
  574. padding: 10px 15px;
  575. padding-right: 0px;
  576. box-sizing: border-box;
  577. &::after {
  578. content: '';
  579. display: block;
  580. width: 1px;
  581. background:linear-gradient(180deg, transparent, #6f6c98, transparent);
  582. height: 100%;
  583. position: absolute;
  584. top: 0;
  585. right: -2px;
  586. }
  587. .info-item {
  588. display: flex;
  589. column-gap: 15px;
  590. .info-item-content {
  591. display: flex;
  592. flex-direction: column;
  593. justify-content: space-between;
  594. font-weight: 500;
  595. .info-item-label {
  596. font-size: 18px;
  597. color: #8675AE;
  598. }
  599. .info-item-value {
  600. font-size: 18px;
  601. color: #fff;
  602. display: flex;
  603. align-items: center;
  604. flex-wrap: wrap;
  605. gap: 2px;
  606. }
  607. .info-item-tag {
  608. font-size: 14px;
  609. display: block;
  610. padding: 3px 15px;
  611. background: linear-gradient(125deg, #2AB3E6, #9605FC);
  612. border-radius: 20px;
  613. }
  614. }
  615. }
  616. }
  617. .userInfo-score {
  618. :deep(.el-progress-circle) {
  619. width: 160px;
  620. height: 160px;
  621. --el-fill-color-light: #004970;
  622. transform: rotate(0.5turn);
  623. }
  624. flex: 1;
  625. display: flex;
  626. align-items: center;
  627. padding-left: 15px;
  628. column-gap: 25px;
  629. .score-progress {
  630. .percentage-content {
  631. display: flex;
  632. flex-direction: column;
  633. align-items: center;
  634. row-gap: 6px;
  635. font-weight: 500;
  636. .percentage-value {
  637. font-size: 48px;
  638. color: #02E5CC;
  639. }
  640. .percentage-text {
  641. font-size: 16px;
  642. color: #fff;
  643. }
  644. }
  645. }
  646. .score-box {
  647. display: flex;
  648. flex-direction: column;
  649. font-weight: 500;
  650. row-gap: 15px;
  651. font-size: 18px;
  652. .score-col {
  653. display: inline-block;
  654. width: 5em;
  655. }
  656. .score-col-2 {
  657. font-size: 24px;
  658. }
  659. }
  660. }
  661. }
  662. }
  663. .content-bottom {
  664. flex: 1;
  665. display: flex;
  666. column-gap: 15px;
  667. overflow: hidden;
  668. .content-bottom-left {
  669. width: 380px;
  670. display: flex;
  671. flex-direction: column;
  672. row-gap: 15px;
  673. & > *:nth-child(1) {
  674. height: 160px;
  675. .work-history {
  676. font-size: 18px;
  677. }
  678. }
  679. & > *:nth-child(2) {
  680. flex: 1.6;
  681. .honor-item {
  682. height: 48px;
  683. padding-right: 15px;
  684. box-sizing: border-box;
  685. font-weight: 500;
  686. font-size: 18px;
  687. display: flex;
  688. align-items: center;
  689. justify-content: space-between;
  690. position: relative;
  691. & > div:nth-child(1) {
  692. display: flex;
  693. align-items: center;
  694. column-gap: 15px;
  695. margin-left: 10px;
  696. & > div {
  697. width: 26px;
  698. height: 20px;
  699. position: relative;
  700. font-size: 18px;
  701. text-align: center;
  702. line-height: 20px;
  703. color: #222;
  704. &::before {
  705. position: absolute;
  706. inset: 0;
  707. content: '';
  708. display: block;
  709. background: var(--indexBgColor);
  710. width: 100%;
  711. height: 100%;
  712. transform: skew(-25deg);
  713. }
  714. &::after {
  715. position: absolute;
  716. inset: 0;
  717. content: attr(data-index);
  718. display: block;
  719. width: 100%;
  720. height: 100%;
  721. z-index: 100;
  722. }
  723. }
  724. }
  725. & > div:nth-child(2) {
  726. color: #02E5CC;
  727. }
  728. &::after {
  729. content: '';
  730. display: block;
  731. position: absolute;
  732. bottom: 0;
  733. left: 0;
  734. height: 1px;
  735. width: 100%;
  736. background:linear-gradient(45deg, transparent 0%, transparent 30%, #6f6c98 100%);
  737. }
  738. }
  739. }
  740. }
  741. .content-bottom-center {
  742. flex: 1;
  743. & > * {
  744. height: 100%;
  745. }
  746. .chart-legend {
  747. display: flex;
  748. justify-content: center;
  749. align-items: center;
  750. flex-wrap: wrap;
  751. gap: 10px 18px;
  752. padding-top: 8px;
  753. color: #fff;
  754. font-size: 14px;
  755. }
  756. .legend-item {
  757. display: flex;
  758. align-items: center;
  759. gap: 6px;
  760. span {
  761. width: 24px;
  762. height: 10px;
  763. border: 2px solid currentColor;
  764. border-radius: 2px;
  765. }
  766. }
  767. .legend-warning {
  768. color: #fe4322;
  769. span {
  770. background-color: #fe4322;
  771. }
  772. }
  773. .legend-normal {
  774. color: #fff;
  775. span {
  776. background-color: #fff;
  777. }
  778. }
  779. .legend-excellent {
  780. color: #8EC742;
  781. span {
  782. background-color: #8EC742;
  783. }
  784. }
  785. .legend-current {
  786. color: #1890ff;
  787. span {
  788. background-color: #1890ff;
  789. }
  790. }
  791. .chart-box {
  792. width: 100%;
  793. height: calc(100% - 34px);
  794. overflow: hidden;
  795. }
  796. }
  797. .content-bottom-right {
  798. width: 380px;
  799. & > * {
  800. height: 100%;
  801. }
  802. .supp-item {
  803. display: flex;
  804. justify-content: space-between;
  805. align-items: center;
  806. height: 42px;
  807. padding: 0 15px;
  808. font-weight: 500;
  809. font-size: 18px;
  810. .s-lbl {
  811. color: #8675AE;
  812. }
  813. .s-val {
  814. color: #fff;
  815. }
  816. }
  817. .supp-item-2 {
  818. background: #261C48;
  819. }
  820. }
  821. .card-content {
  822. height: 100%;
  823. overflow-x: hidden;
  824. overflow-y: auto;
  825. &::-webkit-scrollbar-track {
  826. background: transparent;
  827. }
  828. &::-webkit-scrollbar-thumb {
  829. background: linear-gradient(225deg, #4355cb, #873dc3);
  830. border-radius: 4px;
  831. }
  832. }
  833. }
  834. }
  835. /* Tooltip 样式(全部放这里) */
  836. .radar-tooltip {
  837. position: fixed;
  838. min-width: 300px;
  839. z-index: 999;
  840. pointer-events: none;
  841. transform: translateY(-80%);
  842. display: none;
  843. .p-empty {
  844. text-align: center;
  845. margin-top: 5px;
  846. }
  847. .tooltip-title {
  848. font-size: 18px;
  849. font-weight: bold;
  850. color: #fff;
  851. padding: 0 10px;
  852. }
  853. .tooltip-section {
  854. padding: 0 10px;
  855. }
  856. .list-item {
  857. margin-top: 4px;
  858. display: flex;
  859. align-items: center;
  860. justify-content: space-between;
  861. color: #fff;
  862. .deduct-text {
  863. color: #ff4d4f;
  864. }
  865. .add-text {
  866. color: #00b42a;
  867. }
  868. }
  869. .section-total {
  870. margin-top: 4px;
  871. display: flex;
  872. align-items: center;
  873. justify-content: space-between;
  874. font-weight: 500;
  875. font-size: 16px;
  876. &.deduct {
  877. color: #ff4d4f;
  878. }
  879. &.add {
  880. color: #00b42a;
  881. }
  882. }
  883. }
  884. </style>