index.vue 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. <template>
  2. <view class="wisdom-container">
  3. <!-- 通用登录浮层 -->
  4. <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
  5. <!-- Tabbar 切换过渡页 -->
  6. <tab-transition v-if="showTabTransition" dimCode="wisdom" ref="tabTransition" @close="showTabTransition = false" />
  7. <!-- 品牌头部 -->
  8. <PageBanner theme="wisdom" tagline="认知赋能 · 提升学习效率" quote="认知赋能 · 智慧人生" />
  9. <!-- 品牌定位语(登录前显示) -->
  10. <view class="brand-strap" v-if="!isLoggedIn">
  11. <text class="brand-strap-text">提升全家认知能力与学习效率</text>
  12. </view>
  13. <!-- 2. 学习能力评估图(始终显示,无数据时仅显示人群平均值) -->
  14. <view class="section">
  15. <view class="section-header">
  16. <text class="section-title">学习能力评估</text>
  17. <text class="section-sub">通过测评了解全家认知能力,找出可以提升的方向</text>
  18. <text class="avg-badge" v-if="!cognitiveReport">同龄平均值 · 完成认知测评后显示个人数据</text>
  19. <text class="section-more" v-if="isLoggedIn" @click="goCognitiveReport">查看详情 ›</text>
  20. </view>
  21. <RadarChart
  22. :dimensions="cognitiveDimensions"
  23. :scores="cognitiveScores"
  24. :avg-scores="cognitiveFamilyScores"
  25. :population-scores="cognitivePopulationScores"
  26. :childName="activeChildName"
  27. fillColor="#6366F1"
  28. gridColor="#E0E7FF"
  29. labelColor="#3730A3" />
  30. </view>
  31. <!-- 家庭成员选择条 -->
  32. <FamilyMemberStrip
  33. v-if="currentView === 'self' && isLoggedIn"
  34. :members="familyMembersVisible"
  35. :selectedMemberId="selectedMemberId"
  36. @select="onFamilyMemberSelect"
  37. @removed="loadFamilyMembersVisible" />
  38. <!-- 游客引导语 -->
  39. <view class="monthly-guidance" v-if="!isLoggedIn">
  40. <text class="guidance-icon">⚡</text>
  41. <text class="guidance-text">{{ monthlyGuidance }}</text>
  42. </view>
  43. <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
  44. <DimensionIntroCard
  45. v-if="!isLoggedIn"
  46. dimCode="wisdom"
  47. card-title="智维度 · 认知赋能"
  48. :benefits="[
  49. '全家认知能力评估与强项分析',
  50. '学习解读与认知训练建议',
  51. '认知趋势与同龄对比'
  52. ]"
  53. cta-text="登录做认知测评"
  54. secondary-text="了解认知维度"
  55. secondary-url="/pages/wisdom-detail/dimension-intro" />
  56. <!-- 4. 五维能量条(登录后) -->
  57. <FamilyEnergyBar
  58. v-if="isLoggedIn"
  59. dimensionCode="wisdom"
  60. :sandboxData="sandboxData"
  61. :dualDimension="dualDimension" />
  62. <!-- 认知指标卡片 -->
  63. <view class="indicator-cards-section" v-if="isLoggedIn && wisdomIndicators.length > 0">
  64. <view class="section-header">
  65. <text class="section-title">⚡ 认知指标</text>
  66. <text class="section-more" @click="goWisdomDetail">更多 ›</text>
  67. </view>
  68. <view class="indicator-cards">
  69. <view
  70. v-for="(ind, i) in wisdomIndicators"
  71. :key="ind.code"
  72. class="indicator-card"
  73. :class="{ 'no-data': !ind.value }"
  74. @click="goWisdomDetail()"
  75. >
  76. <view class="indicator-header">
  77. <text class="indicator-name">{{ ind.name }}</text>
  78. <text class="indicator-unit" v-if="ind.unit">{{ ind.unit }}</text>
  79. </view>
  80. <view class="indicator-body">
  81. <text class="indicator-value" v-if="ind.value || ind.numericValue !== null">{{ ind.value || formatNumeric(ind.numericValue) }}</text>
  82. <text class="indicator-placeholder" v-else>暂无数据</text>
  83. </view>
  84. <view class="indicator-footer">
  85. <text class="indicator-range" v-if="ind.refRange">参考: {{ ind.refRange }}</text>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. <!-- 学习解读 -->
  91. <view class="section score-explain-card" v-if="isLoggedIn && currentView === 'self' && dualDimension">
  92. <view class="explain-header">
  93. <text class="explain-icon">{{ (dualDimension.primary && dualDimension.primary.score || 0) >= 80 ? '✅' : (dualDimension.primary && dualDimension.primary.score || 0) >= 60 ? '⚠️' : '❗' }}</text>
  94. <text class="explain-title">学习解读</text>
  95. </view>
  96. <text class="explain-text">{{ (dualDimension.primary && dualDimension.primary.score || 0) >= 80 ? '学习能力良好,继续保持学习节奏。' : (dualDimension.primary && dualDimension.primary.score || 0) >= 60 ? '学习能力一般,建议多做认知训练。' : '学习能力需要提升,建议进行专业评估。' }}</text>
  97. </view>
  98. <!-- ===== 认知强项与提升空间 ===== -->
  99. <view class="section cognitive-insight" v-if="isLoggedIn && cognitiveReport">
  100. <view class="section-header">
  101. <text class="section-title">📈 认知强项与提升</text>
  102. </view>
  103. <template v-if="cogStrengths.length > 0 || cogImproves.length > 0">
  104. <view class="insight-group" v-if="cogStrengths.length > 0">
  105. <text class="insight-group-title">💪 你的强项</text>
  106. <view class="insight-item" v-for="(item, idx) in cogStrengths" :key="getInsightKey('s', idx)">
  107. <text class="insight-item-name">{{ item.label }}</text>
  108. <text class="insight-item-score">{{ item.score }}分</text>
  109. <text class="insight-item-tag tag-strong">强项</text>
  110. </view>
  111. </view>
  112. <view class="insight-group" v-if="cogImproves.length > 0">
  113. <text class="insight-group-title">🌱 提升空间</text>
  114. <view class="insight-item" v-for="(item, idx) in cogImproves" :key="getInsightKey('i', idx)">
  115. <text class="insight-item-name">{{ item.label }}</text>
  116. <text class="insight-item-score">{{ item.score }}分</text>
  117. <text class="insight-item-tag tag-improve">待提升</text>
  118. </view>
  119. </view>
  120. <view class="insight-tip" v-if="cogStrengths.length > 0 && cogImproves.length > 0">
  121. <text class="insight-tip-text">强项保持,薄弱维建议通过训练中心针对性提升</text>
  122. </view>
  123. </template>
  124. <view class="insight-empty" v-else-if="!cognitiveReport || !hasCognitiveScore">
  125. <text class="insight-empty-text">完成认知测评,查看强项与提升建议</text>
  126. <button class="insight-btn" @click="goCognitiveReport">去测评</button>
  127. </view>
  128. </view>
  129. <!-- 心维度画像 - 总体说明 -->
  130. <view class="dimension-portrait-section" v-if="isLoggedIn && wisdomPortrait">
  131. <view class="section-header">
  132. <text class="section-title">🧭 心维度画像</text>
  133. <text class="portrait-tier">{{ wisdomPortrait.tierLabel }}</text>
  134. </view>
  135. <view class="portrait-summary">
  136. <text class="portrait-desc">{{ wisdomPortrait.summary }}</text>
  137. </view>
  138. <view class="portrait-dimensions">
  139. <view class="portrait-dim-item" v-for="dim in wisdomPortrait.dims" :key="dim.code">
  140. <view class="portrait-dim-header">
  141. <view class="portrait-dim-icon" :style="{ background: dim.color }">{{ dim.icon }}</view>
  142. <text class="portrait-dim-name">{{ dim.name }}</text>
  143. <text class="portrait-dim-score">{{ dim.score }}</text>
  144. </view>
  145. <view class="portrait-dim-bar-track">
  146. <view class="portrait-dim-bar-fill" :style="{ width: dim.score + '%', background: dim.color }"></view>
  147. </view>
  148. <text class="portrait-dim-desc">{{ dim.desc }}</text>
  149. </view>
  150. </view>
  151. <view class="portrait-tip" v-if="wisdomPortrait.tip">
  152. <text class="portrait-tip-text">💡 {{ wisdomPortrait.tip }}</text>
  153. </view>
  154. </view>
  155. <!-- ===== DAN 报告(登录后) ===== -->
  156. <view class="section" v-if="isLoggedIn">
  157. <view class="section-header">
  158. <text class="section-title">&#x1F4CA; DAN 报告</text>
  159. <text class="section-more" @click="goDanUpload">上传 +</text>
  160. </view>
  161. <view class="dan-card" v-for="report in danReports" :key="report.id" @click="viewDanReport(report)">
  162. <view class="dan-card-header">
  163. <text class="dan-report-date">{{ formatDate(report.assessmentDate) }}</text>
  164. <text :class="['dan-status', report.draftStatus === 'confirmed' ? 'confirmed' : 'pending']">
  165. {{ report.draftStatus === 'confirmed' ? '已确认' : '待确认' }}
  166. </text>
  167. </view>
  168. <text class="dan-report-desc">DAN B4 智维度测评报告</text>
  169. </view>
  170. <view class="dan-empty" v-if="danReports.length === 0">
  171. <text class="dan-empty-text">暂无 DAN 报告,点击上方「上传 +」上传测评报告</text>
  172. </view>
  173. </view>
  174. <!-- 肠胃与认知卡片(有菌群报告显示认知指标,无报告引导上传) -->
  175. <view class="section gut-cognition-card" v-if="isLoggedIn">
  176. <view class="section-header">
  177. <text class="section-title">🦠 肠胃与认知</text>
  178. </view>
  179. <text class="gut-cog-subtitle">肠脑轴 · 肠道菌群,影响学习与思维</text>
  180. <template v-if="cognitionGutIndicators && cognitionGutIndicators.length > 0">
  181. <view class="gut-indicator-list">
  182. <view class="gut-indicator-item" v-for="ind in cognitionGutIndicators" :key="ind.id">
  183. <view class="gut-indicator-left">
  184. <text class="gut-indicator-name">{{ ind.indicatorName || ind.name }}</text>
  185. <text class="gut-indicator-impact" v-if="cognitiveImpactLabel(ind.indicatorName || ind.name)">影响{{ cognitiveImpactLabel(ind.indicatorName || ind.name) }}</text>
  186. </view>
  187. <view class="gut-indicator-bar-track">
  188. <view class="gut-indicator-bar-fill" :style="{ width: indicatorBarWidth(ind), background: indicatorColor(ind.status) }"></view>
  189. </view>
  190. <text class="gut-indicator-value">{{ formatIndicatorValue(ind) }}{{ ind.unit || '%' }}</text>
  191. <text :class="['gut-indicator-status', 'status-' + (ind.status || 'normal')]">{{ statusLabel(ind.status) }}</text>
  192. </view>
  193. </view>
  194. </template>
  195. <view class="gut-empty" v-else>
  196. <text class="gut-empty-icon">📄</text>
  197. <text class="gut-empty-text">上传菌群报告,查看认知相关指标</text>
  198. <button class="gut-upload-btn" @click="goReportUpload">去上传</button>
  199. </view>
  200. </view>
  201. <!-- 6. 功能入口 -->
  202. <view class="func-section" v-if="sectionVisible('func_entries')">
  203. <view class="func-grid">
  204. <view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
  205. <view class="func-icon-wrap">
  206. <text class="func-icon">{{ item.icon }}</text>
  207. </view>
  208. <text class="func-label">{{ item.label }}</text>
  209. </view>
  210. </view>
  211. </view>
  212. <!-- 智维度介绍卡片(登录后显示) -->
  213. <DimensionIntroCard dimCode="wisdom" v-if="isLoggedIn" learn-more-url="/pages/wisdom-detail/dimension-intro" />
  214. <!-- 底部占位 -->
  215. <view class="bottom-spacer"></view>
  216. <FloatingAvatar />
  217. </view>
  218. </template>
  219. <script>
  220. import nav from '../../utils/nav'
  221. import TabTransition from '../../components/tab-transition.vue'
  222. import RadarChart from '../../components/RadarChart.vue'
  223. import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
  224. import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
  225. import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
  226. import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
  227. import { getVisibleSections, getChildren, getFamilyEnergySandbox, getEnergySandbox, getAssessmentLatestResult, getDanReportByChild, getGutCognitionIndicators, getLatestIndicators, getRadarData } from '../../utils/api.js'
  228. import { parseDate } from '../../utils/format.js'
  229. export default {
  230. components: { TabTransition, RadarChart, FamilyEnergyBar, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
  231. data() {
  232. return {
  233. isLoggedIn: false,
  234. role: null,
  235. showTabTransition: true,
  236. activeChildId: null,
  237. activeChildName: '',
  238. userName: '',
  239. children: [],
  240. familyMembersVisible: [],
  241. selectedMemberId: null,
  242. wisdomSubDims: [],
  243. wisdomIndicators: [],
  244. sandboxData: null,
  245. currentView: 'self', // 'self' | 'family'
  246. dualDimension: null,
  247. visibleSections: [],
  248. cognitiveReport: null,
  249. articles: [],
  250. /** 认知雷达图:家庭平均分(默认取同龄同性 P50 常模) */
  251. cognitiveFamilyScores: [],
  252. /** 认知雷达图:人群位置(同龄同性 P50 常模,接口返回,失败时不显示假数据) */
  253. cognitivePopulationScores: [],
  254. danReports: [],
  255. cognitionGutIndicators: [],
  256. funcList: [
  257. { icon: '\u{1F4CA}', label: '认知报告', needLogin: true, page: '/pages/wisdom-detail/cognitive-report' },
  258. { icon: '\u{1F9E0}', label: '训练中心', needLogin: true, page: '/pages/wisdom-detail/training-hub' },
  259. { icon: '\u{1F3AF}', label: '测评预约', needLogin: false, page: '/pages/assessment/apply' },
  260. { icon: '\u{1F4D6}', label: '阅读天地', needLogin: false, page: '/pages/article-center/index?dimension=wisdom' }
  261. ]
  262. }
  263. },
  264. computed: {
  265. wisdomArticles: function() {
  266. if (!this.articles || this.articles.length === 0) return []
  267. var result = []
  268. for (var i = 0; i < this.articles.length; i++) {
  269. var a = this.articles[i]
  270. if (a.relatedDimensions && a.relatedDimensions.indexOf('wisdom') !== -1) {
  271. result.push(a)
  272. }
  273. }
  274. if (result.length === 0) return this.articles.slice(0, 5)
  275. return result.slice(0, 5)
  276. },
  277. cognitiveDimensions: function() {
  278. return [
  279. { label: '感知能力', key: 'perceptionScore' },
  280. { label: '专注力', key: 'focusScore' },
  281. { label: '记忆力', key: 'memoryScore' },
  282. { label: '逻辑思维', key: 'logicScore' },
  283. { label: '空间思维', key: 'spatialScore' },
  284. { label: '加工速度', key: 'processingSpeedScore' },
  285. { label: '成长型思维', key: 'wisdomGrowthMindset' }
  286. ]
  287. },
  288. cognitiveScores: function() {
  289. if (!this.cognitiveReport) return []
  290. var dims = this.cognitiveDimensions
  291. var scores = []
  292. // 解析 structuredAnalysis 中的成长型思维
  293. var growthMindset = 0
  294. if (this.cognitiveReport.structuredAnalysis) {
  295. try {
  296. var analysis = JSON.parse(this.cognitiveReport.structuredAnalysis)
  297. if (analysis && analysis.extra && analysis.extra.growthMindset) {
  298. var gm = analysis.extra.growthMindset
  299. var vals = Object.values(gm)
  300. if (vals.length > 0) growthMindset = parseInt(vals[0]) || 0
  301. }
  302. } catch (e) { /* ignore parse error */ }
  303. }
  304. for (var i = 0; i < dims.length; i++) {
  305. var key = dims[i].key
  306. if (key === 'wisdomGrowthMindset') {
  307. scores.push(growthMindset)
  308. } else {
  309. scores.push(this.cognitiveReport[key] || 0)
  310. }
  311. }
  312. return scores
  313. },
  314. /** 认知测评是否已有有效得分(任一维度 > 0) */
  315. hasCognitiveScore: function() {
  316. if (!this.cognitiveReport) return false
  317. var dims = this.cognitiveDimensions
  318. for (var i = 0; i < dims.length; i++) {
  319. var key = dims[i].key
  320. var v = 0
  321. if (key === 'wisdomGrowthMindset') {
  322. v = this.cognitiveScores[this.cognitiveDimensions.length - 1] || 0
  323. } else {
  324. v = this.cognitiveReport[key] || 0
  325. }
  326. if (v > 0) return true
  327. }
  328. return false
  329. },
  330. /** 认知强项:得分 ≥75 的维度 */
  331. cogStrengths: function() {
  332. var dims = this.cognitiveDimensions
  333. var result = []
  334. for (var i = 0; i < dims.length; i++) {
  335. var score = this.cognitiveScores[i] || 0
  336. if (score >= 75) {
  337. result.push({ label: dims[i].label, score: score })
  338. }
  339. }
  340. return result
  341. },
  342. /** 认知提升空间:得分 <60 的维度 */
  343. cogImproves: function() {
  344. var dims = this.cognitiveDimensions
  345. var result = []
  346. for (var i = 0; i < dims.length; i++) {
  347. var score = this.cognitiveScores[i] || 0
  348. if (score > 0 && score < 60) {
  349. result.push({ label: dims[i].label, score: score })
  350. }
  351. }
  352. return result
  353. },
  354. /** 每月引导语 */
  355. monthlyGuidance: function() {
  356. var month = new Date().getMonth()
  357. var messages = [
  358. '一月:新年新目标,制定学习计划',
  359. '二月:春节收获知识,拓展认知边界',
  360. '三月:植树节到,播种智慧种子',
  361. '四月:清明踏青,激发创造力',
  362. '五月:劳动节后,专注能力提升',
  363. '六月:考试季,科学备考策略',
  364. '七月:暑期黄金期,查漏补缺',
  365. '八月:开学准备,提前规划学习',
  366. '九月:新学期,设定学习目标',
  367. '十月:国庆假期,阅读充电',
  368. '十一月:年终冲刺,巩固知识',
  369. '十二月:年度总结,规划来年学习'
  370. ]
  371. return messages[month] || messages[0]
  372. },
  373. wisdomPortrait: function() {
  374. var self = this
  375. if (!this.isLoggedIn || !this.cognitiveReport) return null
  376. var cognitiveReport = this.cognitiveReport
  377. var dims = [
  378. { key: 'perceptionScore', label: '感知能力', color: '#FF6B9D', desc: '感官获取信息的能力' },
  379. { key: 'focusScore', label: '专注力', color: '#F59E0B', desc: '保持注意力集中的能力' },
  380. { key: 'memoryScore', label: '记忆力', color: '#6366F1', desc: '记忆和回忆信息的能力' },
  381. { key: 'logicScore', label: '逻辑思维', color: '#10B981', desc: '分析和推理能力' },
  382. { key: 'spatialScore', label: '空间思维', color: '#F59E0B', desc: '操作空间关系的能力' },
  383. { key: 'processingSpeedScore', label: '加工速度', color: '#FF8C42', desc: '快速处理信息的能力' },
  384. { key: 'growthMindset', label: '成长型思维', color: '#EC4899', desc: '相信能力可提升的信念' }
  385. ]
  386. var result = { overall: 0, tierLabel: '', summary: '', dims: [], tip: '' }
  387. var total = 0
  388. var count = 0
  389. var dimList = []
  390. for (var i = 0; i < dims.length; i++) {
  391. var key = dims[i].key
  392. var score = 0
  393. if (key === 'growthMindset' && cognitiveReport.structuredAnalysis) {
  394. try {
  395. var analysis = JSON.parse(cognitiveReport.structuredAnalysis)
  396. if (analysis && analysis.extra && analysis.extra.growthMindset) {
  397. var gm = analysis.extra.growthMindset
  398. var vals = Object.values(gm)
  399. if (vals.length > 0) score = parseInt(vals[0]) || 0
  400. }
  401. } catch (e) { /* ignore parse error */ }
  402. } else {
  403. score = cognitiveReport[key] || 0
  404. }
  405. if (isNaN(score)) score = 0
  406. score = Math.max(0, Math.min(100, Number(score)))
  407. total += score
  408. count++
  409. var matched = dims.find(function(d) { return d.key === key })
  410. dimList.push({
  411. code: key,
  412. name: matched ? matched.label : key,
  413. score: Math.round(score),
  414. color: matched ? matched.color : '#FF6B9D',
  415. desc: matched ? matched.desc : ''
  416. })
  417. }
  418. if (count === 0) return null
  419. result.overall = Math.round(total / count)
  420. result.dims = dimList
  421. var o = result.overall
  422. if (o >= 80) {
  423. result.tierLabel = '认知卓越者'
  424. result.summary = '您的认知能力卓越,各项能力均衡强劲,学习效率高。'
  425. result.tip = '保持学习节奏,可进一步拓展更广的知识边界'
  426. } else if (o >= 60) {
  427. result.tierLabel = '认知进取者'
  428. result.summary = '您的认知能力处于良好水平,具备较强的学习潜力。'
  429. result.tip = '针对薄弱维度进行针对性训练,逐步突破瓶颈'
  430. } else if (o >= 40) {
  431. result.tierLabel = '认知探索者'
  432. result.summary = '您的认知能力正在建立基础,关注学习习惯的养成很重要。'
  433. result.tip = '从最容易提升的维度开始,每天投入少量学习时间'
  434. } else {
  435. result.tierLabel = '认知起步者'
  436. result.summary = '您的认知能力需要更多关注和引导,建议从基础认能训练开始。'
  437. result.tip = '建议咨询专业教育者,制定个性化认知发展计划'
  438. }
  439. return result
  440. }
  441. },
  442. onShow() {
  443. this.showTabTransition = true
  444. var token = uni.getStorageSync('token')
  445. this.isLoggedIn = !!token
  446. if (this.isLoggedIn) {
  447. this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  448. this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
  449. }
  450. this.loadSectionConfig()
  451. if (this.isLoggedIn) {
  452. this.loadChildren()
  453. this.loadFamilyMembersVisible()
  454. this.loadCognitiveReport()
  455. this.loadDanReports()
  456. this.loadGutCognitionData()
  457. }
  458. // 游客也能浏览文章
  459. this._watchPageReady()
  460. },
  461. methods: {
  462. getInsightKey: function(prefix, idx) { return prefix + idx },
  463. onFamilyMemberSelect: function(member) {
  464. this.selectedMemberId = member.id
  465. },
  466. loadSectionConfig: function() {
  467. var baseSections = ['func_entries', 'recommended_reading']
  468. if (!uni.getStorageSync('token')) {
  469. this.visibleSections = baseSections
  470. return
  471. }
  472. var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  473. var self = this
  474. getVisibleSections({ pageKey: 'wisdom', role: role }).then(function(res) {
  475. if (res.code === 200 && res.data) {
  476. var keys = res.data.map(function(s) { return s.sectionKey })
  477. var merged = baseSections.slice()
  478. keys.forEach(function(k) { if (merged.indexOf(k) === -1) merged.push(k) })
  479. self.visibleSections = merged
  480. }
  481. }).catch(function(e) {
  482. self.visibleSections = ['func_entries', 'recommended_reading']
  483. })
  484. },
  485. loadChildren: function() {
  486. var self = this
  487. getChildren().then(function(res) {
  488. if (res.code === 200 && res.data) {
  489. self.children = res.data
  490. if (self.children.length > 0) {
  491. var activeId = uni.getStorageSync('currentChildId')
  492. if (activeId) {
  493. for (var i = 0; i < self.children.length; i++) {
  494. if (self.children[i].id == activeId) {
  495. self.activeChildId = activeId
  496. self.activeChildName = self.children[i].nickname || self.children[i].name || ''
  497. break
  498. }
  499. }
  500. }
  501. // Fallback: 首次进入或未选孩子时,默认选第一个孩子以加载认知数据(仅本地状态,不触发全局切换)
  502. if (!self.activeChildId && self.children.length > 0) {
  503. self.activeChildId = self.children[0].id
  504. self.activeChildName = self.children[0].nickname || self.children[0].name || ''
  505. }
  506. }
  507. // Load cognitive report if activeChildId is available
  508. if (self.activeChildId) {
  509. self.loadCognitiveReport()
  510. }
  511. }
  512. }).catch(function(e) {})
  513. },
  514. loadWisdomIndicators: function(memberId) {
  515. var self = this
  516. getLatestIndicators({ memberId: memberId, domain: 'wisdom' }).then(function(res) {
  517. if (res.code === 200) self.wisdomIndicators = res.data || []
  518. }).catch(function(e) { console.log('[wisdom] loadWisdomIndicators failed', e) })
  519. },
  520. goWisdomDetail: function() {
  521. var id = this.activeChildId || uni.getStorageSync('currentChildId')
  522. if (!id) return
  523. uni.navigateTo({ url: '/pages/wisdom-detail/member-wisdom-detail?memberId=' + id })
  524. },
  525. formatNumeric: function(v) {
  526. if (v === null || v === undefined) return ''
  527. var n = parseFloat(v)
  528. if (isNaN(n)) return String(v)
  529. return n % 1 === 0 ? String(n) : n.toFixed(1)
  530. },
  531. loadFamilyMembersVisible: function() {
  532. var self = this
  533. getFamilyEnergySandbox().then(function(res) {
  534. if (res && res.data) {
  535. self.sandboxData = res.data.sandboxData || null
  536. self.dualDimension = res.data.dualDimension || null
  537. if (res.data.members) {
  538. self.familyMembersVisible = res.data.members
  539. }
  540. }
  541. }).catch(function(e) {})
  542. },
  543. loadCognitiveReport: function() {
  544. var self = this
  545. var memberId = this.activeChildId
  546. if (!memberId) return
  547. getAssessmentLatestResult(memberId).then(function(res) {
  548. if (res.code === 200 && res.data) {
  549. self.cognitiveReport = res.data
  550. self.wisdomSubDims = (function() { var r = self.cognitiveReport || {}; return [{label:'感知能力', score:r.perceptionScore||0, desc:'通过感官获取信息'}, {label:'专注力', score:r.focusScore||0, desc:'保持注意力集中'}, {label:'记忆力', score:r.memoryScore||0, desc:'记忆和回忆信息'}, {label:'逻辑思维', score:r.logicScore||0, desc:'分析和推理能力'}, {label:'空间思维', score:r.spatialScore||0, desc:'操作空间关系'}, {label:'加工速度', score:r.processingSpeedScore||0, desc:'快速处理信息'}, {label:'成长型思维', score:r.growthMindset||0, desc:'相信能力可提升'}]; })();
  551. self.loadWisdomIndicators(memberId)
  552. self.loadCognitiveRadarData(memberId)
  553. }
  554. }).catch(function(e) {})
  555. },
  556. loadCognitiveRadarData: function(memberId) {
  557. var self = this
  558. if (!memberId) return
  559. getRadarData({ memberId: memberId, dimensions: ['perception','focus','memory','logic','spatial','processingSpeed','wisdomGrowthMindset'] }).then(function(res) {
  560. if (res && res.data) {
  561. self.cognitiveFamilyScores = res.data.familyScores || []
  562. self.cognitivePopulationScores = res.data.populationScores || []
  563. }
  564. }).catch(function(e) {
  565. console.warn('[wisdom] radar-data failed', e)
  566. })
  567. },
  568. onChildChanged: function(memberId) {
  569. this.activeChildId = memberId
  570. // FIX: 同步提交到 Vuex store
  571. this.$store.commit('switchToChild', memberId)
  572. for (var i = 0; i < this.children.length; i++) {
  573. if (this.children[i].id == memberId) {
  574. this.activeChildName = this.children[i].nickname || this.children[i].name || ''
  575. break
  576. }
  577. }
  578. this.loadFamilyMembersVisible()
  579. this.loadCognitiveReport()
  580. this.loadDanReports()
  581. },
  582. sectionVisible: function(key) {
  583. return this.visibleSections.length === 0 || this.visibleSections.indexOf(key) !== -1
  584. },
  585. onFuncClick: function(item) {
  586. if (item.needLogin && !this.isLoggedIn) {
  587. uni.showModal({
  588. title: '提示',
  589. content: '请先登录后使用此功能',
  590. success: (res) => { if (res.confirm) this.openLoginSheet() }
  591. })
  592. return
  593. }
  594. if (item.page) {
  595. nav.goPage(item.page)
  596. }
  597. },
  598. scrollToSection: function(section) {},
  599. // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
  600. onLoginSuccess: function() {
  601. this.isLoggedIn = true
  602. this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  603. this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
  604. this.loadSectionConfig()
  605. this.loadChildren()
  606. this.loadFamilyMembersVisible()
  607. this.loadCognitiveReport()
  608. this.loadDanReports()
  609. this.loadGutCognitionData()
  610. this._watchPageReady()
  611. },
  612. goCognitiveReport: function() {
  613. uni.navigateTo({ url: '/pages/wisdom-detail/cognitive-report' })
  614. },
  615. _watchPageReady() {
  616. var self = this
  617. var unwatch = this.$watch(function() {
  618. if (self.isLoggedIn) {
  619. return !!(self.cognitiveReport || self.sandboxData)
  620. } }, function(val) {
  621. if (val) {
  622. if (unwatch) unwatch()
  623. if (self.$refs.tabTransition) self.$refs.tabTransition.markReady()
  624. }
  625. })
  626. },
  627. // ===== DAN 报告 =====
  628. loadDanReports: function() {
  629. var self = this
  630. var memberId = self.activeChildId
  631. if (!memberId) return
  632. getDanReportByChild(memberId, 'wisdom').then(function(res) {
  633. if (res.code === 200 && res.data) {
  634. self.danReports = res.data
  635. }
  636. }).catch(function() {})
  637. },
  638. // ===== 肠胃与认知(肠脑轴认知指标) =====
  639. loadGutCognitionData: function() {
  640. var self = this
  641. var memberId = self.activeChildId
  642. if (!memberId) return
  643. getGutCognitionIndicators(memberId).then(function(res) {
  644. if (res.code === 200 && res.data) {
  645. var list = res.data
  646. self.cognitionGutIndicators = Array.isArray(list) ? list.slice(0, 6) : []
  647. } else {
  648. self.cognitionGutIndicators = []
  649. }
  650. }).catch(function() {
  651. self.cognitionGutIndicators = []
  652. })
  653. },
  654. // 认知影响映射:指标名 → 影响的认知功能
  655. cognitiveImpactLabel: function(name) {
  656. if (!name) return ''
  657. if (name.indexOf('多巴胺') !== -1 || name.indexOf('Dopamine') !== -1) return '专注力'
  658. if (name.indexOf('谷氨酸') !== -1 || name.indexOf('Glutamate') !== -1) return '学习记忆'
  659. if (name.indexOf('乙酰胆碱') !== -1) return '记忆力'
  660. if (name.indexOf('色氨酸') !== -1 || name.indexOf('Tryptophan') !== -1) return '睡眠·记忆'
  661. if (name.indexOf('丁酸') !== -1 || name.indexOf('Butyrate') !== -1) return '脑健康'
  662. if (name.indexOf('丙酸') !== -1 || name.indexOf('Propionate') !== -1) return '脑健康'
  663. if (name.indexOf('乙酸') !== -1 || name.indexOf('Acetate') !== -1) return '脑健康'
  664. if (name.indexOf('组胺') !== -1 || name.indexOf('Histamine') !== -1) return '注意力·觉醒'
  665. if (name.indexOf('短链脂肪酸') !== -1) return '脑健康'
  666. return ''
  667. },
  668. indicatorColor: function(status) {
  669. if (status === 'abnormal' || status === '偏高' || status === '偏低' || status === '过高' || status === '缺乏') return '#EF4444'
  670. if (status === 'warning' || status === '注意' || status === '不足') return '#F59E0B'
  671. return '#10B981'
  672. },
  673. statusLabel: function(status) {
  674. if (status === 'normal') return '正常'
  675. if (status === 'abnormal') return '异常'
  676. if (status === 'warning') return '警讯'
  677. if (status === '过高' || status === '过多') return '偏高'
  678. if (status === '缺乏' || status === '不足') return '偏低'
  679. return status || '正常'
  680. },
  681. // 指标值展示:后端字段为 indicatorValue,兼容数值型(如 0.32)
  682. formatIndicatorValue: function(ind) {
  683. if (!ind) return ''
  684. var v = ind.value !== undefined && ind.value !== null ? ind.value : ind.indicatorValue
  685. if (v === undefined || v === null || v === '') return ''
  686. return String(v)
  687. },
  688. // 指标条宽度:将指标值归一化为 0-100 百分比(仅对纯数字有效,否则按状态给默认宽度)
  689. indicatorBarWidth: function(ind) {
  690. var raw = ind.value !== undefined && ind.value !== null ? ind.value : ind.indicatorValue
  691. var n = parseFloat(raw)
  692. if (!isNaN(n) && n >= 0 && n <= 100) return n + '%'
  693. if (!isNaN(n) && n > 100) return '100%'
  694. // 非数值(如 "偏低"/"未检出"),按状态给默认可视化宽度
  695. var st = ind.status || ''
  696. if (st === '偏低' || st === '缺乏' || st === '不足' || st === 'abnormal') return '30%'
  697. if (st === '过高' || st === '过多' || st === 'warning') return '75%'
  698. return '55%'
  699. },
  700. goReportUpload: function() {
  701. uni.navigateTo({ url: '/pages/health/report-upload' })
  702. },
  703. goDanUpload: function() {
  704. uni.navigateTo({ url: '/pages/health/report-upload?type=a2' })
  705. },
  706. viewDanReport: function(report) {
  707. var reportId = report.sourceReportId || report.id
  708. uni.navigateTo({ url: '/pages/dan-assessment/report-result?reportId=' + reportId })
  709. },
  710. formatDate: function(dateStr) {
  711. if (!dateStr) return ''
  712. var d = parseDate(dateStr)
  713. if (!d) return ''
  714. return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate()
  715. }
  716. }
  717. }
  718. </script>
  719. <style scoped>
  720. .wisdom-container {
  721. min-height: 100vh;
  722. background: #f5f7fa;
  723. padding-bottom: 120rpx;
  724. }
  725. .func-section {
  726. margin: 20rpx 20rpx 0;
  727. }
  728. .func-grid {
  729. background: #fff;
  730. border-radius: 24rpx;
  731. padding: 30rpx 16rpx;
  732. display: flex;
  733. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  734. }
  735. .func-item {
  736. flex: 1;
  737. display: flex;
  738. flex-direction: column;
  739. align-items: center;
  740. }
  741. .func-item:active {
  742. opacity: 0.7;
  743. }
  744. .func-icon-wrap {
  745. width: 88rpx;
  746. height: 88rpx;
  747. border-radius: 50%;
  748. background: rgba(99,102,241,0.12);
  749. display: flex;
  750. align-items: center;
  751. justify-content: center;
  752. margin-bottom: 10rpx;
  753. }
  754. .func-icon {
  755. font-size: 40rpx;
  756. }
  757. .func-label {
  758. font-size: 24rpx;
  759. color: #333;
  760. font-weight: 500;
  761. }
  762. /* ===== Section ===== */
  763. .section {
  764. margin: 20rpx 20rpx;
  765. }
  766. .section-header {
  767. display: flex;
  768. flex-wrap: wrap;
  769. justify-content: space-between;
  770. align-items: center;
  771. margin-bottom: 20rpx;
  772. }
  773. .section-title {
  774. font-size: 30rpx;
  775. font-weight: bold;
  776. color: #333;
  777. }
  778. /* 页面说明(独占一行,避免与标题挤在同一行) */
  779. .section-sub {
  780. flex: 1 1 100%;
  781. font-size: 22rpx;
  782. color: #94A3B8;
  783. line-height: 1.4;
  784. margin-top: 6rpx;
  785. display: block;
  786. }
  787. .section-more {
  788. flex-shrink: 0;
  789. font-size: 24rpx;
  790. color: #6366F1;
  791. }
  792. /* 得分解读卡片 */
  793. .score-explain-card {
  794. background: #fff;
  795. border-radius: 24rpx;
  796. padding: 28rpx 24rpx;
  797. border-left: 8rpx solid #6366F1;
  798. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  799. }
  800. .explain-header {
  801. display: flex;
  802. align-items: center;
  803. margin-bottom: 14rpx;
  804. }
  805. .explain-icon {
  806. font-size: 36rpx;
  807. margin-right: 10rpx;
  808. }
  809. .explain-title {
  810. font-size: 30rpx;
  811. font-weight: bold;
  812. color: #333;
  813. }
  814. .explain-text {
  815. font-size: 26rpx;
  816. color: #666;
  817. line-height: 1.6;
  818. display: block;
  819. }
  820. /* 主按钮 */
  821. .primary-btn {
  822. background: linear-gradient(135deg, #6366F1, #4F46E5);
  823. color: #fff;
  824. font-size: 26rpx;
  825. font-weight: bold;
  826. border: none;
  827. border-radius: 48rpx;
  828. padding: 18rpx 56rpx;
  829. line-height: 1.6;
  830. margin: 0;
  831. box-shadow: 0 4rpx 16rpx rgba(79,70,229,0.35);
  832. }
  833. .primary-btn:active {
  834. opacity: 0.88;
  835. }
  836. /* ===== 底部占位 ===== */
  837. .indicator-cards-section { margin: 20rpx 30rpx; }
  838. .indicator-cards { display: flex; flex-direction: row; flex-wrap: wrap; gap: 16rpx; }
  839. .indicator-card { width: calc(50% - 8rpx); background: #fff; border-radius: 20rpx; padding: 24rpx 20rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
  840. .indicator-card.no-data { opacity: 0.5; }
  841. .indicator-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12rpx; }
  842. .indicator-name { font-size: 26rpx; color: #666; font-weight: 500; }
  843. .indicator-unit { font-size: 22rpx; color: #999; }
  844. .indicator-body { margin-bottom: 8rpx; }
  845. .indicator-value { font-size: 40rpx; font-weight: bold; color: #6366F1; }
  846. .indicator-placeholder { font-size: 26rpx; color: #ccc; }
  847. .indicator-range { font-size: 20rpx; color: #bbb; }
  848. .bottom-spacer { height: 120rpx; }
  849. /* DAN 报告卡片 */
  850. .dan-card {
  851. background: #fff;
  852. border-radius: 16rpx;
  853. padding: 24rpx;
  854. margin-bottom: 16rpx;
  855. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
  856. }
  857. .dan-card-header {
  858. display: flex;
  859. justify-content: space-between;
  860. align-items: center;
  861. margin-bottom: 12rpx;
  862. }
  863. .dan-report-date {
  864. font-size: 26rpx;
  865. color: #666;
  866. }
  867. .dan-status {
  868. font-size: 22rpx;
  869. padding: 4rpx 16rpx;
  870. border-radius: 8rpx;
  871. }
  872. .dan-status.confirmed {
  873. background: #D1FAE5;
  874. color: #065F46;
  875. }
  876. .dan-status.pending {
  877. background: #FEF3C7;
  878. color: #92400E;
  879. }
  880. .dan-report-desc {
  881. font-size: 28rpx;
  882. color: #333;
  883. font-weight: 500;
  884. margin-bottom: 12rpx;
  885. display: block;
  886. }
  887. .dan-card-footer {
  888. text-align: right;
  889. }
  890. .dan-view-link {
  891. font-size: 26rpx;
  892. color: #6366F1;
  893. }
  894. .dan-empty {
  895. background: #fff;
  896. border-radius: 16rpx;
  897. padding: 40rpx 24rpx;
  898. text-align: center;
  899. }
  900. .dan-empty-text {
  901. font-size: 24rpx;
  902. color: #999;
  903. }
  904. .avg-badge {
  905. flex: 1 1 100%;
  906. font-size: 20rpx;
  907. color: #94A3B8;
  908. margin-top: 6rpx;
  909. display: block;
  910. }
  911. /* 品牌定位语 */
  912. .brand-strap {
  913. margin: 16rpx 30rpx 0;
  914. padding: 24rpx 28rpx;
  915. background: linear-gradient(135deg, #F0F0FF, rgba(99,102,241,0.16));
  916. border-radius: 16rpx;
  917. border-left: 8rpx solid #6366F1;
  918. box-shadow: 0 2rpx 12rpx rgba(99,102,241,0.12);
  919. }
  920. .brand-strap-text {
  921. font-size: 26rpx;
  922. color: #3730A3;
  923. font-weight: bold;
  924. line-height: 1.5;
  925. }
  926. /* 每月引导语 */
  927. .monthly-guidance {
  928. margin: 16rpx 30rpx 0;
  929. padding: 20rpx 28rpx;
  930. background: linear-gradient(135deg, #F0F0FF, rgba(99,102,241,0.08));
  931. border-radius: 16rpx;
  932. border-left: 8rpx solid #6366F1;
  933. display: flex;
  934. align-items: center;
  935. gap: 12rpx;
  936. }
  937. .guidance-icon { font-size: 32rpx; }
  938. .guidance-text {
  939. font-size: 26rpx;
  940. color: #3730A3;
  941. font-weight: 500;
  942. }
  943. /* ===== 肠胃与认知卡片 ===== */
  944. .gut-cog-subtitle {
  945. font-size: 24rpx;
  946. color: #6366F1;
  947. margin-bottom: 16rpx;
  948. display: block;
  949. }
  950. .gut-indicator-list {
  951. display: flex;
  952. flex-direction: column;
  953. gap: 12rpx;
  954. }
  955. .gut-indicator-item {
  956. display: flex;
  957. align-items: center;
  958. gap: 12rpx;
  959. }
  960. .gut-indicator-left {
  961. width: 110rpx;
  962. flex-shrink: 0;
  963. }
  964. .gut-indicator-name {
  965. font-size: 22rpx;
  966. color: #666;
  967. font-weight: 500;
  968. display: block;
  969. }
  970. .gut-indicator-impact {
  971. font-size: 18rpx;
  972. color: #6366F1;
  973. display: block;
  974. margin-top: 2rpx;
  975. }
  976. .gut-indicator-bar-track {
  977. flex: 1;
  978. height: 16rpx;
  979. background: #F0F0F0;
  980. border-radius: 8rpx;
  981. overflow: hidden;
  982. }
  983. .gut-indicator-bar-fill {
  984. height: 100%;
  985. border-radius: 8rpx;
  986. transition: width 0.3s;
  987. }
  988. .gut-indicator-value {
  989. font-size: 22rpx;
  990. color: #666;
  991. width: 60rpx;
  992. text-align: right;
  993. }
  994. .gut-indicator-status {
  995. font-size: 20rpx;
  996. padding: 2rpx 12rpx;
  997. border-radius: 10rpx;
  998. width: 60rpx;
  999. text-align: center;
  1000. }
  1001. .status-normal {
  1002. background: #D1FAE5;
  1003. color: #059669;
  1004. }
  1005. .status-abnormal {
  1006. background: #FEE2E2;
  1007. color: #DC2626;
  1008. }
  1009. .status-warning {
  1010. background: #FEF3C7;
  1011. color: #D97706;
  1012. }
  1013. .gut-empty {
  1014. display: flex;
  1015. flex-direction: column;
  1016. align-items: center;
  1017. padding: 40rpx 0;
  1018. }
  1019. .gut-empty-icon {
  1020. font-size: 56rpx;
  1021. margin-bottom: 16rpx;
  1022. }
  1023. .gut-empty-text {
  1024. font-size: 26rpx;
  1025. color: #999;
  1026. margin-bottom: 24rpx;
  1027. }
  1028. .gut-upload-btn {
  1029. background: linear-gradient(135deg, #6366F1, #818CF8);
  1030. color: #fff;
  1031. font-size: 26rpx;
  1032. font-weight: 600;
  1033. border: none;
  1034. border-radius: 40rpx;
  1035. padding: 16rpx 64rpx;
  1036. line-height: 1.6;
  1037. margin: 0;
  1038. }
  1039. /* ===== 认知强项与提升卡片 ===== */
  1040. .insight-group {
  1041. margin-bottom: 16rpx;
  1042. }
  1043. .insight-group-title {
  1044. font-size: 26rpx;
  1045. font-weight: 600;
  1046. color: #333;
  1047. margin-bottom: 10rpx;
  1048. display: block;
  1049. }
  1050. .insight-item {
  1051. display: flex;
  1052. align-items: center;
  1053. padding: 12rpx 16rpx;
  1054. background: #F8FAFF;
  1055. border-radius: 12rpx;
  1056. margin-bottom: 8rpx;
  1057. }
  1058. .insight-item-name {
  1059. flex: 1;
  1060. font-size: 26rpx;
  1061. color: #333;
  1062. }
  1063. .insight-item-score {
  1064. font-size: 24rpx;
  1065. color: #666;
  1066. margin-right: 12rpx;
  1067. }
  1068. .insight-item-tag {
  1069. font-size: 20rpx;
  1070. padding: 4rpx 16rpx;
  1071. border-radius: 8rpx;
  1072. font-weight: 500;
  1073. }
  1074. .insight-item-tag.tag-strong {
  1075. background: #D1FAE5;
  1076. color: #059669;
  1077. }
  1078. .insight-item-tag.tag-improve {
  1079. background: #FEF3C7;
  1080. color: #D97706;
  1081. }
  1082. .insight-tip {
  1083. padding: 12rpx 16rpx;
  1084. background: #EEF2FF;
  1085. border-radius: 12rpx;
  1086. }
  1087. .insight-tip-text {
  1088. font-size: 22rpx;
  1089. color: #6366F1;
  1090. }
  1091. .insight-empty {
  1092. display: flex;
  1093. flex-direction: column;
  1094. align-items: center;
  1095. padding: 30rpx 0;
  1096. }
  1097. .insight-empty-text {
  1098. font-size: 24rpx;
  1099. color: #999;
  1100. margin-bottom: 20rpx;
  1101. }
  1102. .insight-btn {
  1103. background: linear-gradient(135deg, #6366F1, #818CF8);
  1104. color: #fff;
  1105. font-size: 26rpx;
  1106. font-weight: 600;
  1107. border: none;
  1108. border-radius: 40rpx;
  1109. padding: 14rpx 60rpx;
  1110. line-height: 1.6;
  1111. margin: 0;
  1112. }
  1113. /* 维度画像样式 */
  1114. .dimension-portrait-section {
  1115. margin: 24rpx 30rpx;
  1116. background: #fff;
  1117. border-radius: 24rpx;
  1118. padding: 32rpx 24rpx;
  1119. box-shadow: 0 4rpx 20rpx rgba(99, 102, 241, 0.12);
  1120. border: 2rpx solid rgba(99, 102, 241, 0.1);
  1121. }
  1122. .portrait-tier {
  1123. font-size: 26rpx;
  1124. color: #6366F1;
  1125. font-weight: bold;
  1126. margin-left: 16rpx;
  1127. }
  1128. .portrait-summary {
  1129. margin-bottom: 24rpx;
  1130. }
  1131. .portrait-desc {
  1132. font-size: 26rpx;
  1133. color: #666;
  1134. line-height: 1.6;
  1135. display: block;
  1136. }
  1137. .portrait-dimensions {
  1138. margin-bottom: 20rpx;
  1139. }
  1140. .portrait-dim-item {
  1141. margin-bottom: 18rpx;
  1142. }
  1143. .portrait-dim-header {
  1144. display: flex;
  1145. align-items: center;
  1146. margin-bottom: 6rpx;
  1147. }
  1148. .portrait-dim-icon {
  1149. width: 36rpx;
  1150. height: 36rpx;
  1151. border-radius: 18rpx;
  1152. display: flex;
  1153. align-items: center;
  1154. justify-content: center;
  1155. margin-right: 10rpx;
  1156. font-size: 20rpx;
  1157. }
  1158. .portrait-dim-name {
  1159. font-size: 24rpx;
  1160. color: #333;
  1161. flex: 1;
  1162. }
  1163. .portrait-dim-score {
  1164. font-size: 26rpx;
  1165. font-weight: bold;
  1166. color: #6366F1;
  1167. margin-right: 12rpx;
  1168. }
  1169. .portrait-dim-bar-track {
  1170. height: 10rpx;
  1171. background: #F0F4FF;
  1172. border-radius: 5rpx;
  1173. overflow: hidden;
  1174. margin-bottom: 4rpx;
  1175. flex: 1;
  1176. }
  1177. .portrait-dim-bar-fill {
  1178. height: 100%;
  1179. border-radius: 5rpx;
  1180. transition: width 0.3s ease;
  1181. }
  1182. .portrait-dim-desc {
  1183. font-size: 20rpx;
  1184. color: #999;
  1185. display: block;
  1186. }
  1187. .portrait-tip {
  1188. padding: 16rpx 20rpx;
  1189. background: linear-gradient(135deg, #F0F4FF, #E8F1FE);
  1190. border-radius: 12rpx;
  1191. border-left: 8rpx solid #6366F1;
  1192. margin-top: 16rpx;
  1193. }
  1194. .portrait-tip-text {
  1195. font-size: 22rpx;
  1196. color: #1D1D1F;
  1197. line-height: 1.5;
  1198. }
  1199. </style>