| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841 |
- <template>
- <view class="container">
- <!-- ==================== Hero 头部 ==================== -->
- <view class="hero">
- <view class="hero-bg-circle hero-bg-circle-1"></view>
- <view class="hero-bg-circle hero-bg-circle-2"></view>
- <view class="hero-top">
- <text class="hero-name">{{ childName }}</text>
- <view class="hero-dim-row">
- <text class="hero-dim-badge">{{ dimensionLabel }}</text>
- <text class="hero-level-badge" v-if="level">等级 {{ level }}</text>
- <text class="hero-date">{{ dateText }}</text>
- </view>
- </view>
- <!-- 总分区 -->
- <view class="hero-score-wrap" v-if="totalScore !== null">
- <view class="hero-score-col">
- <text class="hero-score-num">{{ totalScore }}</text>
- <text class="hero-score-label">{{ percentile !== null ? '总得分' : '综合得分' }}</text>
- </view>
- <template v-if="percentile !== null">
- <view class="hero-score-divider"></view>
- <view class="hero-score-col">
- <text class="hero-score-num hero-score-num-pct">{{ percentile }}<text class="hero-score-pct-unit">%</text></text>
- <text class="hero-score-label">百分位</text>
- </view>
- </template>
- </view>
- <!-- 百分位进度条:位置标记 -->
- <view class="hero-pct-bar-wrap" v-if="percentile !== null">
- <view class="hero-pct-track">
- <view class="hero-pct-fill" :style="'width:' + percentile + '%'"></view>
- <view class="hero-pct-marker" :style="'left:' + percentile + '%'"></view>
- </view>
- <view class="hero-pct-foot">
- <text class="hero-pct-foot-text">超过 {{ percentile }}% 的同龄人</text>
- </view>
- </view>
- </view>
- <view class="body">
- <view class="loading-wrap" v-if="loading">
- <text class="loading-text">加载中...</text>
- </view>
- <template v-else-if="hasContent">
- <!-- ==================== 核心能力(认知/综合能力类,百分位制) ==================== -->
- <view class="card" v-if="abilityBlock">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">{{ abilityBlock.title }}</text>
- <text class="card-title-sub" v-if="!showAbilityRadar">百分位制,50 为同龄平均</text>
- </view>
- <!-- 认知能力:3 项及以上数值 → 雷达图 -->
- <view class="radar-wrap" v-if="showAbilityRadar">
- <RadarChart
- :dimensions="abilityRadarDims"
- :scores="abilityRadarScores"
- :population-scores="abilityRadarPopulation"
- fillColor="#6366F1"
- gridColor="#EEF0F6"
- labelColor="#1F2937"
- :width="600"
- :height="600"
- :child-name="childName" />
- </view>
- <view v-else>
- <view class="metric-row" v-for="(m, mi) in abilityMetrics" :key="mi">
- <view class="metric-head">
- <text class="metric-name">{{ m.name }}</text>
- <view class="metric-value-wrap">
- <text class="metric-warn-badge" v-if="m.warn">需关注</text>
- <text class="metric-value" :class="m.warn ? 'metric-value-warn' : ''">{{ m.value }}<text class="metric-unit">%</text></text>
- </view>
- </view>
- <view class="metric-track">
- <view class="metric-fill" :class="m.warn ? 'metric-fill-warn' : 'metric-fill-main'"
- :style="'width:' + m.pct + '%'"></view>
- <view class="metric-ref-line" style="left:50%"></view>
- </view>
- </view>
- </view>
- </view>
- <!-- ==================== 大五人格(1-5 分制,5 项 → 雷达图) ==================== -->
- <view class="card" v-if="bigFiveBlock">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">{{ bigFiveBlock.title }}</text>
- <text class="card-title-sub">1 ~ 5 分,虚线为同龄平均</text>
- </view>
- <view class="radar-wrap" v-if="showBigFiveRadar">
- <RadarChart
- :dimensions="bigFiveRadarDims"
- :scores="bigFiveRadarScores"
- :population-scores="bigFiveRadarPopulation"
- fillColor="#8B5CF6"
- gridColor="#F0ECFA"
- labelColor="#5B21B6"
- :width="600"
- :height="600"
- :child-name="childName" />
- </view>
- <view v-else>
- <view class="bf-row" v-for="(m, mi) in bigFiveMetrics" :key="mi">
- <view class="bf-head">
- <text class="bf-name">{{ m.name }}</text>
- <text class="bf-value">{{ m.value }}</text>
- </view>
- <view class="bf-track">
- <view class="bf-fill" :class="m.tone" :style="'width:' + m.pct + '%'"></view>
- <view class="bf-avg-line"></view>
- </view>
- <text class="bf-hint" v-if="m.hint">{{ m.hint }}</text>
- </view>
- </view>
- </view>
- <!-- ==================== 情绪状态(4 分测验,0-15 分制) ==================== -->
- <view class="card" v-if="emotionBlock">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">{{ emotionBlock.title }}</text>
- </view>
- <view class="emo-summary" v-if="emotionSummary.total || emotionSummary.avg">
- <text class="emo-summary-text">情绪不稳定总分 {{ emotionSummary.total || '-' }} 分,同龄平均 {{ emotionSummary.avg || '-' }} 分</text>
- </view>
- <view class="emo-row" v-for="(m, mi) in emotionMetrics" :key="mi">
- <view class="emo-chip">
- <text class="emo-name">{{ m.name }}</text>
- <text class="emo-value">{{ m.value }}</text>
- </view>
- <view class="emo-track">
- <view class="emo-fill" :style="'width:' + m.pct + '%'"></view>
- </view>
- </view>
- </view>
- <!-- ==================== 体质健康(身高/体重/BMI + 生活习惯) ==================== -->
- <view class="card" v-if="bodyBaseMetrics.length > 0 || bodyHabitMetrics.length > 0">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">体质健康</text>
- </view>
- <view class="info-grid" v-if="bodyBaseMetrics.length > 0">
- <view class="info-cell" v-for="(m, mi) in bodyBaseMetrics" :key="mi">
- <text class="info-value">{{ m.value }}<text class="info-unit" v-if="m.unit">{{ m.unit }}</text></text>
- <text class="info-name">{{ m.name }}</text>
- </view>
- </view>
- <view class="habit-row" v-for="(m, mi) in bodyHabitMetrics" :key="mi">
- <text class="habit-name">{{ m.name }}</text>
- <text class="habit-value">{{ m.value }}</text>
- </view>
- </view>
- <!-- ==================== 通用指标块(自我概念/自驱力等,量纲自适应) ==================== -->
- <view class="card" v-for="(blk, bi) in genericBlocks" :key="getBlockKey(bi)">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">{{ blk.title }}</text>
- </view>
- <view class="metric-row" v-for="(m, mi) in blk.metrics" :key="mi">
- <!-- 非数值指标(如依恋类型)只展示名称与取值,不画条 -->
- <view class="metric-plain-row" v-if="m.nonNumeric">
- <text class="metric-name">{{ m.name }}</text>
- <text class="metric-plain-value">{{ m.value }}</text>
- </view>
- <template v-else>
- <view class="metric-head">
- <text class="metric-name">{{ m.name }}</text>
- <view class="metric-value-wrap">
- <text class="metric-warn-badge" v-if="m.warn">需关注</text>
- <text class="metric-value" :class="m.warn ? 'metric-value-warn' : ''">{{ m.value }}</text>
- </view>
- </view>
- <view class="metric-track">
- <view class="metric-fill" :class="m.warn ? 'metric-fill-warn' : 'metric-fill-main'"
- :style="'width:' + m.pct + '%'"></view>
- </view>
- </template>
- </view>
- </view>
- <!-- ==================== 基本信息(身高体重等,过滤噪音) ==================== -->
- <view class="card" v-if="otherMetrics.length > 0">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">基本信息</text>
- </view>
- <view class="info-grid">
- <view class="info-cell" v-for="(m, mi) in otherMetrics" :key="mi">
- <text class="info-value">{{ m.value }}<text class="info-unit" v-if="m.unit">{{ m.unit }}</text></text>
- <text class="info-name">{{ m.name }}</text>
- </view>
- </view>
- </view>
- <!-- ==================== 成长建议(长文本折叠) ==================== -->
- <view class="card" v-if="adviceText">
- <view class="card-title-row">
- <view class="card-title-bar card-title-bar-gold"></view>
- <text class="card-title">{{ adviceTitle }}</text>
- </view>
- <text class="advice-content" :class="!adviceExpanded ? 'advice-clamped' : ''">{{ adviceText }}</text>
- <view class="advice-toggle" @click="adviceExpanded = !adviceExpanded" v-if="adviceText.length > 160">
- <text class="advice-toggle-text">{{ adviceExpanded ? '收起 ▲' : '展开全部 ▼' }}</text>
- </view>
- </view>
- <!-- ==================== 报告评语 ==================== -->
- <view class="card" v-if="summaryText">
- <view class="card-title-row">
- <view class="card-title-bar"></view>
- <text class="card-title">报告评语</text>
- </view>
- <text class="advice-content">{{ summaryText }}</text>
- </view>
- <view class="foot-note">
- <text class="foot-note-text">本报告由 DAN 测评系统生成,结果仅供成长参考</text>
- </view>
- </template>
- <view class="empty-wrap" v-else>
- <text class="empty-text">暂无数据</text>
- </view>
- </view>
- <view class="bottom-spacer"></view>
- </view>
- </template>
- <script>
- import { getDanReportDetail } from '../../utils/api.js'
- import RadarChart from '../../components/RadarChart.vue'
- // 噪音指标过滤(报告头/OCR 带出的非测评数据,对历史数据同样生效)
- var NOISE_NAME_KEYS = ['联系电话', '报告编码', '测试时间', '档案', '具体情况', '页', '公众号',
- '出生', '年份', '编码', '编号', '手机', '身份证', '总均分', '邮箱']
- // 百分位制 category(0-100)
- var PERCENT_CATEGORIES = ['overall', 'cognitive']
- export default {
- components: { RadarChart },
- data() {
- return {
- reportId: null,
- loading: true,
- childName: '',
- dateText: '',
- dimension: '',
- reportType: '',
- level: '',
- totalScore: null,
- percentile: null,
- blocks: [],
- catMap: {},
- adviceExpanded: false,
- adviceSourceTitle: '',
- adviceSourceText: '',
- summarySourceText: ''
- }
- },
- computed: {
- hasContent() {
- return this.blocks.length > 0
- },
- dimensionLabel() {
- var map = {
- mind: '心维度',
- wisdom: '智维度',
- cognition: '认知维度',
- learning: '学习能力',
- behavior: '行为教养',
- career: '职业发展',
- campus: '校园标准',
- adolescent: '青春期挑战'
- }
- var base = map[this.dimension] || ''
- if (this.reportType) base += ' · ' + this.reportType
- return base || 'DAN 测评'
- },
- // 核心能力块(优先"认知能力",其次含"能力"的块)
- abilityBlock() {
- var fallback = null
- for (var i = 0; i < this.blocks.length; i++) {
- var b = this.blocks[i]
- if (b.type !== 'indicator' || !b.items || !b.items.length) continue
- var t = String(b.title || '')
- if (t.indexOf('认知') >= 0) return b
- if (t.indexOf('能力') >= 0 && !fallback) fallback = b
- }
- return fallback
- },
- abilityMetrics() {
- if (!this.abilityBlock) return []
- var self = this
- return (this.abilityBlock.items || []).filter(function(it) {
- return !self.isNoise(it.name)
- }).map(function(it) {
- var v = parseFloat(it.value)
- var pct = isNaN(v) ? 0 : self.clampPct(v)
- return {
- name: String(it.name).replace('百分位', ''),
- value: it.value,
- pct: pct,
- warn: !isNaN(v) && v < 50
- }
- })
- },
- // 认知能力雷达图模式:标题含「认知」且数值项 >= 3
- showAbilityRadar() {
- var b = this.abilityBlock
- if (!b) return false
- var t = String(b.title || '')
- return t.indexOf('认知') >= 0 && this.abilityMetrics.length >= 3
- },
- abilityRadarDims() {
- return this.abilityMetrics.map(function(m) {
- return { label: m.name + ' ' + m.value + '%', key: m.name }
- })
- },
- abilityRadarScores() {
- return this.abilityMetrics.map(function(m) {
- var v = parseFloat(m.value)
- return isNaN(v) ? 0 : Math.min(100, Math.max(0, Math.round(v)))
- })
- },
- // 同龄平均位置:50 百分位
- abilityRadarPopulation() {
- var arr = []
- for (var i = 0; i < this.abilityMetrics.length; i++) { arr.push(50) }
- return arr
- },
- // 大五人格块
- bigFiveBlock() {
- for (var i = 0; i < this.blocks.length; i++) {
- var b = this.blocks[i]
- if (b.type === 'indicator' && b.title && String(b.title).indexOf('人格') >= 0) {
- return b
- }
- }
- return null
- },
- bigFiveMetrics() {
- if (!this.bigFiveBlock) return []
- return (this.bigFiveBlock.items || []).map(function(it) {
- var v = parseFloat(it.value)
- var pct = isNaN(v) ? 0 : Math.min(100, Math.max(0, v / 5 * 100))
- var tone = 'bf-neutral'
- var hint = ''
- if (!isNaN(v)) {
- if (v >= 4) { tone = 'bf-high'; hint = '突出优势' }
- else if (v <= 2.5) { tone = 'bf-low'; hint = '低于同龄平均' }
- }
- return { name: it.name, value: it.value, pct: Math.round(pct), tone: tone, hint: hint }
- })
- },
- // 大五人格雷达图模式:5 个维度齐全时用雷达图(1-5 分制换算成 0-100)
- showBigFiveRadar() {
- return this.bigFiveMetrics.length >= 3
- },
- bigFiveRadarDims() {
- return this.bigFiveMetrics.map(function(m) {
- return { label: m.name + ' ' + m.value, key: m.name }
- })
- },
- bigFiveRadarScores() {
- return this.bigFiveMetrics.map(function(m) {
- var v = parseFloat(m.value)
- return isNaN(v) ? 0 : Math.round(v / 5 * 100)
- })
- },
- // 同龄平均位置:3 分(1-5 分制)
- bigFiveRadarPopulation() {
- var arr = []
- for (var i = 0; i < this.bigFiveMetrics.length; i++) { arr.push(60) }
- return arr
- },
- // 情绪状态块
- emotionBlock() {
- for (var i = 0; i < this.blocks.length; i++) {
- var b = this.blocks[i]
- if (b.type === 'indicator' && b.title && String(b.title).indexOf('情绪') >= 0) {
- return b
- }
- }
- return null
- },
- emotionMetrics() {
- if (!this.emotionBlock) return []
- var self = this
- return (this.emotionBlock.items || []).filter(function(it) {
- if (self.isNoise(it.name)) return false
- var n = String(it.name)
- return n.indexOf('总得分') < 0 && n.indexOf('平均分') < 0
- }).map(function(it) {
- var v = parseFloat(it.value)
- // 分测验为 0-15 分制(PDF 标尺 1/10/15)
- var pct = isNaN(v) ? 0 : Math.min(100, Math.max(0, v / 15 * 100))
- return { name: it.name, value: it.value, pct: Math.round(pct) }
- })
- },
- // 情绪稳定性总分 / 同龄平均分(单独成行展示,不与分测验混在同一量纲)
- emotionSummary() {
- var out = { total: '', avg: '' }
- if (!this.emotionBlock) return out
- var items = this.emotionBlock.items || []
- for (var i = 0; i < items.length; i++) {
- var n = String(items[i].name)
- if (n.indexOf('总得分') >= 0) out.total = items[i].value
- else if (n.indexOf('平均分') >= 0) out.avg = items[i].value
- }
- return out
- },
- // 体质健康块(身高/体重/BMI + 饮食/睡眠/运动习惯)
- bodyBlock() {
- for (var i = 0; i < this.blocks.length; i++) {
- var b = this.blocks[i]
- if (b.type !== 'indicator' || !b.items || !b.items.length) continue
- var t = String(b.title || '')
- if (t.indexOf('体质') >= 0 || t.indexOf('健康') >= 0 || t.indexOf('身体') >= 0) {
- return b
- }
- }
- return null
- },
- bodyBlockItems() {
- var self = this
- if (!this.bodyBlock) return []
- return (this.bodyBlock.items || []).filter(function(it) {
- return !self.isNoise(it.name)
- })
- },
- // 体质卡片上部:数值型指标(身高/体重/BMI)
- bodyBaseMetrics() {
- return this.bodyBlockItems.filter(function(it) {
- var n = String(it.name)
- return n.indexOf('身高') >= 0 || n.indexOf('体重') >= 0 || n.indexOf('BMI') >= 0
- }).map(function(it) {
- var name = String(it.name)
- var unit = ''
- if (name.indexOf('身高') >= 0) unit = 'cm'
- else if (name.indexOf('体重') >= 0) unit = 'kg'
- return { name: name, value: it.value, unit: unit }
- })
- },
- // 体质卡片下部:等级与生活习惯(文本/量纲不一,用列表行展示)
- bodyHabitMetrics() {
- return this.bodyBlockItems.filter(function(it) {
- var n = String(it.name)
- return n.indexOf('身高') < 0 && n.indexOf('体重') < 0 && n.indexOf('BMI') < 0
- })
- },
- // 其他指标("其他指标"块,过滤噪音,提取身高体重等)
- otherMetrics() {
- for (var i = 0; i < this.blocks.length; i++) {
- var b = this.blocks[i]
- if (b.type === 'indicator' && b.title && String(b.title).indexOf('其他') >= 0) {
- var self = this
- return (b.items || []).filter(function(it) {
- return !self.isNoise(it.name)
- }).map(function(it) {
- var unit = ''
- var name = String(it.name)
- if (name.indexOf('身高') >= 0) { unit = 'cm' }
- if (name.indexOf('体重') >= 0) { unit = 'kg' }
- return { name: name, value: it.value, unit: unit }
- })
- }
- }
- return []
- },
- // 通用指标块(自我概念/自驱力/社会关系等未专项渲染的 indicator)
- genericBlocks() {
- var taken = []
- if (this.abilityBlock) taken.push(this.abilityBlock)
- if (this.bigFiveBlock) taken.push(this.bigFiveBlock)
- if (this.emotionBlock) taken.push(this.emotionBlock)
- if (this.bodyBlock) taken.push(this.bodyBlock)
- var self = this
- return this.blocks.filter(function(b) {
- if (b.type !== 'indicator') return false
- if (taken.indexOf(b) >= 0) return false
- if (b.title && String(b.title).indexOf('其他') >= 0) return false
- return b.items && b.items.length > 0
- }).map(function(b) {
- var title = String(b.title || '')
- return {
- title: b.title,
- metrics: (b.items || []).filter(function(it) {
- return !self.isNoise(it.name)
- }).map(function(it) {
- return self.toMetric(it, title)
- })
- }
- }).filter(function(b) { return b.metrics.length > 0 })
- },
- adviceTitle() {
- return this.adviceSourceTitle || '成长建议'
- },
- adviceText() {
- return this.adviceSourceText || ''
- },
- summaryText() {
- return this.summarySourceText || ''
- }
- },
- onLoad(options) {
- this.reportId = options.reportId ? parseInt(options.reportId) : null
- if (this.reportId) this.loadData()
- },
- methods: {
- getBlockKey(bi) {
- return 'g' + bi
- },
- isNoise(name) {
- if (!name) return true
- for (var i = 0; i < NOISE_NAME_KEYS.length; i++) {
- if (String(name).indexOf(NOISE_NAME_KEYS[i]) >= 0) return true
- }
- return false
- },
- clampPct(v) {
- return Math.round(Math.min(100, Math.max(0, v)))
- },
- // 依据 category / 名称推断量纲上限
- resolveMax(name, category) {
- if (name && String(name).indexOf('百分位') >= 0) return 100
- if (category === 'bigFive') return 5
- if (category === 'social') return 100
- if (PERCENT_CATEGORIES.indexOf(category) >= 0) return 100
- return 10
- },
- // blockTitle:调用方(通用指标块)可显式指定量纲,如社会关系传 100
- toMetric(it, blockTitle) {
- var v = parseFloat(it.value)
- if (isNaN(v)) {
- // 非数值(如依恋类型「安全型/安全型/安全型」)不画条,仅展示取值
- return { name: it.name, value: it.value, pct: 0, warn: false, nonNumeric: true }
- }
- var cat = this.catMap[it.name] || ''
- var max = null
- if (blockTitle) {
- var t = String(blockTitle)
- if (t.indexOf('社会') >= 0 || t.indexOf('关系') >= 0) max = 100
- }
- if (max === null) max = this.resolveMax(it.name, cat)
- var pct = Math.min(100, Math.max(0, v / max * 100))
- // 仅百分位制指标(A2 认知等)低于 50 才告警;人格/自我概念等非能力项不告警
- var warn = (max === 100) && v < 50
- return { name: it.name, value: it.value, pct: Math.round(pct), warn: warn, nonNumeric: false }
- },
- loadData() {
- var self = this
- this.loading = true
- getDanReportDetail(this.reportId).then(function(res) {
- self.loading = false
- if (res.code !== 200 || !res.data) {
- uni.showToast({ title: res.message || '加载失败', icon: 'none' })
- return
- }
- var data = res.data
- var result = data.result || {}
- self.childName = result.childName || data.childName || ''
- var dateRaw = result.assessmentDate || data.confirmedAt || ''
- self.dateText = dateRaw ? String(dateRaw).substring(0, 10) : ''
- self.dimension = data.dimension || ''
- self.reportType = data.reportType || ''
- self.level = result.danLevel || ''
- self.blocks = (data.blocks || []).filter(function(b) {
- return b && ((b.items && b.items.length) || b.content)
- })
- // 从 structuredAnalysis 构建 名称→category 映射(blocks item 已丢 category)
- var catMap = {}
- if (result.structuredAnalysis) {
- try {
- var arr = JSON.parse(result.structuredAnalysis)
- for (var ai = 0; ai < arr.length; ai++) {
- if (arr[ai] && arr[ai].name) catMap[arr[ai].name] = arr[ai].category || ''
- }
- } catch (e) {}
- }
- self.catMap = catMap
- // 提取总分/百分位(score 块)
- for (var i = 0; i < self.blocks.length; i++) {
- var b = self.blocks[i]
- if (b.type === 'score' && b.items) {
- for (var j = 0; j < b.items.length; j++) {
- var it = b.items[j]
- var label = String(it.label)
- if (label.indexOf('总') >= 0) {
- var tv = parseFloat(it.value)
- if (!isNaN(tv)) self.totalScore = tv
- } else if (label.indexOf('百分位') >= 0) {
- var pv = parseFloat(it.value)
- if (!isNaN(pv)) self.percentile = Math.min(100, Math.max(0, pv))
- }
- }
- }
- }
- // 兜底:无 score 块(如 B4 智维度)时,用 result.overallScore 作综合得分
- if (self.totalScore === null && result.overallScore !== null && result.overallScore !== undefined) {
- var ov = parseFloat(result.overallScore)
- if (!isNaN(ov)) self.totalScore = ov
- }
- // text 块:成长建议优先(非空 content),评语单独
- for (var k = 0; k < self.blocks.length; k++) {
- var tb = self.blocks[k]
- if (tb.type === 'text' && tb.content) {
- if (String(tb.title).indexOf('建议') >= 0) {
- self.adviceSourceTitle = tb.title
- self.adviceSourceText = tb.content
- } else {
- self.summarySourceText = tb.content
- }
- }
- }
- // 兜底:无 text 建议块时用 result.growthSuggestions
- if (!self.adviceSourceText && result.growthSuggestions) {
- self.adviceSourceText = result.growthSuggestions
- }
- }).catch(function(e) {
- self.loading = false
- uni.showToast({ title: '加载异常', icon: 'none' })
- })
- }
- }
- }
- </script>
- <style scoped>
- .container { min-height: 100vh; background: #F3F2FA; padding-bottom: 40rpx; }
- /* ==================== Hero ==================== */
- .hero {
- position: relative;
- background: linear-gradient(160deg, #4F46E5 0%, #7C3AED 55%, #8B5CF6 100%);
- padding: 48rpx 36rpx 44rpx;
- overflow: hidden;
- border-bottom-left-radius: 36rpx;
- border-bottom-right-radius: 36rpx;
- }
- .hero-bg-circle { position: absolute; border-radius: 50%; background: rgba(255,255,255,0.06); }
- .hero-bg-circle-1 { width: 380rpx; height: 380rpx; right: -120rpx; top: -140rpx; }
- .hero-bg-circle-2 { width: 220rpx; height: 220rpx; left: -80rpx; bottom: -60rpx; }
- .hero-top { position: relative; z-index: 1; }
- .hero-name { font-size: 44rpx; font-weight: 700; color: #FFFFFF; display: block; }
- .hero-dim-row { display: flex; align-items: center; margin-top: 14rpx; flex-wrap: wrap; }
- .hero-dim-badge {
- font-size: 22rpx; color: #FFFFFF;
- background: rgba(255,255,255,0.22);
- border-radius: 24rpx; padding: 6rpx 18rpx;
- margin-right: 14rpx;
- }
- .hero-level-badge {
- font-size: 22rpx; color: #7C2D12; font-weight: 600;
- background: linear-gradient(135deg, #FDE68A, #FBBF24);
- border-radius: 24rpx; padding: 6rpx 18rpx;
- margin-right: 14rpx;
- }
- .hero-date { font-size: 24rpx; color: rgba(255,255,255,0.75); }
- .hero-score-wrap {
- position: relative; z-index: 1;
- display: flex; align-items: center; justify-content: center;
- margin-top: 36rpx;
- }
- .hero-score-col { flex: 1; display: flex; flex-direction: column; align-items: center; }
- .hero-score-divider { width: 1rpx; height: 72rpx; background: rgba(255,255,255,0.25); }
- .hero-score-num { font-size: 88rpx; font-weight: 700; color: #FFFFFF; line-height: 1; }
- .hero-score-num-pct { font-size: 72rpx; }
- .hero-score-pct-unit { font-size: 32rpx; font-weight: 400; margin-left: 2rpx; }
- .hero-score-label { font-size: 24rpx; color: rgba(255,255,255,0.75); margin-top: 12rpx; }
- .hero-pct-bar-wrap { position: relative; z-index: 1; margin-top: 32rpx; }
- .hero-pct-track {
- position: relative; height: 14rpx; border-radius: 8rpx;
- background: rgba(255,255,255,0.18);
- }
- .hero-pct-fill {
- height: 100%; border-radius: 8rpx;
- background: linear-gradient(90deg, #A5F3FC, #67E8F9, #FDE68A);
- }
- .hero-pct-marker {
- position: absolute; top: -6rpx; width: 4rpx; height: 26rpx;
- background: #FFFFFF; border-radius: 2rpx; transform: translateX(-2rpx);
- }
- .hero-pct-foot { margin-top: 12rpx; text-align: center; }
- .hero-pct-foot-text { font-size: 22rpx; color: rgba(255,255,255,0.85); }
- /* ==================== 通用卡片 ==================== */
- .body { padding: 28rpx 24rpx 0; }
- .card {
- background: #FFFFFF;
- border-radius: 20rpx;
- padding: 28rpx 26rpx;
- margin-bottom: 22rpx;
- box-shadow: 0 4rpx 16rpx rgba(99,102,241,0.06);
- }
- .card-title-row { display: flex; align-items: baseline; margin-bottom: 24rpx; }
- .card-title-bar {
- width: 8rpx; height: 30rpx; border-radius: 4rpx;
- background: linear-gradient(180deg, #6366F1, #8B5CF6);
- margin-right: 12rpx; align-self: center;
- }
- .card-title-bar-gold { background: linear-gradient(180deg, #F59E0B, #F97316); }
- .card-title { font-size: 30rpx; font-weight: 600; color: #1F2937; }
- .card-title-sub { font-size: 22rpx; color: #9CA3AF; margin-left: 14rpx; }
- /* ==================== 指标条 ==================== */
- .metric-row { margin-bottom: 26rpx; }
- .metric-row:last-child { margin-bottom: 4rpx; }
- .metric-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
- .metric-name { font-size: 26rpx; color: #374151; font-weight: 500; }
- .metric-value-wrap { display: flex; align-items: center; }
- .metric-value { font-size: 30rpx; font-weight: 700; color: #4F46E5; }
- .metric-value-warn { color: #EA580C; }
- .metric-unit { font-size: 20rpx; font-weight: 400; }
- .metric-warn-badge {
- font-size: 20rpx; color: #EA580C;
- background: #FFF7ED; border: 1rpx solid #FDBA74;
- border-radius: 16rpx; padding: 2rpx 12rpx; margin-right: 10rpx;
- }
- .metric-track {
- position: relative; height: 16rpx; border-radius: 8rpx; background: #EEF0F6;
- }
- .metric-fill { height: 100%; border-radius: 8rpx; }
- .metric-fill-main { background: linear-gradient(90deg, #818CF8, #6366F1); }
- .metric-fill-warn { background: linear-gradient(90deg, #FDBA74, #EA580C); }
- .metric-ref-line {
- position: absolute; top: -4rpx; width: 2rpx; height: 24rpx;
- background: #C7C9D9; border-radius: 1rpx;
- }
- /* 非数值指标行(名称 + 取值,无进度条) */
- .metric-plain-row {
- display: flex; justify-content: space-between; align-items: center;
- padding: 10rpx 0;
- }
- .metric-plain-value { font-size: 26rpx; color: #4F46E5; font-weight: 600; }
- /* ==================== 大五人格 ==================== */
- .bf-row { margin-bottom: 24rpx; }
- .bf-row:last-child { margin-bottom: 4rpx; }
- .bf-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10rpx; }
- .bf-name { font-size: 26rpx; color: #374151; font-weight: 500; }
- .bf-value { font-size: 30rpx; font-weight: 700; color: #7C3AED; }
- .bf-track { position: relative; height: 14rpx; border-radius: 7rpx; background: #F0ECFA; }
- .bf-fill { height: 100%; border-radius: 7rpx; }
- .bf-neutral { background: linear-gradient(90deg, #C4B5FD, #8B5CF6); }
- .bf-high { background: linear-gradient(90deg, #6EE7B7, #10B981); }
- .bf-low { background: linear-gradient(90deg, #FCD34D, #F59E0B); }
- .bf-avg-line {
- position: absolute; top: -5rpx; left: 60%; width: 2rpx; height: 24rpx;
- background: #9CA3AF; border-radius: 1rpx;
- }
- .bf-hint { display: block; font-size: 20rpx; color: #9CA3AF; margin-top: 6rpx; }
- /* ==================== 情绪状态 ==================== */
- .emo-row { display: flex; align-items: center; margin-bottom: 20rpx; }
- .emo-row:last-child { margin-bottom: 4rpx; }
- .emo-chip {
- display: flex; align-items: baseline;
- background: #F5F3FF; border-radius: 14rpx;
- padding: 10rpx 18rpx; margin-right: 18rpx; flex-shrink: 0;
- }
- .emo-name { font-size: 24rpx; color: #6D28D9; margin-right: 12rpx; }
- .emo-value { font-size: 28rpx; font-weight: 700; color: #7C3AED; }
- .emo-track { flex: 1; height: 12rpx; border-radius: 6rpx; background: #F0ECFA; overflow: hidden; }
- .emo-fill { height: 100%; border-radius: 6rpx; background: linear-gradient(90deg, #A78BFA, #7C3AED); }
- /* ==================== 雷达图容器 ==================== */
- .radar-wrap {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-bottom: 8rpx;
- }
- /* ==================== 情绪总分行 ==================== */
- .emo-summary {
- background: #F5F3FF;
- border-radius: 14rpx;
- padding: 16rpx 20rpx;
- margin-bottom: 20rpx;
- }
- .emo-summary-text { font-size: 24rpx; color: #6D28D9; }
- /* ==================== 体质健康 ==================== */
- .habit-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 14rpx 0;
- border-bottom: 1rpx solid #F1F2F7;
- }
- .habit-row:last-child { border-bottom: none; }
- .habit-name { font-size: 26rpx; color: #374151; }
- .habit-value { font-size: 26rpx; color: #4F46E5; font-weight: 600; }
- /* ==================== 基本信息 ==================== */
- .info-grid { display: flex; flex-wrap: wrap; }
- .info-cell {
- width: 33.33%; display: flex; flex-direction: column; align-items: center;
- margin-bottom: 16rpx;
- }
- .info-value { font-size: 36rpx; font-weight: 700; color: #1F2937; }
- .info-unit { font-size: 20rpx; font-weight: 400; color: #9CA3AF; margin-left: 4rpx; }
- .info-name { font-size: 22rpx; color: #9CA3AF; margin-top: 6rpx; }
- /* ==================== 成长建议/评语 ==================== */
- .advice-content {
- font-size: 26rpx; color: #4B5563; line-height: 1.8;
- white-space: pre-wrap; word-break: break-all;
- }
- .advice-clamped {
- max-height: 320rpx; overflow: hidden;
- -webkit-mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
- mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
- }
- .advice-toggle { text-align: center; margin-top: 16rpx; }
- .advice-toggle-text { font-size: 24rpx; color: #6366F1; font-weight: 500; }
- .foot-note { text-align: center; padding: 18rpx 0 8rpx; }
- .foot-note-text { font-size: 20rpx; color: #B0B3C3; }
- /* ==================== 其他 ==================== */
- .loading-wrap { display: flex; justify-content: center; padding: 140rpx 0; }
- .loading-text { font-size: 28rpx; color: #999; }
- .empty-wrap { display: flex; justify-content: center; padding: 140rpx 0; }
- .empty-text { font-size: 28rpx; color: #999; }
- .bottom-spacer { height: 60rpx; }
- </style>
|