| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749 |
- <template>
- <view class="employee-profile-page">
- <view class="page-header">
- <view class="header-title">
- <view class="title-main">员工综合信息展示</view>
- <view class="header-right">
- <view class="current-time">{{ currentTime }}</view>
- </view>
- </view>
- <view class="time-filter">
- <scroll-view scroll-x class="time-scroll">
- <view class="time-tags">
- <view v-for="(tag, index) in timeTags" :key="index"
- :class="['time-tag', { active: selectedTimeTag === index }]" @click="onTimeTagClick(index)">
- {{ tag }}
- </view>
- </view>
- </scroll-view>
- <view v-if="selectedTimeTag === 4" class="date-range-picker">
- <picker mode="date" :value="beginTime" @change="onBeginTimeChange">
- <view class="date-input" :class="{ filled: beginTime }">
- {{ beginTime || '开始日期' }}
- </view>
- </picker>
- <text class="date-separator">至</text>
- <picker mode="date" :value="endTime" @change="onEndTimeChange">
- <view class="date-input" :class="{ filled: endTime }">
- {{ endTime || '结束日期' }}
- </view>
- </picker>
- </view>
- </view>
- <view class="search-selector">
- <view class="search-select-trigger"
- :class="{ 'search-select-trigger-disabled': isSecurityCheck || routeUserId }"
- @click="!isSecurityCheck && !routeUserId && (showEmployeePicker = true)">
- <u-icon name="list" color="#A78BFA" size="16"></u-icon>
- <text class="search-name-text">{{ selectedEmployeeName || '请选择员工' }}</text>
- <u-icon v-if="!isSecurityCheck && !routeUserId" name="arrow-down" color="#A78BFA"
- size="14"></u-icon>
- </view>
- </view>
- <!-- <view class="tab-nav">
- <view class="tab-item" :class="{ active: activeTab === 'profile' }" @click="activeTab = 'profile'">
- 能力画像
- </view>
- <view class="tab-item" :class="{ active: activeTab === 'data' }" @click="activeTab = 'data'">
- 运行数据
- </view>
- </view> -->
- </view>
- <view class="page-content">
- <view v-if="!portrait.personName" class="empty-state">
- <view class="empty-icon">📋</view>
- <view class="empty-text">搜索员工姓名以查看画像</view>
- </view>
- <view v-if="portrait.personName">
- <SectionTitle title="个人基本信息">
- <view class="user-info">
- <view class="user-header">
- <view class="avatar-area">
- <image v-if="portrait.avatar" :src="portrait.avatar" class="avatar" mode="aspectFill">
- </image>
- <view v-else class="avatar-placeholder">{{ portrait.personName ?
- portrait.personName.charAt(0) : '' }}</view>
- </view>
- <view class="name-row">
- <view class="name-label">姓名:</view>
- <view class="name-value">{{ portrait.personName }}</view>
- </view>
- </view>
- <view class="info-grid">
- <view class="info-item">
- <view class="info-label">所属部门及队室:</view>
- <view class="info-value">{{ portrait.deptPath || '-' }}</view>
- </view>
- <view class="info-item">
- <view class="info-label">学历:</view>
- <view class="info-value">{{ schoolingText }}</view>
- </view>
- <view class="info-item">
- <view class="info-label">出生日期:</view>
- <view class="info-value">{{ portrait.birthday || '-' }}</view>
- </view>
- <view class="info-item">
- <view class="info-label">专业:</view>
- <view class="info-value">{{ portrait.major || '-' }}</view>
- </view>
- <view class="info-item">
- <view class="info-label">技能等级:</view>
- <view class="info-value">{{ portrait.qualificationLevelText || '-' }}</view>
- </view>
- <view class="info-item">
- <view class="info-label">标签:</view>
- <view class="info-value" v-if="portrait.userTags">
- <text class="tag" v-for="tag in portrait.userTags.split(',')" :key="tag">{{ tag
- }}</text>
- </view>
- </view>
- </view>
- <view class="score-section">
- <div class="score-circle" ref="scoreCircle"></div>
- <view class="score-box">
- <!-- <view class="score-row">
- <text class="score-label">评分:</text>
- <text class="score-val">{{ portrait.totalScore || 0 }}</text>
- </view> -->
- <view class="score-row">
- <text class="score-label">附加分:</text>
- <text class="score-val">{{ tagScoreDisplay }}分</text>
- </view>
- </view>
- </view>
- </view>
- </SectionTitle>
- <SectionTitle v-if="activeTab === 'profile'" title="工作履历">
- <view class="work-history">
- <text v-if="portrait.entryDate">
- {{ formatWorkDate(portrait.entryDate) }}入职 | 司龄{{ portrait.companyYears != null ?
- portrait.companyYears : '-' }}年
- | 开机年限{{ portrait.xrayOperatorYears != null ? portrait.xrayOperatorYears : '-' }}年
- | 现任职{{ portrait.roleNames || '-' }}
- </text>
- <text v-else>暂无数据</text>
- </view>
- </SectionTitle>
- <SectionTitle v-if="activeTab === 'profile'" title="获奖记录">
- <view class="honor-list">
- <view class="honor-item" v-for="(item, index) in portrait.awards" :key="index">
- <view class="honor-name">
- <text class="honor-dot"
- :style="{ background: item.color || honorColors[index % honorColors.length] }"></text>
- {{ item.level2Name }} {{ item.level4Name }}
- </view>
- <view class="honor-score">{{ item.score || '-' }}分</view>
- </view>
- <view v-if="!portrait.awards || portrait.awards.length === 0" class="no-data">暂无获奖记录</view>
- </view>
- </SectionTitle>
- <SectionTitle v-if="activeTab === 'profile'" title="职业资格证书获取时间">
- <view class="cert-info">
- <view class="cert-item" v-for="level in displayQualificationLevels" :key="level.field">
- <text class="cert-name">{{ level.label }}:{{ level.time || '-' }}</text>
- </view>
- <view v-if="!displayQualificationLevels.length" class="no-data">暂无数据</view>
- </view>
- </SectionTitle>
- <!-- <SectionTitle v-if="activeTab === 'profile' && displayQualificationLevels.length > 0"
- title="职业资格证书获取时间">
- <view class="cert-info">
- <view class="cert-item" v-for="level in displayQualificationLevels" :key="level.field">
- <text class="cert-name">{{ level.label }}:{{ level.time || '-' }}</text>
- </view>
- </view>
- </SectionTitle> -->
- <SectionTitle v-if="activeTab === 'profile'" title="个人能力">
- <div class="chart-legend">
- <div class="legend-item legend-warning"><span></span>预警线(低于75分)</div>
- <!-- <div class="legend-item legend-normal"><span></span>正常线(75~90分)</div> -->
- <div class="legend-item legend-excellent"><span></span>优秀线(高于90分)</div>
- <div class="legend-item legend-current"><span></span>当前分值</div>
- </div>
- <div class="chart-container" ref="radarChart"></div>
- </SectionTitle>
- <SectionTitle v-if="activeTab === 'profile'" title="补充信息">
- <view class="supp-grid">
- <view class="supp-item">
- <text class="supp-label">政治面貌:</text>
- <text class="supp-value">{{ portrait.politicalStatusText || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">性别:</text>
- <text class="supp-value">{{ portrait.sexText || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">籍贯:</text>
- <text class="supp-value">{{ portrait.nativePlace || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">民族:</text>
- <text class="supp-value">{{ portrait.nation || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">年龄:</text>
- <text class="supp-value">{{ ageText }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">司龄:</text>
- <text class="supp-value">{{ portrait.companyYears != null ? portrait.companyYears + '年' :
- '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">性格特征:</text>
- <text class="supp-value">{{ portrait.characterCharacteristics || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">工作风格:</text>
- <text class="supp-value">{{ portrait.workingStyle || '-' }}</text>
- </view>
- <view class="supp-item">
- <text class="supp-label">业务岗位:</text>
- <text class="supp-value">{{ portrait.postNames || '-' }}</text>
- </view>
- </view>
- </SectionTitle>
- <!-- <SectionTitle v-if="activeTab === 'data'" title="预警信息">
- <view class="warning-content" @click="showWarning = true">
- <view class="warning-tip">点击查看详细预警信息</view>
- </view>
- </SectionTitle> -->
- </view>
- </view>
- <view class="warning-overlay" v-if="showWarning" @click="showWarning = false">
- <view class="warning-panel" @click.stop>
- <view class="warning-header">
- <text class="warning-title">预警中枢</text>
- <text class="warning-close" @click="showWarning = false">✕</text>
- </view>
- <view class="warning-body">
- <view class="warning-desc">员工综合评估(<75分红色预警 | ≥90分优秀)</view>
- <view class="warning-score">
- <view class="warning-score-item">
- <text class="warning-score-label">综合得分</text>
- <text class="warning-score-value" :class="scoreLevelClass">{{ portrait.totalScore || 0
- }}</text>
- </view>
- </view>
- <view class="warning-detail" v-if="scoreDetails.length">
- <view class="warning-detail-title">评分明细</view>
- <view class="warning-detail-item" v-for="(item, index) in scoreDetails" :key="index">
- <view class="detail-left">
- <text class="detail-dim">{{ item.dimensionName }}</text>
- <text class="detail-name">{{ item.level3Name }}</text>
- </view>
- <view class="detail-score"
- :class="{ 'add-text': item.totalScore > 0, 'deduct-text': item.totalScore < 0 }">
- {{ item.totalScore > 0 ? '+' : '' }}{{ item.totalScore }}
- </view>
- </view>
- </view>
- </view>
- </view>
- </view>
- <u-popup :show="showEmployeePicker" mode="bottom" :round="16" :mask-close-able="true"
- :safe-area-inset-bottom="true" @close="onEmployeePickerClose">
- <view class="employee-picker">
- <view class="picker-header">
- <text class="picker-title">选择员工</text>
- </view>
- <view class="search-box">
- <u-input v-model="employeeSearchKeyword" placeholder="搜索员工" @confirm="onEmployeeSearch"
- @input="onEmployeeSearch"></u-input>
- </view>
- <scroll-view v-if="!employeeSearchKeyword.trim()" scroll-y class="tree-list">
- <template v-for="(node, index) in deptTreeData">
- <employee-tree-node :key="node.id" :node="node" :expanded-ids="expandedDeptIds"
- :selected-id="selectedEmployeeId" @toggle="toggleDeptExpand" @select="onEmployeeSelect" />
- </template>
- </scroll-view>
- <scroll-view v-else scroll-y class="employee-list">
- <view class="employee-item" v-for="item in filteredEmployeeList" :key="item.userId"
- @click="onEmployeeSelect(item)">
- <text class="employee-item-name">{{ item.nickName }}</text>
- <u-icon v-if="item.userId === selectedEmployeeId" name="checkmark" color="#34D399"
- size="18"></u-icon>
- </view>
- <view v-if="filteredEmployeeList.length === 0 && !employeeLoading" class="empty-state">
- <text>暂无数据</text>
- </view>
- <view v-if="employeeLoading" class="loading-state">
- <u-loading-icon text="加载中" textSize="14"></u-loading-icon>
- </view>
- </scroll-view>
- </view>
- </u-popup>
- <!-- 雷达图tooltip -->
- <u-popup :show="showRadarTooltipPopup" mode="center" :round="12" :mask-close-able="true"
- @close="closeRadarPopup">
- <div class="radar-tooltip-popup">
- <div class="tooltip-card">
- <!-- 标题 -->
- <view class="tooltip-header">
- <text class="tooltip-title">{{ activeDimName }}</text>
- <u-icon name="close" color="#666666" size="20" @click="closeRadarPopup" />
- </view>
- <!-- 加分区域 -->
- <div class="tooltip-section">
- <div class="section-list">
- <template v-if="addGroupList.length">
- <div class="list-item" v-for="(item, i) in addGroupList" :key="i">
- <span>{{ item.name }}:</span>
- <span class="add-text">+{{ item.total }}分</span>
- </div>
- </template>
- <div v-else class="p-empty">暂无加分记录</div>
- </div>
- <div class="section-total add">
- <span>合计加分:</span>
- <span>{{ addTotal }}分</span>
- </div>
- </div>
- <!-- 扣分区域 -->
- <div class="tooltip-section">
- <div class="section-list">
- <template v-if="deductGroupList.length">
- <div class="list-item" v-for="(item, i) in deductGroupList" :key="i">
- <span>{{ item.name }}:</span>
- <span class="deduct-text">{{ item.total }}分</span>
- </div>
- </template>
- <div v-else class="p-empty">暂无扣分记录</div>
- </div>
- <div class="section-total deduct">
- <span>合计扣分:</span>
- <span>{{ deductTotal }}分</span>
- </div>
- </div>
- </div>
- </div>
- </u-popup>
- </view>
- </template>
- <script>
- import * as echarts from 'echarts'
- import { getEmployeePortrait, countTagScore, getQualificationLevelTime } from '@/api/portraitManagement/portraitManagement'
- import { listAllUser, getDeptUserTree } from '@/api/system/user'
- import SectionTitle from '@/components/SectionTitle.vue'
- import EmployeeTreeNode from '@/pages/components/EmployeeTreeNode.vue'
- const honorColors = ['#60A5FA', '#34D399', '#FBBF24', '#EF4444', '#A78BFA', '#F472B6', '#6EE7B7', '#FB923C']
- const schoolingMap = {
- '1': '小学',
- '2': '初中',
- '3': '高中',
- '4': '中专',
- '5': '大专',
- '6': '本科',
- '7': '硕士',
- '8': '博士'
- }
- function getRandomHexColor() {
- return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')
- }
- export default {
- name: 'EmployeeProfile',
- components: {
- SectionTitle,
- EmployeeTreeNode
- },
- data() {
- return {
- activeTab: 'profile',
- selectedTimeTag: 3,
- timeTags: ['近一周', '近一月', '近三月', '近一年', '自定义时间范围'],
- currentTime: '',
- timer: null,
- beginTime: '',
- endTime: '',
- searchKeyword: '',
- portrait: { dimensions: [], awards: [] },
- tagScoreData: null,
- qualificationData: null,
- scoreDetails: [],
- showWarning: false,
- radarChartInstance: null,
- scoreChartInstance: null,
- // 员工选择相关
- showEmployeePicker: false,
- selectedEmployeeId: null,
- selectedEmployeeName: '',
- deptTreeData: [],
- pickerTreeData: [],
- employeeList: [],
- employeeSearchKeyword: '',
- employeeLoading: false,
- expandedDeptIds: [],
- isSecurityCheck: false,
- userInfo: null,
- // 路由跳转参数
- routeUserId: null,
- routeUserName: '',
- // 雷达图tooltip相关
- activeDimName: null,
- radarTooltipPosition: { x: 0, y: 0 },
- // 控制雷达图 tooltip popup 显示
- showRadarTooltipPopup: false,
- // 评分明细数据
- allScoreDetails: [],
- qualificationLevelMap: [
- { label: '五级', field: 'levelFiveTime' },
- { label: '四级', field: 'levelFourTime' },
- { label: '三级', field: 'levelThreeTime' },
- { label: '二级', field: 'levelTwoTime' },
- { label: '一级', field: 'levelOneTime' },
- ]
- }
- },
- computed: {
-
- displayQualificationLevels() {
- if (!this.qualificationData) return []
- console.log(this.qualificationData)
- debugger
- return this.qualificationLevelMap.map(item => ({
- label: item.label,
- time: this.qualificationData[item.field] || 0
- })).filter(item => item.time)
- },
- schoolingText() {
- if (!this.portrait.schooling) return '-'
- return schoolingMap[this.portrait.schooling] || this.portrait.schooling
- },
- ageText() {
- if (!this.portrait.birthday) return '-'
- const birthDate = new Date(this.portrait.birthday)
- if (isNaN(birthDate.getTime())) return '-'
- const today = new Date()
- let age = today.getFullYear() - birthDate.getFullYear()
- const monthDiff = today.getMonth() - birthDate.getMonth()
- if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
- age--
- }
- return age + '岁'
- },
- tagScoreDisplay() {
- return this.portrait?.userTags?.split(',').length || 0
- },
- scoreLevelClass() {
- if ((this.portrait.totalScore || 0) < 75) return 'score-danger'
- if ((this.portrait.totalScore || 0) >= 90) return 'score-excellent'
- return ''
- },
- currentDate() {
- const now = new Date()
- const year = now.getFullYear()
- const month = String(now.getMonth() + 1).padStart(2, '0')
- const day = String(now.getDate()).padStart(2, '0')
- const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
- const weekDay = weekDays[now.getDay()]
- return `${year}年${month}月${day}日 ${weekDay}`
- },
- filteredEmployeeList() {
- const keyword = this.employeeSearchKeyword.trim().toLowerCase()
- if (!keyword) return this.employeeList
- return this.employeeList.filter(item =>
- (item.nickName || '').toLowerCase().includes(keyword)
- )
- },
- // 雷达图tooltip相关计算
- addList() {
- const all = (this.allScoreDetails || []).filter(d => d.totalScore != null && Number(d.totalScore) > 0)
- return this.activeDimName ? all.filter(d => d.dimensionName === this.activeDimName) : all
- },
- deductList() {
- const all = (this.allScoreDetails || []).filter(d => d.totalScore != null && Number(d.totalScore) < 0)
- return this.activeDimName ? all.filter(d => d.dimensionName === this.activeDimName) : all
- },
- addGroupList() {
- const groups = {}
- this.addList.forEach(d => {
- const key = d.level2Name || '其他'
- groups[key] = (groups[key] || 0) + Number(d.totalScore)
- })
- return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
- },
- deductGroupList() {
- const groups = {}
- this.deductList.forEach(d => {
- const key = d.level2Name || '其他'
- groups[key] = (groups[key] || 0) + Number(d.totalScore)
- })
- return Object.entries(groups).map(([name, total]) => ({ name, total: total.toFixed(2) }))
- },
- addTotal() {
- const s = this.addList.reduce((acc, d) => acc + Number(d.totalScore), 0)
- return (s > 0 ? '+' : '') + s.toFixed(2)
- },
- deductTotal() {
- const s = this.deductList.reduce((acc, d) => acc + Number(d.totalScore), 0)
- return s.toFixed(2)
- },
- },
- // uni-app 页面生命周期:接收路由参数
- onLoad(options) {
- if (options.userId) {
- this.routeUserId = options.userId
- this.routeUserName = decodeURIComponent(options.userName || '')
- }
- },
- mounted() {
- this.updateTime()
- this.timer = setInterval(() => {
- this.updateTime()
- }, 1000)
- // 默认计算时间范围(近一年)
- this.onTimeTagClick(this.selectedTimeTag)
- if (this.routeUserId) {
- // 来自组织架构跳转,直接加载指定人员
- this.selectedEmployeeId = this.routeUserId
- this.selectedEmployeeName = this.routeUserName
- this.searchKeyword = this.routeUserName
- this.employeeLoading = false
- this.$nextTick(() => {
- this.fetchEmployeePortrait()
- })
- } else {
- this.fetchEmployeeList()
- }
- },
- beforeDestroy() {
- if (this.timer) {
- clearInterval(this.timer)
- }
- if (this.radarChartInstance) {
- this.radarChartInstance.dispose()
- }
- if (this.scoreChartInstance) {
- this.scoreChartInstance.dispose()
- }
- },
- methods: {
- updateTime() {
- const now = new Date()
- const hours = String(now.getHours()).padStart(2, '0')
- const minutes = String(now.getMinutes()).padStart(2, '0')
- const seconds = String(now.getSeconds()).padStart(2, '0')
- this.currentTime = `${hours}:${minutes}:${seconds}`
- },
- // 员工相关方法
- // 扁平化部门树,提取人员
- flattenDeptTree(tree) {
- const result = []
- const traverse = (nodes) => {
- nodes.forEach(node => {
- const hasChildren = node.children && node.children.length > 0
- if (!hasChildren && node.nodeType !== 'dept') {
- result.push({
- nodeType: node.nodeType || '',
- userId: node.userId || node.id,
- nickName: node.nickName || node.label || node.userName || ''
- })
- }
- if (hasChildren) {
- traverse(node.children)
- }
- })
- }
- traverse(tree)
- return result
- },
- // 从树中查找第一个用户节点
- findFirstUser(nodes) {
- for (const node of nodes) {
- const hasChildren = node.children && node.children.length > 0
- if (!hasChildren && node.nodeType !== 'dept') {
- return node
- }
- if (hasChildren) {
- const found = this.findFirstUser(node.children)
- if (found) return found
- }
- }
- return null
- },
- // 展开所有部门节点
- expandAllDepts(nodes) {
- this.expandedDeptIds = []
- const traverse = (list) => {
- list.forEach(node => {
- const hasChildren = node.children && node.children.length > 0
- if (hasChildren || node.nodeType === 'dept') {
- this.expandedDeptIds.push(node.id)
- if (hasChildren) {
- traverse(node.children)
- }
- }
- })
- }
- traverse(nodes)
- },
- fetchEmployeeList() {
- this.employeeLoading = true
- const user = this.$store.state.user?.userInfo || this.$store.state.user?.user
- this.userInfo = user
- // 检查角色是否包含 SecurityCheck
- const roles = this.$store.state.user?.roles || []
- this.isSecurityCheck = roles.some(role => role.includes('SecurityCheck'))
- if (this.isSecurityCheck && user) {
- // SecurityCheck 角色,默认显示登录人,不可编辑
- this.selectedEmployeeId = user.userId || user.id
- this.selectedEmployeeName = user.nickName || user.userName || ''
- this.searchKeyword = this.selectedEmployeeName
- this.deptTreeData = []
- this.pickerTreeData = []
- this.employeeLoading = false
- // 自动加载画像数据
- this.fetchEmployeePortrait()
- } else {
- // 其他角色,使用 getDeptUserTree 接口
- if (user && user.deptId) {
- getDeptUserTree({ deptId: user.deptId }).then(res => {
- if (res.code === 200) {
- this.deptTreeData = res.data || []
- const allUsers = this.flattenDeptTree(this.deptTreeData)
- this.employeeList = allUsers.filter(user => user.nodeType === 'user')
- // 默认展开所有部门
- this.expandAllDepts(this.deptTreeData)
- // 默认选中第一个用户
- const firstUser = this.findFirstUser(this.deptTreeData)
- if (firstUser) {
- this.selectedEmployeeId = firstUser.userId || firstUser.id
- this.selectedEmployeeName = firstUser.nickName || firstUser.label || firstUser.userName || ''
- this.searchKeyword = this.selectedEmployeeName
- this.fetchEmployeePortrait()
- }
- }
- }).catch(() => {
- }).finally(() => {
- this.employeeLoading = false
- })
- } else {
- this.employeeLoading = false
- }
- }
- },
- onEmployeeSearch() {
- // 搜索由 computed 自动处理
- },
- toggleDeptExpand(id) {
- const index = this.expandedDeptIds.indexOf(id)
- if (index > -1) {
- this.expandedDeptIds.splice(index, 1)
- } else {
- this.expandedDeptIds.push(id)
- }
- },
- onEmployeeSelect(item) {
- this.selectedEmployeeId = item.userId
- this.selectedEmployeeName = item.nickName
- this.searchKeyword = item.nickName
- this.showEmployeePicker = false
- this.fetchEmployeePortrait()
- },
- onEmployeePickerClose() {
- this.showEmployeePicker = false
- },
- buildTimeParams() {
- const now = new Date()
- const today = this.formatDate(now)
- if (this.selectedTimeTag === 4) {
- if (this.startDate && this.endDate) {
- return { startDate: this.startDate, endDate: this.endDate }
- }
- return {}
- }
- let startDate
- switch (this.selectedTimeTag) {
- case 0:
- startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
- break
- case 1:
- startDate = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())
- break
- case 2:
- startDate = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate())
- break
- case 3:
- startDate = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
- break
- default:
- return {}
- }
- return { startDate: this.formatDate(startDate), endDate: today }
- },
- formatDate(date) {
- const y = date.getFullYear()
- const m = String(date.getMonth() + 1).padStart(2, '0')
- const d = String(date.getDate()).padStart(2, '0')
- return `${y}-${m}-${d}`
- },
- onTimeTagClick(index) {
- this.selectedTimeTag = index
- if (index === 4) {
- this.beginTime = ''
- this.endTime = ''
- return
- }
- // 自动计算时间范围
- const now = new Date()
- // 设置结束时间为今天
- const endTime = new Date(now.getFullYear(), now.getMonth(), now.getDate())
- // 设置开始时间
- let beginTime
- switch (index) {
- case 0: // 近一周
- beginTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
- break
- case 1: // 近一月
- beginTime = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())
- break
- case 2: // 近三月
- beginTime = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate())
- break
- case 3: // 近一年
- beginTime = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate())
- break
- default:
- beginTime = now
- }
- // 格式化日期为 YYYY-MM-DD
- this.beginTime = this.formatDate(beginTime)
- this.endTime = this.formatDate(endTime)
- // 选择时间标签后立即查询
- if (this.selectedEmployeeId) {
- this.fetchEmployeePortrait()
- }
- },
- onBeginTimeChange(e) {
- const dateStr = e.detail.value
- this.beginTime = dateStr
- // 自定义时间选择后立即查询
- if (this.selectedEmployeeId && this.endTime) {
- this.fetchEmployeePortrait()
- }
- },
- onEndTimeChange(e) {
- const dateStr = e.detail.value
- this.endTime = dateStr
- // 自定义时间选择后立即查询
- if (this.selectedEmployeeId && this.beginTime) {
- this.fetchEmployeePortrait()
- }
- },
- formatWorkDate(d) {
- if (!d) return '-'
- const str = String(d)
- const parts = str.substring(0, 10).split('-')
- if (parts.length < 3) return str
- return parts[0] + '.' + Number(parts[1]) + '.' + Number(parts[2])
- },
- onSearch() {
- if (!this.searchKeyword.trim()) return
- this.fetchEmployeePortrait()
- },
- fetchEmployeePortrait() {
- const params = {}
- if (this.searchKeyword.trim()) {
- params.personName = this.searchKeyword.trim()
- }
- // 添加时间参数
- if (this.beginTime) {
- params.beginTime = this.beginTime
- }
- if (this.endTime) {
- params.endTime = this.endTime
- }
- const portraitPromise = getEmployeePortrait(params).then(res => {
- if (res.code === 200 && res.data) {
- this.portrait = res.data || { dimensions: [], awards: [] }
- this.portrait.awards.forEach(item => {
- item.color = getRandomHexColor()
- })
- // 只取数组中最后一个元素的 scoreDetails
- const allScoreDetails = res.data.scoreDetails || []
- this.allScoreDetails = allScoreDetails
- this.scoreDetails = allScoreDetails.length > 0 ? allScoreDetails[allScoreDetails.length - 1] : []
- }
- }).catch(() => { })
- const tagPromise = countTagScore(params).then(res => {
- const data = res.data
- if (Array.isArray(data)) {
- const found = data.find(item => item.userId === this.selectedEmployeeId)
- this.tagScoreData = found ? (found.totalScore ?? found.score) : null
- } else {
- this.tagScoreData = data
- }
- }).catch(() => { })
- const qualificationPromise = getQualificationLevelTime({ userId: this.selectedEmployeeId }).then(res => {
- this.qualificationData = res.data || null
- }).catch(() => {
- this.qualificationData = null
- })
- Promise.all([portraitPromise, tagPromise, qualificationPromise]).finally(() => {
- this.$nextTick(() => {
- this.initRadarChart()
- this.initScoreChart()
- })
- })
- },
- initScoreChart() {
- if (!this.$refs.scoreCircle) return
- if (this.scoreChartInstance) {
- this.scoreChartInstance.dispose()
- }
- this.scoreChartInstance = echarts.init(this.$refs.scoreCircle)
- const score = this.portrait.totalScore || 0
- const option = {
- series: [
- {
- type: 'gauge',
- startAngle: 90,
- endAngle: -270,
- pointer: {
- show: false
- },
- progress: {
- show: true,
- overlap: false,
- roundCap: true,
- clip: false,
- itemStyle: {
- borderWidth: 1,
- borderColor: '#464646'
- }
- },
- axisLine: {
- lineStyle: {
- width: 5,
- color: [[1, '#34D399']]
- }
- },
- splitLine: {
- show: false,
- distance: 0,
- length: 10
- },
- axisTick: {
- show: false
- },
- axisLabel: {
- show: false,
- distance: 50
- },
- data: [
- {
- value: score,
- name: '综合得分'
- }
- ],
- title: {
- fontSize: 14
- },
- detail: {
- width: 50,
- height: 14,
- fontSize: 20,
- color: 'inherit',
- padding: [0, 0, 80, 0],
- borderColor: 'inherit',
- formatter: '{value}',
- rich: {
- value: {
- fontSize: 14,
- color: '#333',
- lineHeight: 35,
- }
- }
- }
- }
- ]
- }
- this.scoreChartInstance.setOption(option)
- },
- initRadarChart() {
- if (!this.$refs.radarChart) return
- if (this.radarChartInstance) {
- this.radarChartInstance.dispose()
- }
- this.radarChartInstance = echarts.init(this.$refs.radarChart)
- const dimensions = this.portrait.dimensions || []
- // 仿照 index copy.vue 的配置
- const radarData = {
- grounp: dimensions.map(d => ({ name: d.name + '\n\n' + d.score, max: 100 })),
- data: [{
- name: '个人能力',
- value: dimensions.map(attr => attr.score),
- symbolSize: 10,
- areaStyle: {
- show: false,
- opacity: 0
- },
- lineStyle: {
- color: '#4DC8FE',
- width: 1
- },
- itemStyle: { color: '#fff', borderWidth: 1, borderColor: '#00C8DA', borderJoin: 'round' }
- }]
- }
- const option = {
- radar: {
- indicator: radarData.grounp,
- center: ['50%', '52%'],
- radius: '50%',
- splitNumber: 8,
- axisLine: { lineStyle: { color: '#ccc' } },
- splitLine: { lineStyle: { color: ['#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#fe4322', '#8EC742', '#ccc'], width: 1 } },
- splitArea: { show: false },
- axisName: {
- color: '#333',
- fontSize: 12
- }
- },
- series: [
- {
- type: 'radar',
- data: radarData.data,
- symbol: 'circle',
- symbolSize: 13,
- label: {
- show: false,
- formatter: (p) => p.value,
- color: '#333',
- fontSize: 16,
- fontWeight: 'bold'
- }
- }
- ]
- }
- this.radarChartInstance.setOption(option)
- // 绑定鼠标事件
- this.bindRadarEvents()
- },
- bindRadarEvents() {
- if (!this.radarChartInstance || !this.$refs.radarChart) return
- this.radarChartInstance.off('click')
- this.radarChartInstance.on('click', (params) => {
- if (!Array.isArray(this.portrait.dimensions) || !this.portrait.dimensions.length) return
- const rect = this.$refs.radarChart.getBoundingClientRect()
- if (!rect) return
- const cx = rect.width / 2
- const cy = rect.height / 2
- const dx = params.event.offsetX - cx
- const dy = params.event.offsetY - cy
- const distance = Math.sqrt(dx * dx + dy * dy)
- const radarRadius = rect.width * 0.35 // 对应 radius: 65%
- if (distance > radarRadius) {
- return
- }
- // 角度计算
- let angle = Math.atan2(dx, -dy) * (180 / Math.PI)
- if (angle < 0) angle += 360
- const count = this.portrait.dimensions.length
- const step = 360 / count
- let idx = Math.abs(Math.round(angle / step) % count - count)
- if (idx < 0) idx += count
- if (idx >= count) idx = 0
- // 显示tooltip popup - 加上小延迟避免 u-popup 立即关闭的问题
- this.activeDimName = this.portrait.dimensions[idx].name
- setTimeout(() => {
- this.showRadarTooltipPopup = true
- }, 50)
- })
- },
- // 关闭雷达图 tooltip popup
- closeRadarPopup() {
- this.showRadarTooltipPopup = false
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .employee-profile-page {
- min-height: 100vh;
- background: #fff;
- padding-bottom: 40rpx;
- }
- .page-header {
- position: sticky;
- top: 0;
- z-index: 100;
- color: black;
- background: #fff;
- backdrop-filter: blur(10px);
- box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.2);
- }
- .header-title {
- padding: 24rpx 32rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- .title-main {
- font-size: 36rpx;
- font-weight: bold;
- color: black;
- }
- }
- .header-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
- .current-time {
- font-size: 24rpx;
- color: #999;
- }
- }
- .time-filter {
- padding: 16rpx 32rpx;
- background: #fff;
- }
- .time-scroll {
- width: 100%;
- }
- .time-tags {
- display: flex;
- gap: 16rpx;
- white-space: nowrap;
- }
- .time-tag {
- padding: 8rpx 10rpx;
- border-radius: 50rpx;
- background: #f0f0f0;
- font-size: 24rpx;
- color: #666;
- transition: all 0.3s;
- &.active {
- background: #60A5FA;
- color: #fff;
- font-weight: 500;
- }
- }
- .date-range-picker {
- display: flex;
- align-items: center;
- gap: 16rpx;
- margin-top: 16rpx;
- padding-top: 16rpx;
- border-top: 1rpx solid #e0e0e0;
- }
- .date-input {
- flex: 1;
- padding: 12rpx 24rpx;
- border-radius: 12rpx;
- background: #f5f5f5;
- font-size: 24rpx;
- color: #999;
- text-align: center;
- &.filled {
- color: #333;
- }
- }
- .date-separator {
- font-size: 24rpx;
- color: #999;
- flex-shrink: 0;
- }
- .search-selector {
- padding: 16rpx 32rpx;
- background: #fff;
- }
- .search-select-trigger {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 12rpx;
- padding: 16rpx 24rpx;
- background: #f5f5f5;
- border: 1rpx solid #e0e0e0;
- border-radius: 50rpx;
- }
- .search-select-trigger-disabled {
- opacity: 0.5;
- pointer-events: none;
- }
- .search-name-text {
- font-size: 26rpx;
- color: #333;
- }
- .employee-picker {
- background: #fff;
- display: flex;
- flex-direction: column;
- border-radius: 16rpx 16rpx 0 0;
- width: 100%;
- height: 70vh;
- }
- .picker-header {
- padding: 24rpx 32rpx;
- border-bottom: 1rpx solid #eee;
- display: flex;
- justify-content: center;
- flex-shrink: 0;
- }
- .picker-title {
- font-size: 32rpx;
- font-weight: 600;
- color: #333;
- }
- .search-box {
- padding: 16rpx 32rpx;
- flex-shrink: 0;
- }
- .tree-list {
- flex: 1;
- padding: 0;
- height: 0;
- overflow: hidden;
- }
- .employee-list {
- flex: 1;
- padding: 0 32rpx;
- height: 0;
- overflow: hidden;
- }
- .employee-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx 0;
- border-bottom: 1rpx solid #f0f0f0;
- }
- .employee-item-name {
- font-size: 28rpx;
- color: #333;
- }
- .tab-nav {
- padding: 16rpx 32rpx;
- display: flex;
- justify-content: center;
- gap: 64rpx;
- }
- .tab-item {
- font-size: 28rpx;
- color: #999;
- padding-bottom: 8rpx;
- border-bottom: 2rpx solid transparent;
- transition: all 0.3s;
- &.active {
- color: #333;
- border-bottom-color: #333;
- }
- }
- .page-content {
- padding: 32rpx;
- display: flex;
- flex-direction: column;
- }
- .empty-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 120rpx 0;
- .empty-icon {
- font-size: 80rpx;
- margin-bottom: 24rpx;
- }
- .empty-text {
- font-size: 28rpx;
- color: #999;
- }
- }
- .user-info {
- .user-header {
- display: flex;
- align-items: center;
- gap: 32rpx;
- margin-bottom: 32rpx;
- padding-bottom: 24rpx;
- border-bottom: 1rpx solid #e0e0e0;
- }
- .avatar-area {
- width: 120rpx;
- height: 120rpx;
- border-radius: 50%;
- background: #f0f0f0;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- .avatar {
- width: 100%;
- height: 100%;
- border-radius: 50%;
- object-fit: cover;
- }
- .avatar-placeholder {
- font-size: 48rpx;
- font-weight: bold;
- color: #999;
- }
- }
- .name-row {
- display: flex;
- align-items: baseline;
- .name-label {
- font-size: 26rpx;
- color: #999;
- }
- .name-value {
- font-size: 36rpx;
- font-weight: bold;
- color: #333;
- }
- }
- }
- .info-grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 20rpx;
- margin-bottom: 32rpx;
- .info-item {
- .info-label {
- font-size: 22rpx;
- color: #999;
- margin-bottom: 4rpx;
- }
- .info-value {
- font-size: 26rpx;
- color: #333;
- .tag {
- display: inline-block;
- padding: 4rpx 16rpx;
- margin-left: 2rpx;
- background: #f0f0f0;
- border-radius: 8rpx;
- color: #333;
- font-size: 22rpx;
- }
- }
- }
- }
- .score-section {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 48rpx;
- padding-top: 24rpx;
- border-top: 1rpx solid #e0e0e0;
- .score-circle {
- width: 280rpx;
- height: 280rpx;
- }
- .score-box {
- .score-row {
- display: flex;
- align-items: center;
- margin-bottom: 16rpx;
- .score-label {
- font-size: 26rpx;
- color: #999;
- }
- .score-val {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- }
- }
- }
- .work-history {
- font-size: 26rpx;
- color: #555;
- line-height: 1.8;
- }
- .honor-list {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
- .honor-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16rpx;
- background: #f5f5f5;
- border-radius: 12rpx;
- .honor-name {
- display: flex;
- align-items: center;
- gap: 12rpx;
- font-size: 26rpx;
- color: #333;
- flex: 1;
- .honor-dot {
- width: 12rpx;
- height: 12rpx;
- border-radius: 50%;
- flex-shrink: 0;
- }
- }
- .honor-score {
- font-size: 26rpx;
- color: #A78BFA;
- font-weight: 500;
- flex-shrink: 0;
- }
- }
- .no-data {
- font-size: 26rpx;
- color: #999;
- text-align: center;
- padding: 24rpx 0;
- }
- }
- .cert-info {
- .cert-item {
- padding: 16rpx 0;
- border-bottom: 1rpx solid #f0f0f0;
- &:last-child {
- border-bottom: none;
- }
- .cert-name {
- font-size: 26rpx;
- color: #333;
- }
- }
- .no-data {
- font-size: 26rpx;
- color: #999;
- text-align: center;
- padding: 24rpx 0;
- }
- }
- .cert-info {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
- }
- .cert-item {
- display: flex;
- align-items: center;
- padding: 12rpx 0;
- border-bottom: 1rpx solid #f0f0f0;
- &:last-child {
- border-bottom: none;
- }
- }
- .cert-name {
- font-size: 28rpx;
- color: #333;
- }
- .chart-container {
- width: 100%;
- height: 500rpx;
- }
- .chart-legend {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-wrap: wrap;
- gap: 10px 18px;
- padding-top: 8px;
- color: #fff;
- font-size: 14px;
- }
- .legend-item {
- display: flex;
- align-items: center;
- gap: 6rpx;
- span {
- width: 24rpx;
- height: 10rpx;
- border: 2rpx solid currentColor;
- border-radius: 2rpx;
- }
- }
- .legend-warning {
- color: #fe4322;
- span {
- background-color: #fe4322;
- }
- }
- .legend-normal {
- color: black;
- span {
- background-color: gray;
- }
- }
- .legend-excellent {
- color: #8EC742;
- span {
- background-color: #8EC742;
- }
- }
- .legend-current {
- color: #1890ff;
- span {
- background-color: #1890ff;
- }
- }
- .supp-grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 20rpx;
- .supp-item {
- .supp-label {
- font-size: 22rpx;
- color: #666;
- margin-bottom: 4rpx;
- display: block;
- }
- .supp-value {
- font-size: 26rpx;
- color: #333;
- }
- }
- }
- .warning-content {
- padding: 32rpx;
- text-align: center;
- cursor: pointer;
- .warning-tip {
- font-size: 26rpx;
- color: #666;
- }
- }
- .warning-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.7);
- z-index: 200;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 48rpx;
- }
- .warning-panel {
- width: 100%;
- max-height: 80vh;
- background: #fff;
- border-radius: 24rpx;
- border: 1rpx solid #e0e0e0;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
- .warning-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 32rpx;
- border-bottom: 1rpx solid #eee;
- .warning-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .warning-close {
- width: 48rpx;
- height: 48rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 32rpx;
- color: #999;
- border-radius: 50%;
- background: #f0f0f0;
- }
- }
- .warning-body {
- padding: 32rpx;
- overflow-y: auto;
- }
- .warning-desc {
- font-size: 24rpx;
- color: #666;
- margin-bottom: 32rpx;
- }
- .warning-score {
- margin-bottom: 32rpx;
- .warning-score-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 24rpx;
- background: #f5f5f5;
- border-radius: 16rpx;
- .warning-score-label {
- font-size: 28rpx;
- color: #333;
- }
- .warning-score-value {
- font-size: 40rpx;
- font-weight: bold;
- &.score-danger {
- color: #EF4444;
- }
- &.score-excellent {
- color: #34D399;
- }
- color: #60A5FA;
- }
- }
- }
- .warning-detail {
- .warning-detail-title {
- font-size: 28rpx;
- color: #333;
- margin-bottom: 20rpx;
- font-weight: 500;
- }
- .warning-detail-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16rpx 0;
- border-bottom: 1rpx solid #f0f0f0;
- .detail-left {
- display: flex;
- flex-direction: column;
- gap: 4rpx;
- .detail-dim {
- font-size: 22rpx;
- color: #999;
- }
- .detail-name {
- font-size: 26rpx;
- color: #333;
- }
- }
- .detail-score {
- font-size: 28rpx;
- font-weight: 500;
- &.add-text {
- color: #34D399;
- }
- &.deduct-text {
- color: #EF4444;
- }
- }
- }
- }
- .radar-tooltip-popup {
- padding: 20rpx;
- min-width: 300px;
- .p-empty {
- text-align: center;
- margin-top: 10rpx;
- font-size: 24rpx;
- color: #999;
- }
- .tooltip-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 0 10rpx 20rpx;
- border-bottom: 1rpx solid #eee;
- margin-bottom: 20rpx;
- .tooltip-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- }
- .tooltip-section {
- padding: 0 10rpx;
- margin-bottom: 16rpx;
- }
- .list-item {
- margin-top: 8rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 26rpx;
- color: #333;
- .deduct-text {
- color: #ff4d4f;
- }
- .add-text {
- color: #00b42a;
- }
- }
- .section-total {
- margin-top: 12rpx;
- padding-top: 12rpx;
- border-top: 1rpx solid #f0f0f0;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-weight: 500;
- font-size: 28rpx;
- &.deduct {
- color: #ff4d4f;
- }
- &.add {
- color: #00b42a;
- }
- }
- }
- </style>
|