index.vue 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. <template>
  2. <view class="container">
  3. <!-- 品牌头部 -->
  4. <PageBanner theme="mind-wisdom"
  5. tagline="心智成长 · 智慧人生"
  6. quote="知己知彼,百战不殆" />
  7. <!-- ===== 心/智 Tab栏(最顶部) ===== -->
  8. <view class="tab-bar">
  9. <view
  10. :class="['tab-item', activeTab === 'emotion' ? 'active' : '']"
  11. @click="switchTab('emotion')">
  12. <text class="tab-icon">&#x2764;</text>
  13. <text class="tab-label">心·心理</text>
  14. </view>
  15. <view
  16. :class="['tab-item', activeTab === 'cognitive' ? 'active' : '']"
  17. @click="switchTab('cognitive')">
  18. <text class="tab-icon">&#x1F9E0;</text>
  19. <text class="tab-label">智·认知</text>
  20. </view>
  21. </view>
  22. <!-- 游客登录引导卡 -->
  23. <template v-if="!isLoggedIn">
  24. <LoginGuideCard
  25. title="登录后查看心智成长报告"
  26. :items="[
  27. '查看EMI心理测评四维分析',
  28. '查看DAN认知六维雷达图',
  29. '阅读专属文章、参与认知训练'
  30. ]" />
  31. </template>
  32. <!-- ===== 心·心理 Tab ===== -->
  33. <template v-if="activeTab === 'emotion'">
  34. <!-- 登录后:五维能量条 -->
  35. <FamilyEnergyBar
  36. v-if="isLoggedIn"
  37. dimensionCode="mind"
  38. :sandboxData="sandboxData"
  39. :dualDimension="dualDimension" />
  40. <!-- 关系图谱(登录后可见) -->
  41. <FamilyRelationGraph
  42. v-if="isLoggedIn && familyMembersVisible.length > 0"
  43. dimensionCode="mind"
  44. :selfId="activeChildId"
  45. :members="familyMembersVisible"
  46. :energyMap="energyMapForGraph"
  47. :intimacyMap="intimacyMapForGraph"
  48. @memberTap="goMemberDetail" />
  49. <!-- 登录后:用户快捷入口 -->
  50. <UserQuickEntry
  51. v-if="isLoggedIn"
  52. :userName="userName"
  53. roleName="家长"
  54. :currentChildName="currentChildName"
  55. :children="children"
  56. @childChanged="onChildChanged"
  57. @scrollTo="scrollToSection" />
  58. <!-- ===== 心理能量概况 ===== -->
  59. <view class="energy-overview" v-if="emiData">
  60. <view class="energy-badge">
  61. <text class="badge-icon">&#x2764;</text>
  62. <text class="badge-value">{{ emiData.overallScore || 0 }}</text>
  63. <text class="badge-label">心能量</text>
  64. </view>
  65. <view class="energy-badge">
  66. <text class="badge-icon">&#x1F4AA;</text>
  67. <text class="badge-value">{{ emiData.resilienceScore || 0 }}</text>
  68. <text class="badge-label">韧性评分</text>
  69. </view>
  70. <view class="energy-badge">
  71. <text class="badge-icon">&#x1F64F;</text>
  72. <text class="badge-value">{{ emiData.emotionScore || 0 }}</text>
  73. <text class="badge-label">情绪评分</text>
  74. </view>
  75. </view>
  76. <!-- EMI 四维条形图 -->
  77. <view class="emi-section" v-if="emiData">
  78. <view class="emi-card">
  79. <text class="emi-card-title">EMI 心理四维</text>
  80. <view class="emi-bar-item" v-for="dim in emiDimensions" :key="dim.key">
  81. <text class="emi-bar-label">{{ dim.label }}</text>
  82. <view class="emi-bar-track">
  83. <view class="emi-bar-fill" :style="{ width: (emiData[dim.key] || 0) + '%', background: dim.color }"></view>
  84. </view>
  85. <text class="emi-bar-score">{{ emiData[dim.key] || 0 }}</text>
  86. <text :class="['emi-bar-level', 'level-' + getScoreLevel(emiData[dim.key] || 0)]">{{ getScoreLevelText(emiData[dim.key] || 0) }}</text>
  87. </view>
  88. <view class="emi-footer">
  89. <text class="emi-overall">综合EQ分: {{ emiData.overallScore || 0 }}</text>
  90. <view class="emi-link" @click="goEmotionReport">
  91. <text class="emi-link-text">查看完整报告</text>
  92. <text class="emi-link-arrow">&#x2192;</text>
  93. </view>
  94. </view>
  95. </view>
  96. </view>
  97. <!-- 无 EMI 数据占位 -->
  98. <view class="placeholder-card" v-if="!emiData && !emiLoading">
  99. <text class="placeholder-icon">&#x1F9E0;</text>
  100. <text class="placeholder-title">心理评估</text>
  101. <text class="placeholder-desc">完成 EMI 心理测评后,可在此查看详细的心理分析报告</text>
  102. <view class="placeholder-btn" @click="goAssessment" v-if="isLoggedIn">预约测评</view>
  103. <view class="placeholder-btn" @click="goLogin" v-else>立即登录</view>
  104. </view>
  105. <!-- ===== 大五人格雷达图(从EMI数据中提取) ===== -->
  106. <view class="section personality-section" v-if="emiData">
  107. <view class="section-header">
  108. <text class="section-title">大五人格</text>
  109. </view>
  110. <RadarChart
  111. :dimensions="personalityDimensions"
  112. :scores="personalityScores"
  113. :childName="currentChildName || '孩子'"
  114. fillColor="#8B5CF6"
  115. gridColor="#EDE9FE"
  116. labelColor="#6D28D9" />
  117. </view>
  118. <!-- 每日心理 -->
  119. <view class="section" v-if="sectionVisible('daily_tip')">
  120. <view class="section-header">
  121. <text class="section-title">每日心理</text>
  122. </view>
  123. <view class="tip-card">
  124. <view class="tip-left-bar"></view>
  125. <view class="tip-body">
  126. <text class="tip-title">{{ dailyTip.title }}</text>
  127. <text class="tip-content">{{ dailyTip.content }}</text>
  128. <view class="tip-footer">
  129. <text class="tip-source">{{ dailyTip.source }}</text>
  130. </view>
  131. </view>
  132. </view>
  133. </view>
  134. <!-- 推荐阅读 -->
  135. <view class="section" v-if="sectionVisible('recommended_reading')">
  136. <view class="section-header">
  137. <text class="section-title">推荐阅读</text>
  138. </view>
  139. <view class="article-list">
  140. <view class="article-card" v-for="article in emotionArticles" :key="article.id" @click="goArticleDetail(article.id)">
  141. <view class="article-category" :style="{ background: article.categoryColor }">
  142. <text class="article-category-text">{{ article.category }}</text>
  143. </view>
  144. <text class="article-title">{{ article.title }}</text>
  145. <text class="article-summary">{{ article.summary }}</text>
  146. <view class="article-meta">
  147. <view class="meta-item">
  148. <text class="meta-icon">&#x1F4D6;</text>
  149. <text class="meta-text">{{ article.views }}</text>
  150. </view>
  151. <view class="meta-item">
  152. <text class="meta-icon">&#x1F44D;</text>
  153. <text class="meta-text">{{ article.likes }}</text>
  154. </view>
  155. <text class="article-date">{{ article.date }}</text>
  156. </view>
  157. </view>
  158. </view>
  159. </view>
  160. <!-- 心愿单入口(心理Tab) -->
  161. <view class="section wishlist-section" v-if="isLoggedIn">
  162. <view class="wishlist-card" @click="goWishlist">
  163. <view class="wishlist-left">
  164. <text class="wishlist-icon">💝</text>
  165. </view>
  166. <view class="wishlist-body">
  167. <text class="wishlist-title">心愿单</text>
  168. <text class="wishlist-desc">孩子可在此创建、兑换心愿</text>
  169. </view>
  170. <text class="wishlist-arrow">›</text>
  171. </view>
  172. </view>
  173. </template>
  174. <!-- ===== 智·认知 Tab ===== -->
  175. <template v-if="activeTab === 'cognitive'">
  176. <!-- 登录后:用户快捷入口 -->
  177. <UserQuickEntry
  178. v-if="isLoggedIn"
  179. :userName="userName"
  180. roleName="家长"
  181. :currentChildName="currentChildName"
  182. :children="children"
  183. @childChanged="onChildChanged"
  184. @scrollTo="scrollToSection" />
  185. <!-- ===== 认知区块 ===== -->
  186. <!-- 认知总分明细 -->
  187. <view class="cognitive-score-highlight" v-if="assessmentData">
  188. <view class="score-big-cell">
  189. <text class="score-big-label">认知总分</text>
  190. <text class="score-big-value">{{ assessmentData.overallScore || 0 }}</text>
  191. </view>
  192. <view class="score-big-cell">
  193. <text class="score-big-label">DAN等级</text>
  194. <text class="score-big-value highlight">{{ assessmentData.danLevel || '-' }}</text>
  195. </view>
  196. <view class="score-big-cell">
  197. <text class="score-big-label">上次测评</text>
  198. <text class="score-big-value small">{{ assessmentData.assessmentDate || '-' }}</text>
  199. </view>
  200. </view>
  201. <!-- 综合认知评分(操作入口) -->
  202. <view class="cognitive-overview" v-if="assessmentData">
  203. <view class="cognitive-score-card">
  204. <view class="score-actions">
  205. <view class="score-link" @click="goCognitiveReport">
  206. <text class="score-link-text">查看完整报告</text>
  207. <text class="score-link-arrow">&#x2192;</text>
  208. </view>
  209. <view class="score-link secondary" @click="goAssessment">
  210. <text class="score-link-text">预约测评</text>
  211. </view>
  212. </view>
  213. </view>
  214. </view>
  215. <!-- 无测评数据占位 -->
  216. <view class="placeholder-card" v-if="!assessmentData && !assessmentLoading">
  217. <text class="placeholder-icon">&#x1F9E0;</text>
  218. <text class="placeholder-title">认知评估</text>
  219. <text class="placeholder-desc">完成 DAN 认知测评后,可在此查看详细的六维认知分析报告</text>
  220. <view class="placeholder-btn" @click="goAssessment" v-if="isLoggedIn">预约测评</view>
  221. <view class="placeholder-btn" @click="goLogin" v-else>立即登录</view>
  222. </view>
  223. <!-- 认知六维雷达图 -->
  224. <view class="radar-section" v-if="assessmentData && familyMembers.length > 0">
  225. <scroll-view class="member-scroll" scroll-x show-scrollbar="false">
  226. <view class="member-row">
  227. <view
  228. v-for="member in familyMembers"
  229. :key="member.childId"
  230. :class="['member-tab', member.childId === selectedChildId ? 'active' : '']"
  231. @click="selectMember(member.childId)"
  232. >
  233. <text class="member-name">{{ member.childName }}</text>
  234. <text class="member-overall">{{ member.overallScore || 0 }}分</text>
  235. </view>
  236. </view>
  237. </scroll-view>
  238. <view class="radar-card">
  239. <RadarChart
  240. :dimensions="cognitiveDimensions"
  241. :scores="currentScores"
  242. :childName="currentChildName"
  243. @dimensionClick="goTraining"
  244. />
  245. <text class="radar-hint">点击维度标签进入能力训练</text>
  246. </view>
  247. </view>
  248. <!-- 认知训练入口 -->
  249. <view class="section" v-if="isLoggedIn">
  250. <view class="section-header">
  251. <text class="section-title">认知训练</text>
  252. </view>
  253. <view class="game-grid">
  254. <view class="game-item" @click="goGame('schulte')">
  255. <view class="game-icon-wrap" style="background: #FFF7ED">
  256. <text class="game-icon">&#x1F3AF;</text>
  257. </view>
  258. <text class="game-name">舒尔特方格</text>
  259. </view>
  260. <view class="game-item" @click="goGame('1a2b')">
  261. <view class="game-icon-wrap" style="background: #EFF6FF">
  262. <text class="game-icon">&#x1F522;</text>
  263. </view>
  264. <text class="game-name">猜数字</text>
  265. </view>
  266. <view class="game-item" @click="goGame('sudoku')">
  267. <view class="game-icon-wrap" style="background: #EDE9FE">
  268. <text class="game-icon">&#x1F9E9;</text>
  269. </view>
  270. <text class="game-name">数独</text>
  271. </view>
  272. </view>
  273. </view>
  274. <!-- 本月阅读统计 -->
  275. <view class="section" v-if="isLoggedIn && readingStats">
  276. <view class="section-header">
  277. <text class="section-title">本月阅读</text>
  278. </view>
  279. <view class="reading-card">
  280. <view class="reading-stats">
  281. <text class="reading-count">{{ readingStats.totalArticles || 0 }}篇</text>
  282. <text class="reading-divider">/</text>
  283. <text class="reading-duration">{{ readingMinutes }}分钟</text>
  284. </view>
  285. <view class="reading-progress-wrap">
  286. <view class="reading-progress-track">
  287. <view class="reading-progress-fill" :style="{ width: (readingStats.progress || 0) + '%' }"></view>
  288. </view>
  289. <text class="reading-progress-text">{{ readingStats.progress || 0 }}%</text>
  290. </view>
  291. <view class="reading-link" @click="goArticles">
  292. <text class="reading-link-text">去阅读 &#x2192;</text>
  293. </view>
  294. </view>
  295. </view>
  296. <!-- 推荐阅读(认知Tab内) -->
  297. <view class="section" v-if="sectionVisible('recommended_reading')">
  298. <view class="section-header">
  299. <text class="section-title">推荐阅读</text>
  300. </view>
  301. <view class="article-list">
  302. <view class="article-card" v-for="article in cognitiveArticles" :key="article.id" @click="goArticleDetail(article.id)">
  303. <view class="article-category" :style="{ background: article.categoryColor }">
  304. <text class="article-category-text">{{ article.category }}</text>
  305. </view>
  306. <text class="article-title">{{ article.title }}</text>
  307. <text class="article-summary">{{ article.summary }}</text>
  308. <view class="article-meta">
  309. <view class="meta-item">
  310. <text class="meta-icon">&#x1F4D6;</text>
  311. <text class="meta-text">{{ article.views }}</text>
  312. </view>
  313. <view class="meta-item">
  314. <text class="meta-icon">&#x1F44D;</text>
  315. <text class="meta-text">{{ article.likes }}</text>
  316. </view>
  317. <text class="article-date">{{ article.date }}</text>
  318. </view>
  319. </view>
  320. </view>
  321. </view>
  322. </template>
  323. <!-- ===== 底部:维度任务/活动/商品 ===== -->
  324. <DimensionTasks
  325. v-if="isLoggedIn"
  326. :dimensionCode="currentDimensionCode"
  327. :tasks="dimensionTasks"
  328. @taskClick="onTaskClick"
  329. @moreTasks="goTasks" />
  330. <DimensionActivities
  331. :dimensionCode="currentDimensionCode"
  332. :activities="dimensionActivities"
  333. @activityClick="goActivityDetail"
  334. @moreActivities="goMoreActivities" />
  335. <DimensionProducts
  336. :dimensionCode="currentDimensionCode"
  337. :products="dimensionProducts"
  338. @productClick="goProductDetail"
  339. @moreProducts="goMoreProducts" />
  340. <!-- ===== 底部:功能入口 Grid(通用) ===== -->
  341. <view class="func-section" v-if="sectionVisible('func_entries')">
  342. <view class="func-grid">
  343. <view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
  344. <view class="func-icon-wrap">
  345. <text class="func-icon">{{ item.icon }}</text>
  346. </view>
  347. <text class="func-label">{{ item.label }}</text>
  348. </view>
  349. </view>
  350. </view>
  351. <!-- 底部占位 -->
  352. <view class="bottom-spacer"></view>
  353. </view>
  354. </template>
  355. <script>
  356. import PageBanner from '../../components/PageBanner.vue'
  357. import LoginGuideCard from '../../components/LoginGuideCard.vue'
  358. import RadarChart from '../../components/RadarChart.vue'
  359. import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
  360. import UserQuickEntry from '../../components/UserQuickEntry.vue'
  361. import DimensionTasks from '../../components/DimensionTasks.vue'
  362. import DimensionActivities from '../../components/DimensionActivities.vue'
  363. import DimensionProducts from '../../components/DimensionProducts.vue'
  364. import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
  365. import { getVisibleSections, getFeaturedArticles, getEmiReport, getAssessmentLatestResult, getDailyTip, getReadingStats, getFamilyEnergySandbox, getTodayTasksByCategory, getActivityList, getProductsByDomain, getChildren, getVisibleFamilyMembers, getEnergyOverview } from '../../utils/api.js'
  366. import config from '../../config.js'
  367. export default {
  368. components: { PageBanner, RadarChart, LoginGuideCard, FamilyEnergyBar, UserQuickEntry, DimensionTasks, DimensionActivities, DimensionProducts, FamilyRelationGraph },
  369. data() {
  370. return {
  371. isLoggedIn: false,
  372. role: null,
  373. currentChildId: null,
  374. visibleSections: [],
  375. familyMembersVisible: [],
  376. // 心理 Tab 数据
  377. emiData: null,
  378. emiLoading: true,
  379. personalityDimensions: [
  380. { key: 'opennessScore', label: '开放性' },
  381. { key: 'conscientiousnessScore', label: '尽责性' },
  382. { key: 'extraversionScore', label: '外向性' },
  383. { key: 'agreeablenessScore', label: '宜人性' },
  384. { key: 'neuroticismScore', label: '神经质' }
  385. ],
  386. dailyTip: {
  387. title: '成长型思维',
  388. content: '拥有成长型思维的孩子相信自己的能力可以通过努力提升,他们更愿意接受挑战,从失败中学习。作为家长,多鼓励孩子的努力而非天赋,可以帮助孩子建立成长型思维。',
  389. source: '—— 卡罗尔·德韦克《终身成长》'
  390. },
  391. emiDimensions: [
  392. { key: 'emotionScore', label: '情绪商数', color: '#FF6B35' },
  393. { key: 'resilienceScore', label: '心理韧性', color: '#F97316' },
  394. { key: 'stressCopingScore', label: '压力应对', color: '#FB923C' },
  395. { key: 'selfAwarenessScore', label: '自我认知', color: '#FBBF24' }
  396. ],
  397. // 认知 Tab 数据
  398. assessmentData: null,
  399. assessmentLoading: true,
  400. cognitiveDimensions: [
  401. { label: '感知', key: 'perception' },
  402. { label: '专注', key: 'focus' },
  403. { label: '记忆', key: 'memory' },
  404. { label: '逻辑', key: 'logic' },
  405. { label: '空间', key: 'spatial' },
  406. { label: '加工速度', key: 'processingSpeed' }
  407. ],
  408. familyMembers: [],
  409. selectedChildId: null,
  410. currentScores: [0, 0, 0, 0, 0, 0],
  411. currentChildName: '',
  412. readingStats: null,
  413. // 功能入口
  414. funcList: [
  415. { icon: '\u{1F4D6}', label: '阅读', needLogin: false, page: '/pages/mind/articles' },
  416. { icon: '\u{1F3AE}', label: '小游戏', needLogin: true, page: '/pages/games/list' },
  417. { icon: '\u{1F4CA}', label: '测评报告', needLogin: true, page: '/pages/mind/emotion-report' },
  418. { icon: '\u{1F9EA}', label: '训练中心', needLogin: true, page: '/pages/mind/training' },
  419. { icon: '\u{1F4DD}', label: '成长档案', needLogin: true, page: '/pages/growth/index' }
  420. ],
  421. articles: [],
  422. // 五维组件数据
  423. userName: '',
  424. children: [],
  425. sandboxData: null,
  426. dualDimension: null,
  427. dimensionTasks: [],
  428. dimensionActivities: [],
  429. dimensionProducts: [],
  430. activeTab: 'emotion'
  431. }
  432. },
  433. computed: {
  434. readingMinutes: function() {
  435. var stats = this.readingStats
  436. if (!stats || !stats.totalDurationSeconds) return 0
  437. return Math.floor((stats.totalDurationSeconds || 0) / 60)
  438. },
  439. personalityScores: function() {
  440. if (!this.emiData) return []
  441. return [
  442. this.emiData.opennessScore || 0,
  443. this.emiData.conscientiousnessScore || 0,
  444. this.emiData.extraversionScore || 0,
  445. this.emiData.agreeablenessScore || 0,
  446. this.emiData.neuroticismScore || 0
  447. ]
  448. },
  449. energyMapForGraph: function() {
  450. var map = {}
  451. if (this.sandboxData && this.sandboxData.members) {
  452. for (var i = 0; i < this.sandboxData.members.length; i++) {
  453. var m = this.sandboxData.members[i]
  454. map[m.memberId || m.id] = {
  455. bodyScore: m.bodyScore || 0,
  456. mindScore: m.mindScore || 0,
  457. actionScore: m.actionScore || 0
  458. }
  459. }
  460. }
  461. return map
  462. },
  463. intimacyMapForGraph: function() {
  464. return {}
  465. },
  466. emotionArticles: function() {
  467. if (!this.articles || this.articles.length === 0) return []
  468. var result = []
  469. for (var i = 0; i < this.articles.length; i++) {
  470. var a = this.articles[i]
  471. if (a.relatedDimensions && a.relatedDimensions.indexOf('mind') !== -1) {
  472. result.push(a)
  473. }
  474. }
  475. if (result.length === 0) return this.articles.slice(0, 5)
  476. return result.slice(0, 5)
  477. },
  478. cognitiveArticles: function() {
  479. if (!this.articles || this.articles.length === 0) return []
  480. var result = []
  481. for (var i = 0; i < this.articles.length; i++) {
  482. var a = this.articles[i]
  483. if (a.relatedDimensions && a.relatedDimensions.indexOf('wisdom') !== -1) {
  484. result.push(a)
  485. }
  486. }
  487. if (result.length === 0) return this.articles.slice(0, 5)
  488. return result.slice(0, 5)
  489. },
  490. currentDimensionCode: function() {
  491. return this.activeTab === 'emotion' ? 'mind' : 'wisdom'
  492. }
  493. },
  494. onShow() {
  495. var token = uni.getStorageSync('token')
  496. this.isLoggedIn = !!token
  497. if (this.isLoggedIn) {
  498. this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  499. this.currentChildId = uni.getStorageSync('currentChildId') || null
  500. this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
  501. }
  502. this.loadSectionConfig()
  503. this.loadFeaturedArticles()
  504. this.loadDailyTip()
  505. if (this.isLoggedIn) {
  506. this.loadChildren()
  507. this.loadFamilyMembersVisible()
  508. this.loadSandboxData()
  509. this.loadEmiData()
  510. this.loadAssessmentData()
  511. this.loadReadingStats()
  512. this.loadFamilyCognitiveData()
  513. this.loadEnergyData()
  514. }
  515. // 游客也能浏览活动和商品
  516. this.loadDimensionActivities()
  517. this.loadDimensionProducts()
  518. },
  519. methods: {
  520. // ===== 区块可见性 =====
  521. loadSectionConfig: function() {
  522. var self = this
  523. var baseSections = ['func_entries', 'daily_tip', 'recommended_reading']
  524. if (!uni.getStorageSync('token')) {
  525. self.visibleSections = baseSections
  526. return
  527. }
  528. var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  529. getVisibleSections({ pageKey: 'mind', role: role }).then(function(res) {
  530. if (res.code === 200 && res.data) {
  531. var keys = res.data.map(function(s) { return s.sectionKey })
  532. var merged = baseSections.slice()
  533. keys.forEach(function(k) { if (merged.indexOf(k) === -1) merged.push(k) })
  534. self.visibleSections = merged
  535. }
  536. }).catch(function() {
  537. self.visibleSections = ['func_entries', 'daily_tip', 'recommended_reading', 'mind_tasks', 'related_activities', 'purchased_courses']
  538. })
  539. },
  540. sectionVisible: function(key) {
  541. return this.visibleSections.length === 0 || this.visibleSections.indexOf(key) !== -1
  542. },
  543. // ===== 心理 Tab:EMI 数据 =====
  544. loadEmiData: function() {
  545. var self = this
  546. var childId = self.currentChildId
  547. if (!childId) {
  548. self.emiLoading = false
  549. return
  550. }
  551. self.emiLoading = true
  552. getEmiReport(childId).then(function(res) {
  553. if (res.code === 200 && res.data) {
  554. self.emiData = res.data
  555. }
  556. self.emiLoading = false
  557. }).catch(function() {
  558. self.emiLoading = false
  559. })
  560. },
  561. // ===== 认知 Tab:测评数据 =====
  562. loadAssessmentData: function() {
  563. var self = this
  564. var childId = self.currentChildId
  565. if (!childId) {
  566. self.assessmentLoading = false
  567. return
  568. }
  569. self.assessmentLoading = true
  570. getAssessmentLatestResult(childId).then(function(res) {
  571. if (res.code === 200 && res.data) {
  572. self.assessmentData = res.data
  573. }
  574. self.assessmentLoading = false
  575. }).catch(function() {
  576. self.assessmentLoading = false
  577. })
  578. },
  579. // ===== 认知 Tab:六维认知数据 =====
  580. loadFamilyCognitiveData: function() {
  581. var self = this
  582. var token = uni.getStorageSync('token')
  583. var userId = uni.getStorageSync('userId') || ''
  584. if (!token) return
  585. uni.request({
  586. url: config.API_BASE_URL + '/api/cognitive/family',
  587. method: 'POST',
  588. header: {
  589. 'Content-Type': 'application/json',
  590. 'Authorization': 'Bearer ' + token,
  591. 'X-User-Id': userId
  592. },
  593. success: function(r) {
  594. if (r.statusCode === 200 && r.data && r.data.code === 200 && r.data.data) {
  595. var data = r.data.data
  596. if (Array.isArray(data) && data.length > 0) {
  597. self.familyMembers = data
  598. var first = data[0]
  599. self.selectedChildId = first.childId
  600. self.updateCurrentScores(first)
  601. }
  602. }
  603. }
  604. })
  605. },
  606. selectMember: function(childId) {
  607. if (childId === this.selectedChildId) return
  608. this.selectedChildId = childId
  609. var member = this.familyMembers.find(function(m) { return m.childId === childId })
  610. if (member) {
  611. this.updateCurrentScores(member)
  612. }
  613. },
  614. updateCurrentScores: function(member) {
  615. this.currentChildName = member.childName || ''
  616. this.currentScores = [
  617. member.perceptionScore || 0,
  618. member.focusScore || 0,
  619. member.memoryScore || 0,
  620. member.logicScore || 0,
  621. member.spatialScore || 0,
  622. member.processingSpeedScore || 0
  623. ]
  624. },
  625. goTraining: function(dimensionKey) {
  626. if (!this.selectedChildId) {
  627. uni.showToast({ title: '请先选择家庭成员', icon: 'none' })
  628. return
  629. }
  630. uni.navigateTo({ url: '/pages/mind/training?dimension=' + dimensionKey + '&childId=' + this.selectedChildId })
  631. },
  632. // ===== 每日心理 =====
  633. loadDailyTip: function() {
  634. var self = this
  635. getDailyTip().then(function(res) {
  636. if (res.code === 200 && res.data) {
  637. self.dailyTip = res.data
  638. }
  639. }).catch(function() {})
  640. },
  641. // ===== 阅读统计 =====
  642. loadReadingStats: function() {
  643. var self = this
  644. var childId = self.currentChildId
  645. if (!childId) return
  646. getReadingStats(childId).then(function(res) {
  647. if (res.code === 200 && res.data) {
  648. self.readingStats = res.data
  649. }
  650. }).catch(function() {})
  651. },
  652. // ===== 文章 =====
  653. loadFeaturedArticles: function() {
  654. var self = this
  655. getFeaturedArticles({ size: 5 }).then(function(res) {
  656. if (res.code === 200 && res.data) {
  657. var gradientColors = [
  658. 'linear-gradient(135deg, #8B5CF6, #A78BFA)',
  659. 'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
  660. 'linear-gradient(135deg, #10B981, #34D399)',
  661. 'linear-gradient(135deg, #8B5CF6, #C084FC)',
  662. 'linear-gradient(135deg, #F97316, #FB923C)'
  663. ]
  664. var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
  665. self.articles = list.map(function(item, index) {
  666. return {
  667. id: item.id,
  668. category: item.categoryName || '成长文章',
  669. categoryColor: gradientColors[index % gradientColors.length],
  670. title: item.title || '',
  671. summary: item.summary || '',
  672. views: item.viewCount ? String(item.viewCount) : '0',
  673. likes: '0',
  674. date: item.publishedAt ? item.publishedAt.slice(0, 10) : '',
  675. relatedDimensions: item.relatedDimensions || []
  676. }
  677. })
  678. }
  679. }).catch(function() {})
  680. },
  681. // ===== EMI 分数评级 =====
  682. getScoreLevel: function(score) {
  683. if (score >= 85) return 'excellent'
  684. if (score >= 70) return 'good'
  685. if (score >= 55) return 'average'
  686. return 'low'
  687. },
  688. getScoreLevelText: function(score) {
  689. if (score >= 85) return '优秀'
  690. if (score >= 70) return '良好'
  691. if (score >= 55) return '一般'
  692. return '需提升'
  693. },
  694. // ===== 导航 =====
  695. goEmotionReport: function() {
  696. var childId = this.currentChildId || ''
  697. uni.navigateTo({ url: '/pages/mind/emotion-report?childId=' + childId })
  698. },
  699. goCognitiveReport: function() {
  700. var childId = this.currentChildId || ''
  701. uni.navigateTo({ url: '/pages/mind/emotion-report?childId=' + childId })
  702. },
  703. goGame: function(gameCode) {
  704. if (!this.isLoggedIn) {
  705. uni.showModal({
  706. title: '提示',
  707. content: '请先登录后使用此功能',
  708. success: function(res) { if (res.confirm) uni.navigateTo({ url: '/pages/login/login' }) }
  709. })
  710. return
  711. }
  712. uni.navigateTo({ url: '/pages/games/' + gameCode })
  713. },
  714. goArticles: function() {
  715. uni.navigateTo({ url: '/pages/mind/articles' })
  716. },
  717. onFuncClick: function(item) {
  718. if (item.needLogin && !this.isLoggedIn) {
  719. uni.showModal({
  720. title: '提示',
  721. content: '请先登录后使用此功能',
  722. success: function(res) { if (res.confirm) uni.navigateTo({ url: '/pages/login/login' }) }
  723. })
  724. return
  725. }
  726. if (item.page) uni.navigateTo({ url: item.page })
  727. },
  728. goArticleDetail: function(id) {
  729. var url = '/pages/mind/article-detail?id=' + id
  730. uni.navigateTo({ url: url })
  731. },
  732. // ===== 孩子列表与维度数据 =====
  733. loadChildren: function() {
  734. var self = this
  735. getChildren().then(function(res) {
  736. if (res.code === 200 && res.data) {
  737. self.children = res.data
  738. var savedChildId = uni.getStorageSync('currentChildId')
  739. var found = false
  740. for (var i = 0; i < self.children.length; i++) {
  741. if (self.children[i].childId === savedChildId) {
  742. self.currentChildId = savedChildId
  743. found = true
  744. break
  745. }
  746. }
  747. if (!found && self.children.length > 0) {
  748. self.currentChildId = self.children[0].childId
  749. uni.setStorageSync('currentChildId', self.currentChildId)
  750. }
  751. if (self.currentChildId) {
  752. self.loadDimensionData()
  753. }
  754. }
  755. }).catch(function(e) {
  756. console.log('获取孩子列表失败', e)
  757. })
  758. },
  759. loadDimensionData: function() {
  760. this.loadDimensionTasks()
  761. this.loadDimensionActivities()
  762. this.loadDimensionProducts()
  763. },
  764. loadSandboxData: function() {
  765. var self = this
  766. getFamilyEnergySandbox().then(function(res) {
  767. if (res && res.data) {
  768. self.sandboxData = res.data
  769. }
  770. }).catch(function() {})
  771. },
  772. loadEnergyData: function() {
  773. var self = this
  774. if (!this.currentChildId) return
  775. getEnergyOverview(this.currentChildId).then(function(res) {
  776. if (res && res.data) {
  777. var dims = res.data.dimensions
  778. if (dims && dims.length > 0) {
  779. var mindDim = null
  780. var wisdomDim = null
  781. for (var i = 0; i < dims.length; i++) {
  782. if (dims[i].code === 'mind') mindDim = dims[i]
  783. if (dims[i].code === 'wisdom') wisdomDim = dims[i]
  784. }
  785. self.dualDimension = {
  786. primary: mindDim || { code: 'mind', score: 0, label: '心理', color: '#8B5CF6' },
  787. secondary: wisdomDim || { code: 'wisdom', score: 0, label: '认知', color: '#3B82F6' }
  788. }
  789. }
  790. }
  791. }).catch(function(e) {
  792. console.log('获取能量概览失败', e)
  793. })
  794. },
  795. loadDimensionTasks: function() {
  796. var self = this
  797. if (!this.currentChildId) return
  798. getTodayTasksByCategory(this.currentChildId, 'mind').then(function(res) {
  799. if (res && res.data) {
  800. self.dimensionTasks = Array.isArray(res.data) ? res.data.slice(0, 5) : []
  801. }
  802. }).catch(function() { self.dimensionTasks = [] })
  803. },
  804. loadDimensionActivities: function() {
  805. var self = this
  806. var dimCode = self.currentDimensionCode
  807. getActivityList({ dimensionCode: dimCode, page: 1, size: 3 }).then(function(res) {
  808. if (res && res.data) {
  809. var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
  810. self.dimensionActivities = list.slice(0, 3)
  811. }
  812. }).catch(function() { self.dimensionActivities = [] })
  813. },
  814. loadDimensionProducts: function() {
  815. var self = this
  816. var dimCode = self.currentDimensionCode
  817. getProductsByDomain(dimCode, 1, 4).then(function(res) {
  818. if (res && res.data) {
  819. var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
  820. self.dimensionProducts = list.slice(0, 4)
  821. }
  822. }).catch(function() { self.dimensionProducts = [] })
  823. },
  824. onChildChanged: function(child) {
  825. this.currentChildId = child.childId
  826. uni.setStorageSync('currentChildId', child.childId)
  827. this.loadDimensionData()
  828. this.loadEmiData()
  829. this.loadAssessmentData()
  830. this.loadReadingStats()
  831. },
  832. loadFamilyMembersVisible: function() {
  833. var self = this
  834. getVisibleFamilyMembers().then(function(res) {
  835. self.familyMembersVisible = res.data || []
  836. }).catch(function(e) {
  837. console.log('获取可见家庭成员失败', e)
  838. self.familyMembersVisible = []
  839. })
  840. },
  841. scrollToSection: function(section) {
  842. uni.pageScrollTo({ selector: '#' + section, duration: 300 })
  843. },
  844. onTaskClick: function(task) {
  845. uni.navigateTo({ url: '/pages/tasks/tasks' })
  846. },
  847. goActivityDetail: function(act) {
  848. if (act && act.id) uni.navigateTo({ url: '/pages/discover/index?type=activity&id=' + act.id })
  849. },
  850. goMoreActivities: function() {
  851. var dimCode = this.currentDimensionCode
  852. uni.navigateTo({ url: '/pages/discover/index?type=activity&dimension=' + dimCode })
  853. },
  854. goProductDetail: function(prod) {
  855. if (prod && prod.id) uni.navigateTo({ url: '/pages/discover/product-detail/product-detail?id=' + prod.id })
  856. },
  857. goMoreProducts: function() {
  858. var dimCode = this.currentDimensionCode
  859. uni.navigateTo({ url: '/pages/discover/index?type=product&domain=' + dimCode })
  860. },
  861. goTasks: function() { uni.switchTab({ url: '/pages/tasks/tasks' }) },
  862. goMemberDetail: function(member) {
  863. if (!member || !member.memberId) return
  864. uni.navigateTo({
  865. url: '/pages/mind/member-mind-detail?childId=' + member.memberId
  866. })
  867. },
  868. switchTab: function(tab) {
  869. if (tab === this.activeTab) return
  870. this.activeTab = tab
  871. this.loadDimensionActivities()
  872. this.loadDimensionProducts()
  873. },
  874. goWishlist: function() {
  875. uni.navigateTo({ url: '/pages/rewards/rewards' })
  876. },
  877. goAssessment: function() { uni.navigateTo({ url: '/pages/assessment/apply' }) },
  878. goLogin: function() { uni.navigateTo({ url: '/pages/login/login' }) }
  879. }
  880. }
  881. </script>
  882. <style scoped>
  883. .container {
  884. min-height: 100vh;
  885. background: #f5f7fa;
  886. padding-bottom: 120rpx;
  887. }
  888. /* ===== 心/智 Tab 栏 ===== */
  889. .tab-bar {
  890. display: flex;
  891. flex-direction: row;
  892. margin: 20rpx 30rpx;
  893. background: #fff;
  894. border-radius: 20rpx;
  895. padding: 8rpx;
  896. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  897. }
  898. .tab-item {
  899. flex: 1;
  900. display: flex;
  901. flex-direction: row;
  902. align-items: center;
  903. justify-content: center;
  904. padding: 20rpx 0;
  905. border-radius: 16rpx;
  906. transition: all 0.3s;
  907. }
  908. .tab-item.active {
  909. background: linear-gradient(135deg, #8B5CF6, #A78BFA);
  910. box-shadow: 0 4rpx 12rpx rgba(139,92,246,0.3);
  911. }
  912. .tab-icon {
  913. font-size: 28rpx;
  914. margin-right: 8rpx;
  915. }
  916. .tab-item.active .tab-icon {
  917. color: #fff;
  918. }
  919. .tab-label {
  920. font-size: 28rpx;
  921. font-weight: 500;
  922. color: #999;
  923. }
  924. .tab-item.active .tab-label {
  925. color: #fff;
  926. font-weight: bold;
  927. }
  928. /* ===== 心理能量概况 ===== */
  929. .energy-overview {
  930. display: flex;
  931. flex-direction: row;
  932. margin: 20rpx 30rpx;
  933. background: #fff;
  934. border-radius: 20rpx;
  935. padding: 24rpx 16rpx;
  936. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  937. justify-content: space-around;
  938. }
  939. .energy-badge {
  940. display: flex;
  941. flex-direction: column;
  942. align-items: center;
  943. flex: 1;
  944. }
  945. .badge-icon {
  946. font-size: 36rpx;
  947. margin-bottom: 8rpx;
  948. }
  949. .badge-value {
  950. font-size: 36rpx;
  951. font-weight: bold;
  952. color: #333;
  953. }
  954. .badge-label {
  955. font-size: 20rpx;
  956. color: #999;
  957. margin-top: 4rpx;
  958. }
  959. /* ===== EMI 四维条形图 ===== */
  960. .emi-section {
  961. margin: 20rpx 30rpx;
  962. }
  963. .emi-card {
  964. background: #fff;
  965. border-radius: 24rpx;
  966. padding: 30rpx;
  967. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
  968. }
  969. .emi-card-title {
  970. font-size: 30rpx;
  971. font-weight: bold;
  972. color: #333;
  973. margin-bottom: 24rpx;
  974. display: block;
  975. }
  976. .emi-bar-item {
  977. display: flex;
  978. flex-direction: row;
  979. align-items: center;
  980. margin-bottom: 20rpx;
  981. }
  982. .emi-bar-label {
  983. font-size: 24rpx;
  984. color: #666;
  985. width: 120rpx;
  986. flex-shrink: 0;
  987. }
  988. .emi-bar-track {
  989. flex: 1;
  990. height: 20rpx;
  991. background: #f0f0f0;
  992. border-radius: 10rpx;
  993. overflow: hidden;
  994. margin: 0 16rpx;
  995. }
  996. .emi-bar-fill {
  997. height: 100%;
  998. border-radius: 10rpx;
  999. transition: width 0.5s;
  1000. }
  1001. .emi-bar-score {
  1002. font-size: 26rpx;
  1003. font-weight: bold;
  1004. color: #333;
  1005. width: 60rpx;
  1006. text-align: right;
  1007. flex-shrink: 0;
  1008. }
  1009. .emi-bar-level {
  1010. font-size: 20rpx;
  1011. width: 80rpx;
  1012. text-align: right;
  1013. flex-shrink: 0;
  1014. }
  1015. .level-excellent { color: #10B981; }
  1016. .level-good { color: #3B82F6; }
  1017. .level-average { color: #F97316; }
  1018. .level-low { color: #EF4444; }
  1019. .emi-footer {
  1020. display: flex;
  1021. flex-direction: row;
  1022. justify-content: space-between;
  1023. align-items: center;
  1024. margin-top: 20rpx;
  1025. padding-top: 20rpx;
  1026. border-top: 1rpx solid #f0f0f0;
  1027. }
  1028. .emi-overall {
  1029. font-size: 28rpx;
  1030. font-weight: bold;
  1031. color: #FF6B35;
  1032. }
  1033. .emi-link {
  1034. display: flex;
  1035. flex-direction: row;
  1036. align-items: center;
  1037. }
  1038. .emi-link-text {
  1039. font-size: 26rpx;
  1040. color: #FF6B35;
  1041. font-weight: 500;
  1042. }
  1043. .emi-link-arrow {
  1044. font-size: 26rpx;
  1045. color: #FF6B35;
  1046. margin-left: 8rpx;
  1047. }
  1048. /* ===== 占位卡 ===== */
  1049. .placeholder-card {
  1050. margin: 30rpx;
  1051. background: #fff;
  1052. border-radius: 24rpx;
  1053. padding: 60rpx 40rpx;
  1054. display: flex;
  1055. flex-direction: column;
  1056. align-items: center;
  1057. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
  1058. }
  1059. .placeholder-icon {
  1060. font-size: 80rpx;
  1061. margin-bottom: 20rpx;
  1062. }
  1063. .placeholder-title {
  1064. font-size: 32rpx;
  1065. font-weight: bold;
  1066. color: #333;
  1067. margin-bottom: 16rpx;
  1068. }
  1069. .placeholder-desc {
  1070. font-size: 26rpx;
  1071. color: #999;
  1072. text-align: center;
  1073. line-height: 1.6;
  1074. margin-bottom: 30rpx;
  1075. }
  1076. .placeholder-btn {
  1077. background: linear-gradient(135deg, #FF6B35, #FFD700);
  1078. color: #fff;
  1079. font-size: 28rpx;
  1080. font-weight: bold;
  1081. padding: 16rpx 64rpx;
  1082. border-radius: 40rpx;
  1083. box-shadow: 0 4rpx 16rpx rgba(255,107,53,0.3);
  1084. }
  1085. /* ===== 认知总分明细 ===== */
  1086. .cognitive-score-highlight {
  1087. display: flex;
  1088. flex-direction: row;
  1089. margin: 20rpx 30rpx;
  1090. background: #fff;
  1091. border-radius: 24rpx;
  1092. padding: 30rpx 16rpx;
  1093. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
  1094. justify-content: space-around;
  1095. }
  1096. .score-big-cell {
  1097. display: flex;
  1098. flex-direction: column;
  1099. align-items: center;
  1100. flex: 1;
  1101. }
  1102. .score-big-label {
  1103. font-size: 22rpx;
  1104. color: #999;
  1105. margin-bottom: 10rpx;
  1106. }
  1107. .score-big-value {
  1108. font-size: 48rpx;
  1109. font-weight: bold;
  1110. color: #333;
  1111. }
  1112. .score-big-value.highlight {
  1113. color: #FFD700;
  1114. }
  1115. .score-big-value.small {
  1116. font-size: 22rpx;
  1117. color: #666;
  1118. }
  1119. /* ===== 认知评分卡 ===== */
  1120. .cognitive-overview {
  1121. margin: 20rpx 30rpx;
  1122. }
  1123. .cognitive-score-card {
  1124. background: #fff;
  1125. border-radius: 24rpx;
  1126. padding: 30rpx;
  1127. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
  1128. }
  1129. .score-row {
  1130. display: flex;
  1131. flex-direction: row;
  1132. justify-content: space-around;
  1133. margin-bottom: 24rpx;
  1134. }
  1135. .score-cell {
  1136. display: flex;
  1137. flex-direction: column;
  1138. align-items: center;
  1139. flex: 1;
  1140. }
  1141. .score-label {
  1142. font-size: 24rpx;
  1143. color: #999;
  1144. margin-bottom: 10rpx;
  1145. }
  1146. .score-value {
  1147. font-size: 40rpx;
  1148. font-weight: bold;
  1149. color: #333;
  1150. }
  1151. .score-value.highlight {
  1152. color: #FFD700;
  1153. }
  1154. .score-value.small {
  1155. font-size: 24rpx;
  1156. color: #666;
  1157. }
  1158. .score-actions {
  1159. display: flex;
  1160. flex-direction: row;
  1161. justify-content: space-between;
  1162. align-items: center;
  1163. border-top: 1rpx solid #f0f0f0;
  1164. padding-top: 20rpx;
  1165. }
  1166. .score-link {
  1167. display: flex;
  1168. flex-direction: row;
  1169. align-items: center;
  1170. background: linear-gradient(135deg, #667eea, #764ba2);
  1171. border-radius: 32rpx;
  1172. padding: 12rpx 24rpx;
  1173. }
  1174. .score-link.secondary {
  1175. background: #f0f0f0;
  1176. }
  1177. .score-link-text {
  1178. font-size: 24rpx;
  1179. color: #fff;
  1180. font-weight: 500;
  1181. }
  1182. .score-link.secondary .score-link-text {
  1183. color: #666;
  1184. }
  1185. .score-link-arrow {
  1186. font-size: 24rpx;
  1187. color: #fff;
  1188. margin-left: 8rpx;
  1189. }
  1190. /* ===== 雷达图区域 ===== */
  1191. .radar-section {
  1192. margin: 20rpx 30rpx;
  1193. }
  1194. .member-scroll {
  1195. width: 100%;
  1196. margin-bottom: 16rpx;
  1197. }
  1198. .member-row {
  1199. display: flex;
  1200. flex-direction: row;
  1201. padding: 4rpx 0;
  1202. white-space: nowrap;
  1203. }
  1204. .member-tab {
  1205. display: inline-flex;
  1206. flex-direction: column;
  1207. align-items: center;
  1208. padding: 16rpx 32rpx;
  1209. margin-right: 16rpx;
  1210. background: #fff;
  1211. border-radius: 16rpx;
  1212. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
  1213. min-width: 120rpx;
  1214. flex-shrink: 0;
  1215. }
  1216. .member-tab.active {
  1217. background: linear-gradient(135deg, #FF6B35, #FFD700);
  1218. box-shadow: 0 4rpx 16rpx rgba(255,107,53,0.3);
  1219. }
  1220. .member-tab.active .member-name {
  1221. color: #fff;
  1222. }
  1223. .member-tab.active .member-overall {
  1224. color: rgba(255,255,255,0.9);
  1225. }
  1226. .member-name {
  1227. font-size: 26rpx;
  1228. font-weight: bold;
  1229. color: #333;
  1230. }
  1231. .member-overall {
  1232. font-size: 20rpx;
  1233. color: #999;
  1234. margin-top: 4rpx;
  1235. }
  1236. .radar-card {
  1237. background: #fff;
  1238. border-radius: 24rpx;
  1239. padding: 30rpx 20rpx 20rpx;
  1240. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
  1241. display: flex;
  1242. flex-direction: column;
  1243. align-items: center;
  1244. }
  1245. .radar-hint {
  1246. font-size: 22rpx;
  1247. color: #bbb;
  1248. margin-top: 16rpx;
  1249. }
  1250. /* ===== 认知训练入口 ===== */
  1251. .game-grid {
  1252. display: flex;
  1253. flex-direction: row;
  1254. justify-content: space-around;
  1255. background: #fff;
  1256. border-radius: 20rpx;
  1257. padding: 24rpx 16rpx;
  1258. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1259. }
  1260. .game-item {
  1261. display: flex;
  1262. flex-direction: column;
  1263. align-items: center;
  1264. flex: 1;
  1265. }
  1266. .game-icon-wrap {
  1267. width: 80rpx;
  1268. height: 80rpx;
  1269. border-radius: 40rpx;
  1270. display: flex;
  1271. align-items: center;
  1272. justify-content: center;
  1273. margin-bottom: 10rpx;
  1274. }
  1275. .game-icon {
  1276. font-size: 40rpx;
  1277. }
  1278. .game-name {
  1279. font-size: 22rpx;
  1280. color: #666;
  1281. font-weight: 500;
  1282. }
  1283. .game-item:active {
  1284. opacity: 0.7;
  1285. }
  1286. /* ===== 阅读统计 ===== */
  1287. .reading-card {
  1288. background: #fff;
  1289. border-radius: 20rpx;
  1290. padding: 28rpx 24rpx;
  1291. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1292. }
  1293. .reading-stats {
  1294. display: flex;
  1295. flex-direction: row;
  1296. align-items: baseline;
  1297. margin-bottom: 16rpx;
  1298. }
  1299. .reading-count {
  1300. font-size: 36rpx;
  1301. font-weight: bold;
  1302. color: #FF6B35;
  1303. }
  1304. .reading-divider {
  1305. font-size: 28rpx;
  1306. color: #ccc;
  1307. margin: 0 8rpx;
  1308. }
  1309. .reading-duration {
  1310. font-size: 36rpx;
  1311. font-weight: bold;
  1312. color: #333;
  1313. }
  1314. .reading-progress-wrap {
  1315. display: flex;
  1316. flex-direction: row;
  1317. align-items: center;
  1318. margin-bottom: 16rpx;
  1319. }
  1320. .reading-progress-track {
  1321. flex: 1;
  1322. height: 16rpx;
  1323. background: #f0f0f0;
  1324. border-radius: 8rpx;
  1325. overflow: hidden;
  1326. margin-right: 16rpx;
  1327. }
  1328. .reading-progress-fill {
  1329. height: 100%;
  1330. background: linear-gradient(90deg, #FF6B35, #FFD700);
  1331. border-radius: 8rpx;
  1332. transition: width 0.5s;
  1333. }
  1334. .reading-progress-text {
  1335. font-size: 24rpx;
  1336. color: #999;
  1337. width: 80rpx;
  1338. text-align: right;
  1339. flex-shrink: 0;
  1340. }
  1341. .reading-link {
  1342. display: flex;
  1343. justify-content: flex-end;
  1344. }
  1345. .reading-link-text {
  1346. font-size: 26rpx;
  1347. color: #FF6B35;
  1348. font-weight: 500;
  1349. }
  1350. /* ===== 每日心理 ===== */
  1351. .tip-card {
  1352. background: #fff;
  1353. border-radius: 20rpx;
  1354. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1355. display: flex;
  1356. flex-direction: row;
  1357. overflow: hidden;
  1358. }
  1359. .tip-left-bar {
  1360. width: 6rpx;
  1361. background: linear-gradient(180deg, #5B9BD5, #8FC5E8);
  1362. flex-shrink: 0;
  1363. }
  1364. .tip-body {
  1365. flex: 1;
  1366. padding: 28rpx 24rpx;
  1367. }
  1368. .tip-title {
  1369. font-size: 28rpx;
  1370. font-weight: bold;
  1371. color: #5B9BD5;
  1372. display: block;
  1373. margin-bottom: 12rpx;
  1374. }
  1375. .tip-content {
  1376. font-size: 24rpx;
  1377. color: #666;
  1378. line-height: 1.6;
  1379. display: block;
  1380. margin-bottom: 16rpx;
  1381. }
  1382. .tip-footer {
  1383. display: flex;
  1384. flex-direction: row;
  1385. justify-content: flex-end;
  1386. }
  1387. .tip-source {
  1388. font-size: 22rpx;
  1389. color: #bbb;
  1390. }
  1391. /* ===== 通用区块 ===== */
  1392. .section {
  1393. margin: 20rpx 30rpx;
  1394. }
  1395. .section-header {
  1396. margin-bottom: 20rpx;
  1397. }
  1398. .section-title {
  1399. font-size: 30rpx;
  1400. font-weight: bold;
  1401. color: #333;
  1402. }
  1403. /* ===== 推荐阅读 ===== */
  1404. .article-list {
  1405. display: flex;
  1406. flex-direction: column;
  1407. }
  1408. .article-card {
  1409. background: #fff;
  1410. border-radius: 20rpx;
  1411. padding: 28rpx 24rpx;
  1412. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1413. margin-bottom: 20rpx;
  1414. }
  1415. .article-category {
  1416. display: inline-block;
  1417. padding: 4rpx 18rpx;
  1418. border-radius: 20rpx;
  1419. margin-bottom: 14rpx;
  1420. }
  1421. .article-category-text {
  1422. font-size: 20rpx;
  1423. color: #fff;
  1424. font-weight: 500;
  1425. }
  1426. .article-title {
  1427. font-size: 28rpx;
  1428. font-weight: bold;
  1429. color: #333;
  1430. display: block;
  1431. margin-bottom: 10rpx;
  1432. }
  1433. .article-summary {
  1434. font-size: 24rpx;
  1435. color: #888;
  1436. line-height: 1.5;
  1437. display: block;
  1438. margin-bottom: 20rpx;
  1439. }
  1440. .article-meta {
  1441. display: flex;
  1442. flex-direction: row;
  1443. align-items: center;
  1444. justify-content: space-between;
  1445. }
  1446. .meta-item {
  1447. display: flex;
  1448. flex-direction: row;
  1449. align-items: center;
  1450. margin-right: 20rpx;
  1451. }
  1452. .meta-icon {
  1453. font-size: 22rpx;
  1454. margin-right: 4rpx;
  1455. }
  1456. .meta-text {
  1457. font-size: 22rpx;
  1458. color: #bbb;
  1459. }
  1460. .article-date {
  1461. font-size: 22rpx;
  1462. color: #bbb;
  1463. margin-left: auto;
  1464. }
  1465. /* ===== 功能区入口 ===== */
  1466. .func-section {
  1467. margin: 20rpx 30rpx;
  1468. }
  1469. .func-grid {
  1470. display: flex;
  1471. flex-direction: row;
  1472. justify-content: space-between;
  1473. background: #fff;
  1474. border-radius: 20rpx;
  1475. padding: 24rpx 16rpx;
  1476. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1477. }
  1478. .func-item {
  1479. display: flex;
  1480. flex-direction: column;
  1481. align-items: center;
  1482. flex: 1;
  1483. }
  1484. .func-icon-wrap {
  1485. width: 80rpx;
  1486. height: 80rpx;
  1487. border-radius: 40rpx;
  1488. background: linear-gradient(135deg, #D6EAF8, #EBF2FA);
  1489. display: flex;
  1490. align-items: center;
  1491. justify-content: center;
  1492. margin-bottom: 10rpx;
  1493. }
  1494. .func-icon {
  1495. font-size: 40rpx;
  1496. }
  1497. .func-label {
  1498. font-size: 22rpx;
  1499. color: #666;
  1500. font-weight: 500;
  1501. }
  1502. .func-item:active {
  1503. opacity: 0.7;
  1504. }
  1505. /* ===== 底部 ===== */
  1506. .bottom-spacer {
  1507. height: 120rpx;
  1508. }
  1509. /* ===== 心愿单入口 ===== */
  1510. .wishlist-card {
  1511. background: #fff;
  1512. border-radius: 20rpx;
  1513. padding: 28rpx 24rpx;
  1514. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1515. display: flex;
  1516. flex-direction: row;
  1517. align-items: center;
  1518. }
  1519. .wishlist-left {
  1520. width: 72rpx;
  1521. height: 72rpx;
  1522. background: linear-gradient(135deg, #FF6B35, #FFD700);
  1523. border-radius: 50%;
  1524. display: flex;
  1525. align-items: center;
  1526. justify-content: center;
  1527. margin-right: 20rpx;
  1528. flex-shrink: 0;
  1529. }
  1530. .wishlist-icon {
  1531. font-size: 36rpx;
  1532. }
  1533. .wishlist-body {
  1534. flex: 1;
  1535. }
  1536. .wishlist-title {
  1537. font-size: 28rpx;
  1538. font-weight: bold;
  1539. color: #333;
  1540. display: block;
  1541. margin-bottom: 4rpx;
  1542. }
  1543. .wishlist-desc {
  1544. font-size: 22rpx;
  1545. color: #999;
  1546. }
  1547. .wishlist-arrow {
  1548. font-size: 36rpx;
  1549. color: #ccc;
  1550. margin-left: 12rpx;
  1551. }
  1552. </style>