index.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <template>
  2. <view class="container">
  3. <view v-if="!isLoggedIn">
  4. <!-- 品牌定位语 -->
  5. <view class="brand-strap"><text class="brand-strap-text">看见改变正在发生</text></view>
  6. <!-- 理念卡 -->
  7. <view class="concept-card">
  8. <text class="concept-quote">记录不是目的,看见改变才是。</text>
  9. <text class="concept-desc">每一份饮食、运动、心情的记录,都在为成长提供依据。</text>
  10. </view>
  11. <!-- 示例案例 -->
  12. <view class="case-card">
  13. <view class="case-header">
  14. <text class="case-label">📖 真实案例(脱敏)</text>
  15. </view>
  16. <view class="case-body">
  17. <text class="case-name">小明(10岁,体校生)</text>
  18. <text class="case-change">3个月干预前后:</text>
  19. <view class="case-metrics">
  20. <view class="metric-item">
  21. <text class="metric-up">↑ 5分</text>
  22. <text class="metric-label">身维度</text>
  23. </view>
  24. <view class="metric-item">
  25. <text class="metric-up">↑ 3分</text>
  26. <text class="metric-label">智维度</text>
  27. </view>
  28. <view class="metric-item">
  29. <text class="metric-up">↑ 8天</text>
  30. <text class="metric-label">连续打卡</text>
  31. </view>
  32. </view>
  33. <text class="case-note">通过饮食记录 + 睡眠打卡,免疫力显著提升</text>
  34. </view>
  35. </view>
  36. <!-- 功能预览 -->
  37. <view class="guest-features">
  38. <view class="feat-card" @click="handleGuestFeatureTap">
  39. <text class="feat-icon">✅</text>
  40. <text class="feat-title">每日打卡</text>
  41. <text class="feat-desc">饮食·运动·睡眠·心情</text>
  42. </view>
  43. <view class="feat-card" @click="handleGuestFeatureTap">
  44. <text class="feat-icon">🏆</text>
  45. <text class="feat-title">里程碑</text>
  46. <text class="feat-desc">连续打卡·挑战完成</text>
  47. </view>
  48. <view class="feat-card" @click="handleGuestFeatureTap">
  49. <text class="feat-icon">🎯</text>
  50. <text class="feat-title">家庭挑战</text>
  51. <text class="feat-desc">全家参与的互动任务</text>
  52. </view>
  53. <view class="feat-card" @click="handleGuestFeatureTap">
  54. <text class="feat-icon">📋</text>
  55. <text class="feat-title">任务中心</text>
  56. <text class="feat-desc">执行任务·完成挑战</text>
  57. </view>
  58. </view>
  59. <!-- 五维健康体系 -->
  60. <view class="wuxing-section">
  61. <view class="wuxing-title">身·心·智·行·富 · 可执行的成长闭环</view>
  62. <view class="wuxing-desc">五行相生相克,动起来、坚持做,让成长真正发生</view>
  63. <view class="wuxing-relations">
  64. <view class="rel-group">
  65. <text class="rel-title">相生 → 系统前进</text>
  66. <view class="rel-chain">
  67. <text class="rel-item">🌏 身</text><text class="rel-arrow">→</text>
  68. <text class="rel-item">⚡ 智</text><text class="rel-arrow">→</text>
  69. <text class="rel-item">💧 富</text><text class="rel-arrow">→</text>
  70. <text class="rel-item">🌿 行</text><text class="rel-arrow">→</text>
  71. <text class="rel-item">🔥 心</text><text class="rel-arrow">→</text>
  72. <text class="rel-item">🌏 身</text>
  73. </view>
  74. <text class="rel-hint">身勤则强,智勤则开,富勤则聚,行勤则和,心勤则安</text>
  75. </view>
  76. <view class="rel-group">
  77. <text class="rel-title">相克 → 系统平衡</text>
  78. <view class="rel-chain">
  79. <text class="rel-item">🌏 身</text><text class="rel-op">克</text><text class="rel-item">💧 富</text><text class="rel-dot">·</text>
  80. <text class="rel-item">⚡ 智</text><text class="rel-op">克</text><text class="rel-item">🌿 行</text><text class="rel-dot">·</text>
  81. <text class="rel-item">💧 富</text><text class="rel-op">克</text><text class="rel-item">🔥 心</text><text class="rel-dot">·</text>
  82. <text class="rel-item">🌿 行</text><text class="rel-op">克</text><text class="rel-item">🌏 身</text><text class="rel-dot">·</text>
  83. <text class="rel-item">🔥 心</text><text class="rel-op">克</text><text class="rel-item">⚡ 智</text>
  84. </view>
  85. <text class="rel-hint">不拿身体换快钱,不算计关系得失,财富服务家庭,关爱有度、不溺爱越界</text>
  86. </view>
  87. </view>
  88. <view class="wuxing-cards">
  89. <view class="wc-card wc-body" @click="goPage('/pages/body-detail/index')">
  90. <text class="wc-icon">🌏</text>
  91. <view class="wc-info">
  92. <text class="wc-name">身 · 主动健康</text>
  93. <text class="wc-text">每天动一动、睡得香,身体状态看得见地变好</text>
  94. </view>
  95. </view>
  96. <view class="wc-card wc-wisdom" @click="goPage('/pages/wisdom-detail/index')">
  97. <text class="wc-icon">⚡</text>
  98. <view class="wc-info">
  99. <text class="wc-name">智 · 赋能未来</text>
  100. <text class="wc-text">每天读几页、练一会专注,智慧是练出来的</text>
  101. </view>
  102. </view>
  103. <view class="wc-card wc-wealth" @click="goPage('/pages/wealth/index')">
  104. <text class="wc-icon">💧</text>
  105. <view class="wc-info">
  106. <text class="wc-name">富 · 利他创富</text>
  107. <text class="wc-text">坚持分享与积累,把第二收入曲线做出来</text>
  108. </view>
  109. </view>
  110. <view class="wc-card wc-action" @click="goPage('/pages/action-detail/member-action-detail')">
  111. <text class="wc-icon">🌿</text>
  112. <view class="wc-info">
  113. <text class="wc-name">行 · 人际和谐</text>
  114. <text class="wc-text">从一次倾听开始,让家庭关系每天升温</text>
  115. </view>
  116. </view>
  117. <view class="wc-card wc-mind" @click="goPage('/pages/mind-detail/index')">
  118. <text class="wc-icon">🔥</text>
  119. <view class="wc-info">
  120. <text class="wc-name">心 · 大爱相随</text>
  121. <text class="wc-text">每天留 10 分钟说说话,把家人的心事接住</text>
  122. </view>
  123. </view>
  124. </view>
  125. </view>
  126. <!-- 登录引导 -->
  127. <view class="login-guide-section">
  128. <LoginGuideCard
  129. title="解锁完整成长档案"
  130. :items="[
  131. '查看干预进度与追踪记录',
  132. '完成每日打卡累积连续记录',
  133. '参与家庭挑战获得里程碑奖励'
  134. ]" />
  135. </view>
  136. </view>
  137. <view v-else>
  138. <view class="reminder-bar" v-if="hasIncomplete">
  139. <text class="reminder-text">⚠ 今日还有 {{ incompleteCount }} 项未记录</text>
  140. <text class="reminder-close" @click="dismissReminder">✕</text>
  141. </view>
  142. <!-- 里程碑(最上) -->
  143. <view class="section">
  144. <view class="milestone-card">
  145. <text class="section-title-row">🏆 里程碑</text>
  146. <view class="milestone-grid">
  147. <view class="milestone-item">
  148. <text class="milestone-icon">🔥</text>
  149. <text class="milestone-value">{{ streakDays }}天</text>
  150. <text class="milestone-label">连续打卡</text>
  151. </view>
  152. <view class="milestone-item">
  153. <text class="milestone-icon">🎯</text>
  154. <text class="milestone-value">{{ activeChallenges }}</text>
  155. <text class="milestone-label">进行中挑战</text>
  156. </view>
  157. <view class="milestone-item">
  158. <text class="milestone-icon">📈</text>
  159. <text class="milestone-value">{{ completeness }}%</text>
  160. <text class="milestone-label">档案完整度</text>
  161. </view>
  162. </view>
  163. </view>
  164. </view>
  165. <!-- 今日成长 -->
  166. <view class="section">
  167. <view class="progress-card">
  168. <view class="progress-header">
  169. <text class="progress-title">今日成长</text>
  170. <text class="progress-date">{{ todayDate }}</text>
  171. </view>
  172. <view class="progress-grid" v-if="progressItems.length > 0">
  173. <view class="progress-grid-item" v-for="(item, idx) in progressItems" :key="getProgressKey(item)" @click="handleProgressClick(idx)">
  174. <text class="progress-grid-icon">{{ item.icon }}</text>
  175. <text class="progress-grid-label">{{ item.label }}</text>
  176. <text class="progress-grid-status" :class="item.done ? 'status-done' : 'status-pending'">{{ item.done ? '✔' : (item.actionText || '去记录') }}</text>
  177. </view>
  178. </view>
  179. <view class="progress-footer">
  180. <text class="footer-text">今日完成 {{ doneCount }}/{{ totalCount }}</text>
  181. <text class="footer-streak" v-if="streakDays > 0">🔥 连续打卡 {{ streakDays }} 天</text>
  182. </view>
  183. </view>
  184. </view>
  185. <view class="section" v-if="cautionCount > 0">
  186. <view class="action-card" @click="goToCaution">
  187. <text class="action-icon">⚠️</text>
  188. <view class="action-info">
  189. <text class="action-title">食材戒备匹配</text>
  190. <text class="action-desc">{{ cautionCount }} 种食材需关注,点击查看戒备详情</text>
  191. </view>
  192. <text class="action-arrow">›</text>
  193. </view>
  194. </view>
  195. <!-- 饮食推荐 -->
  196. <view class="section">
  197. <view class="action-card" @click="goToFoods">
  198. <text class="action-icon">🍽️</text>
  199. <view class="action-info">
  200. <text class="action-title">饮食推荐</text>
  201. <text class="action-desc">选同住人,获取今日个性化饮食建议</text>
  202. </view>
  203. <text class="action-arrow">›</text>
  204. </view>
  205. </view>
  206. <!-- 任务中心(内联) -->
  207. <view class="section" v-if="recentTasks.length > 0">
  208. <view class="task-preview-card">
  209. <view class="task-preview-header">
  210. <text class="task-preview-title">📋 今日任务</text>
  211. <text class="task-preview-more" @click="goToTasks">更多 ›</text>
  212. </view>
  213. <view class="task-preview-list">
  214. <view class="task-preview-item" v-for="(task, idx) in recentTasks" :key="getTaskKey(task, idx)" @click="goToTasks">
  215. <view class="task-preview-left">
  216. <text class="task-preview-name">{{ task.title }}</text>
  217. <view class="task-preview-meta">
  218. <text class="task-preview-points">+{{ task.points }}分</text>
  219. <text class="task-preview-deadline" v-if="task.deadline">⏱ {{ formatTaskTime(task.deadline) }}</text>
  220. </view>
  221. </view>
  222. <text class="task-preview-arrow">›</text>
  223. </view>
  224. </view>
  225. </view>
  226. </view>
  227. <DimensionActivities :isLoggedIn="true" @activityClick="goActivityDetail" @moreActivities="goMoreActivities" style="margin-bottom:60rpx" />
  228. <!-- 推荐阅读 -->
  229. <view class="section" v-if="recommendArticles.length > 0">
  230. <view class="section-header">
  231. <text class="section-title">📖 推荐阅读</text>
  232. <text class="section-more" @click="goMoreArticles">更多 ›</text>
  233. </view>
  234. <ArticleBookshelf :articles="recommendArticles" :isLoggedIn="true" @articleClick="goToArticle" />
  235. </view>
  236. <!-- 推荐商品(放最底部) -->
  237. <view class="section">
  238. <view class="section-header">
  239. <text class="section-title">🛍️ 推荐商品</text>
  240. <text class="section-more" @click="goMoreProducts">更多 ›</text>
  241. </view>
  242. <DimensionProducts :isLoggedIn="true" :showMore="false" @productClick="goToProduct" />
  243. </view>
  244. <view class="bottom-spacer" style="height: 40rpx;"></view>
  245. </view>
  246. </view>
  247. </template>
  248. <script>
  249. import LoginGuideCard from '../../components/LoginGuideCard.vue'
  250. import DimensionActivities from '../../components/DimensionActivities.vue'
  251. import DimensionProducts from '../../components/DimensionProducts.vue'
  252. import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
  253. import { healthCheckinList, getHealthMealList, getFoodCautionList, getGrowthRecommendations, getExerciseRecords, getSleepRecords, getEmotionCheckinList, getCheckinList, getTodayTasks, reportAppEnter
  254. } from '../../utils/api.js'
  255. import { parseDate } from '../../utils/format.js'
  256. export default {
  257. components: { LoginGuideCard, DimensionActivities, DimensionProducts, ArticleBookshelf },
  258. data() { return { memberId: '', todayDate: '', hasIncomplete: false, incompleteCount: 0, progressItems: [], recentTasks: [], streakDays: 0, activeChallenges: 0, completeness: 0, cautionCount: 0,
  259. healthArticles: [],
  260. growthArticles: [],
  261. isLoggedIn: false } },
  262. computed: {
  263. doneCount: function() { var c = 0; for (var i = 0; i < this.progressItems.length; i++) if (this.progressItems[i] && this.progressItems[i].done) c++; return c },
  264. totalCount: function() { return this.progressItems.length },
  265. recommendArticles: function() {
  266. var arr = []
  267. if (this.healthArticles && this.healthArticles.length > 0) arr = arr.concat(this.healthArticles)
  268. if (this.growthArticles && this.growthArticles.length > 0) arr = arr.concat(this.growthArticles)
  269. return arr
  270. }
  271. },
  272. onShow() {
  273. reportAppEnter('growth');
  274. this.isLoggedIn = !!uni.getStorageSync('token')
  275. if (this.isLoggedIn) {
  276. this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || ''
  277. var now = new Date()
  278. this.todayDate = now.getFullYear() + '-' + this.pad(now.getMonth() + 1) + '-' + this.pad(now.getDate())
  279. this.loadAllData()
  280. }
  281. },
  282. methods: {
  283. pad: function(n) { return n < 10 ? '0' + n : '' + n },
  284. getProgressKey: function(item) { return item && item.label ? item.label : 'p-' + Math.random() },
  285. dismissReminder: function() { this.hasIncomplete = false },
  286. handleProgressClick: function(idx) {
  287. var item = this.progressItems[idx]
  288. if (!item || !item.key) return
  289. var m = { diet: '/pages/growth/diet-checkin', exercise: '/pages/growth/exercise-checkin', sleep: '/pages/growth/sleep-checkin', emotion: '/pages/mind-detail/emotion-checkin', checkin: '/pages/wealth-sub/checkin' }
  290. var url = m[item.key]
  291. // 情绪打卡已合并至 mind-detail 情绪打卡页,成长上下文需带上 memberId
  292. if (url && item.key === 'emotion' && this.memberId) url = url + '?memberId=' + this.memberId
  293. if (url) uni.navigateTo({ url: url })
  294. },
  295. getTaskKey: function(task, idx) { return (task && task.id) ? ('t' + task.id) : ('tk' + idx) },
  296. formatTaskTime: function(dateVal) {
  297. if (!dateVal) return ''
  298. var d = parseDate(dateVal)
  299. if (!d) return ''
  300. return (d.getMonth() + 1) + '/' + d.getDate() + ' ' + this.pad(d.getHours()) + ':' + this.pad(d.getMinutes())
  301. },
  302. async loadRecentTasks() {
  303. var self = this
  304. try {
  305. var res = await getTodayTasks(self.memberId ? { memberId: self.memberId } : {})
  306. if (res && res.code === 200 && res.data) {
  307. self.recentTasks = (res.data || []).slice(0, 3)
  308. }
  309. } catch(e) {}
  310. },
  311. goActivityDetail: function(act) { uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id }) },
  312. goMoreActivities: function() { uni.navigateTo({ url: '/pages/activity/index' }) },
  313. goToTasks: function() { uni.navigateTo({ url: '/pages/child/tasks' }) },
  314. handleGuestFeatureTap: function() { uni.showToast({ title: '登录后即可体验该功能', icon: 'none' }) },
  315. goToCaution: function() {
  316. var mid = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || ''
  317. uni.navigateTo({ url: '/pages/diet/food-query' + (mid ? '?memberId=' + mid : '') })
  318. },
  319. goToFoods: function() {
  320. uni.navigateTo({ url: '/pages/diet/food-query' })
  321. },
  322. goToArticle: function(article) { var id = article && article.id ? article.id : article; if (!id) return; uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id }) },
  323. goToProduct: function(prod) { var id = prod && prod.id ? prod.id : prod; if (!id) return; uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id }) },
  324. goMoreProducts: function() { uni.navigateTo({ url: '/pages/shop/index/index' }) },
  325. goMoreArticles: function() { uni.navigateTo({ url: '/pages/article-center/index?dimension=action' }) },
  326. goPage: function(url) { uni.navigateTo({ url: url }) },
  327. formatDateKey: function(dateVal) {
  328. if (!dateVal) return ''
  329. var d = parseDate(dateVal)
  330. if (!d) return ''
  331. return d.getFullYear() + '-' + this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
  332. },
  333. formatRecordDate: function(dateVal) {
  334. if (!dateVal) return ''
  335. var d = parseDate(dateVal)
  336. if (!d) return ''
  337. return this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
  338. },
  339. mealTypeLabel: function(type) {
  340. var m = { breakfast: '早餐', lunch: '午餐', dinner: '晚餐', snack: '加餐', extra: '零食' }
  341. return m[type] || type || '饮食'
  342. },
  343. async loadAllData() {
  344. var self = this
  345. try { await self.loadProgressItems() } catch(e) {}
  346. try { await self.loadRecentTasks() } catch(e) {}
  347. try { await self.loadCautionData() } catch(e) {}
  348. try { await self.loadGrowthRecommendations() } catch(e) {}
  349. try { await self.loadMilestones() } catch(e) {}
  350. },
  351. async loadProgressItems() {
  352. var self = this
  353. var items = []
  354. var dietDone = false
  355. try {
  356. var dParams = self.memberId ? { memberId: self.memberId } : {}
  357. var dRes = await getHealthMealList(dParams)
  358. var dList = (dRes.code === 200 && dRes.data) ? dRes.data : []
  359. for (var di = 0; di < dList.length; di++) {
  360. var dr = dList[di]
  361. if (!dr) continue
  362. if (self.formatDateKey(dr.createdAt || dr.recordDate) === self.todayDate) dietDone = true
  363. }
  364. } catch(e) {}
  365. items.push({ key: 'diet', icon: '🍽️', label: '饮食', done: dietDone, actionText: '补录' })
  366. var exerciseDone = false
  367. try {
  368. var eParams = self.memberId ? { memberId: self.memberId } : {}
  369. var eRes = await getExerciseRecords(eParams)
  370. var eList = (eRes.code === 200 && eRes.data) ? eRes.data : []
  371. for (var ei = 0; ei < eList.length; ei++) {
  372. var er = eList[ei]
  373. if (!er) continue
  374. if (self.formatDateKey(er.startTime || er.createdAt) === self.todayDate) exerciseDone = true
  375. }
  376. } catch(e) {}
  377. items.push({ key: 'exercise', icon: '🏃', label: '运动', done: exerciseDone, actionText: '记录' })
  378. var sleepDone = false
  379. try {
  380. var sParams = self.memberId ? { memberId: self.memberId } : {}
  381. var sRes = await getSleepRecords(sParams)
  382. var sList = (sRes.code === 200 && sRes.data) ? sRes.data : []
  383. for (var si = 0; si < sList.length; si++) {
  384. var sr = sList[si]
  385. if (!sr) continue
  386. if (self.formatDateKey(sr.sleepTime || sr.createdAt) === self.todayDate) sleepDone = true
  387. }
  388. } catch(e) {}
  389. items.push({ key: 'sleep', icon: '😴', label: '睡眠', done: sleepDone, actionText: '记录' })
  390. var emotionDone = false
  391. try {
  392. var mParams = { page: 1, size: 10 }
  393. if (self.memberId) mParams.memberId = self.memberId
  394. var mRes = await getEmotionCheckinList(mParams)
  395. var mList = (mRes.code === 200 && mRes.data) ? mRes.data : []
  396. for (var mi = 0; mi < mList.length; mi++) {
  397. var mr = mList[mi]
  398. if (!mr) continue
  399. if (self.formatDateKey(mr.checkinDate || mr.createdAt) === self.todayDate) emotionDone = true
  400. }
  401. } catch(e) {}
  402. items.push({ key: 'emotion', icon: '😊', label: '心情', done: emotionDone, actionText: '记录' })
  403. var checkinDone = false
  404. try {
  405. var cParams = self.memberId ? { memberId: self.memberId } : {}
  406. var cRes = await getCheckinList(cParams)
  407. var cList = (cRes.code === 200 && cRes.data) ? cRes.data : []
  408. for (var ci = 0; ci < cList.length; ci++) {
  409. if (cList[ci] && cList[ci].checkinDate && cList[ci].checkinDate.indexOf(self.todayDate) === 0) {
  410. checkinDone = true
  411. break
  412. }
  413. }
  414. } catch(e) {}
  415. items.push({ key: 'checkin', icon: '💰', label: '记账', done: checkinDone, actionText: '去打卡' })
  416. self.progressItems = items.filter(function(it) { return it && it.key })
  417. self.incompleteCount = 0
  418. for (var pi = 0; pi < items.length; pi++) if (items[pi] && !items[pi].done) self.incompleteCount++
  419. self.hasIncomplete = self.incompleteCount > 0
  420. },
  421. async loadGrowthRecommendations() {
  422. var self = this
  423. try {
  424. var res = await getGrowthRecommendations('health', 6)
  425. if (res && res.code === 200 && res.data) {
  426. var arts = res.data.articles || []
  427. self.healthArticles = arts.filter(function(a){ return a.growthCategory === 'health' })
  428. self.growthArticles = arts.filter(function(a){ return a.growthCategory === 'growth' })
  429. }
  430. } catch(e) {}
  431. },
  432. async loadCautionData() {
  433. var self = this
  434. try {
  435. var res = await getFoodCautionList(self.memberId)
  436. if (res && res.code === 200) self.cautionCount = (res.data || []).length
  437. } catch(e) {}
  438. },
  439. async loadMilestones() {
  440. var self = this
  441. try {
  442. var ciRes = await healthCheckinList({})
  443. if (ciRes.code === 200 && ciRes.data) self.streakDays = Math.min(ciRes.data.length, 30)
  444. } catch(e) {}
  445. self.activeChallenges = 0
  446. self.completeness = self.streakDays > 0 ? Math.min(100, Math.round(self.streakDays * 10)) : 0
  447. }
  448. }
  449. }
  450. </script>
  451. <style scoped>
  452. .container{background:#FFF7ED;min-height:100vh;padding-bottom:calc(160rpx + env(safe-area-inset-bottom))}
  453. .section{margin:16rpx 24rpx}
  454. /* 游客态 */
  455. .brand-strap{margin:0 24rpx 16rpx;background:linear-gradient(135deg,#F0FFF4,rgba(16,185,129,0.1));border-radius:16rpx;padding:20rpx 24rpx}
  456. .brand-strap-text{font-size:26rpx;color:#10B981;font-weight:600}
  457. .concept-card{margin:0 24rpx 16rpx;background:#fff;border-radius:20rpx;padding:28rpx;border-left:6rpx solid #10B981;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  458. .concept-quote{font-size:30rpx;font-weight:bold;color:#333;display:block;margin-bottom:8rpx}
  459. .concept-desc{font-size:24rpx;color:#888;display:block;line-height:1.6}
  460. .case-card{margin:0 24rpx 16rpx;background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  461. .case-header{margin-bottom:12rpx}
  462. .case-label{font-size:24rpx;color:#10B981;font-weight:bold}
  463. .case-body{padding:8rpx 0}
  464. .case-name{font-size:26rpx;color:#333;font-weight:600;display:block;margin-bottom:6rpx}
  465. .case-change{font-size:24rpx;color:#666;display:block;margin-bottom:12rpx}
  466. .case-metrics{display:flex;gap:16rpx;margin-bottom:12rpx}
  467. .metric-item{display:flex;flex-direction:column;align-items:center;flex:1;background:#F0FFF4;border-radius:12rpx;padding:12rpx 8rpx}
  468. .metric-up{font-size:26rpx;font-weight:bold;color:#10B981}
  469. .metric-label{font-size:20rpx;color:#999;margin-top:4rpx}
  470. .case-note{font-size:22rpx;color:#888;display:block}
  471. .guest-features{display:flex;flex-wrap:wrap;justify-content:space-between;margin:0 24rpx 16rpx}
  472. .feat-card{width:calc(50% - 8rpx);box-sizing:border-box;background:#fff;border-radius:20rpx;padding:24rpx;margin-bottom:16rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  473. .feat-icon{font-size:40rpx;display:block;margin-bottom:8rpx}
  474. .feat-title{font-size:26rpx;font-weight:600;color:#333;display:block;margin-bottom:4rpx}
  475. .feat-desc{font-size:22rpx;color:#999;display:block}
  476. .login-guide-section{margin:0 24rpx 24rpx}
  477. /* 成长行动卡片 */
  478. .action-card{display:flex;align-items:center;padding:24rpx 28rpx;background:#fff;border-radius:16rpx;margin:0 24rpx 16rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  479. .action-card:active{opacity:0.85}
  480. .action-icon{font-size:44rpx;margin-right:20rpx}
  481. .action-info{flex:1}
  482. .action-title{font-size:28rpx;font-weight:600;color:#333;display:block}
  483. .action-desc{font-size:22rpx;color:#888;margin-top:4rpx;display:block}
  484. .action-arrow{font-size:32rpx;color:#ccc}
  485. /* section-header 组件通用样式 */
  486. .section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12rpx}
  487. .section-title{font-size:28rpx;font-weight:600;color:#333}
  488. .section-more{font-size:24rpx;color:#F97316}
  489. /* 登录后 */
  490. .reminder-bar{margin:12rpx 24rpx;background:#FFF3E0;border:1rpx solid #FFB74D;border-radius:12rpx;padding:14rpx 20rpx;display:flex;align-items:center;justify-content:space-between}
  491. .reminder-text{font-size:24rpx;color:#E65100}
  492. .reminder-close{font-size:28rpx;color:#999}
  493. /* 里程碑 */
  494. .milestone-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  495. .section-title-row{font-size:26rpx;font-weight:bold;color:#333;margin-bottom:16rpx;display:block}
  496. .milestone-grid{display:flex;justify-content:space-between}
  497. .milestone-item{display:flex;flex-direction:column;align-items:center;flex:1}
  498. .milestone-icon{font-size:40rpx;margin-bottom:4rpx}
  499. .milestone-value{font-size:28rpx;font-weight:bold;color:#F97316;margin-bottom:2rpx}
  500. .milestone-label{font-size:22rpx;color:#999}
  501. /* 今日成长 */
  502. .progress-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  503. .progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16rpx}
  504. .progress-title{font-size:28rpx;font-weight:bold;color:#333}
  505. .progress-date{font-size:22rpx;color:#999}
  506. .progress-grid{display:flex;justify-content:space-between}
  507. .progress-grid-item{flex:1;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20rpx 8rpx;background:#FFF7ED;border-radius:16rpx;margin-right:12rpx}
  508. .progress-grid-item:last-child{margin-right:0}
  509. .progress-grid-item:active{opacity:0.85;background:#FFEDD5}
  510. .progress-grid-icon{font-size:40rpx;margin-bottom:8rpx}
  511. .progress-grid-label{font-size:24rpx;color:#333;margin-bottom:8rpx;text-align:center}
  512. .progress-grid-status{font-size:20rpx;flex-shrink:0}
  513. .status-done{color:#10B981}
  514. .status-pending{color:#ccc}
  515. .progress-footer{display:flex;justify-content:space-between;align-items:center;margin-top:16rpx;padding-top:16rpx;border-top:1rpx solid #f5f5f5}
  516. .footer-text{font-size:24rpx;color:#666;font-weight:bold}
  517. .footer-streak{font-size:24rpx;color:#F97316;font-weight:bold}
  518. /* 任务中心预览 */
  519. .task-preview-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  520. .task-preview-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16rpx}
  521. .task-preview-title{font-size:28rpx;font-weight:bold;color:#333}
  522. .task-preview-more{font-size:24rpx;color:#F97316}
  523. .task-preview-list{display:flex;flex-direction:column}
  524. .task-preview-item{display:flex;align-items:center;padding:16rpx 0;border-bottom:1rpx solid #f5f5f5}
  525. .task-preview-item:last-child{border-bottom:none}
  526. .task-preview-left{flex:1;min-width:0}
  527. .task-preview-name{font-size:26rpx;color:#333;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  528. .task-preview-meta{display:flex;align-items:center;gap:12rpx;margin-top:6rpx}
  529. .task-preview-points{font-size:22rpx;color:#F97316;font-weight:bold}
  530. .task-preview-deadline{font-size:20rpx;color:#999}
  531. .task-preview-arrow{font-size:28rpx;color:#ccc;flex-shrink:0;margin-left:12rpx}
  532. /* ===== 五维闭环体系样式 ===== */
  533. .wuxing-section { margin: 0 24rpx 16rpx; background: #fff; border-radius: 20rpx; padding: 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
  534. .wuxing-title { font-size: 30rpx; font-weight: bold; color: #333; text-align: center; margin-bottom: 6rpx; }
  535. .wuxing-desc { font-size: 22rpx; color: #999; text-align: center; margin-bottom: 20rpx; }
  536. .wuxing-relations { margin-top: 8rpx; }
  537. .rel-group { margin-bottom: 16rpx; }
  538. .rel-title { font-size: 24rpx; font-weight: bold; color: #333; margin-bottom: 8rpx; display: block; }
  539. .rel-chain { display: flex; flex-wrap: wrap; align-items: center; gap: 4rpx; margin-bottom: 6rpx; }
  540. .rel-item { font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 8rpx; background: #f7f7f7; color: #555; }
  541. .rel-arrow { font-size: 20rpx; color: #10B981; font-weight: bold; }
  542. .rel-op { font-size: 20rpx; color: #f56c6c; font-weight: bold; }
  543. .rel-dot { font-size: 20rpx; color: #ccc; }
  544. .rel-hint { font-size: 20rpx; color: #999; line-height: 1.5; display: block; }
  545. .wuxing-cards { display: flex; flex-direction: column; gap: 10rpx; margin-top: 16rpx; }
  546. .wc-card { display: flex; align-items: center; padding: 16rpx 20rpx; border-radius: 16rpx; border: 2rpx solid #f0f0f0; }
  547. .wc-card:active { opacity: 0.85; }
  548. .wc-icon { font-size: 36rpx; margin-right: 16rpx; flex-shrink: 0; }
  549. .wc-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  550. .wc-name { font-size: 24rpx; font-weight: 600; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  551. .wc-text { font-size: 20rpx; color: #999; display: block; margin-top: 4rpx; white-space: normal; }
  552. .wc-body { border-color: rgba(255,140,66,0.3); background: rgba(255,140,66,0.04); border-left: 8rpx solid #FF8C42; }
  553. .wc-body .wc-name { color: #FF8C42; }
  554. .wc-wisdom { border-color: rgba(99,102,241,0.3); background: rgba(99,102,241,0.04); border-left: 8rpx solid #6366F1; }
  555. .wc-wisdom .wc-name { color: #6366F1; }
  556. .wc-wealth { border-color: rgba(245,158,11,0.3); background: rgba(245,158,11,0.04); border-left: 8rpx solid #F59E0B; }
  557. .wc-wealth .wc-name { color: #F59E0B; }
  558. .wc-action { border-color: rgba(16,185,129,0.3); background: rgba(16,185,129,0.04); border-left: 8rpx solid #10B981; }
  559. .wc-action .wc-name { color: #10B981; }
  560. .wc-mind { border-color: rgba(255,107,157,0.3); background: rgba(255,107,157,0.04); border-left: 8rpx solid #FF6B9D; }
  561. .wc-mind .wc-name { color: #FF6B9D; }
  562. </style>