index.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. <template>
  2. <view class="container">
  3. <view v-if="!isLoggedIn">
  4. <PageBanner theme="action" tagline="干预 · 记录 · 成长" quote="知行合一,每天进步一点点" />
  5. <!-- 品牌定位语 -->
  6. <view class="brand-strap"><text class="brand-strap-text">看见改变正在发生</text></view>
  7. <!-- 五维健康体系 -->
  8. <view class="wuxing-section">
  9. <view class="wuxing-title">身·心·智·行·富 五维闭环</view>
  10. <view class="wuxing-desc">五行相生相克,构成家庭健康的完整生态</view>
  11. <view class="wuxing-diagram">
  12. <view class="wd-center">
  13. <text class="wd-center-text">家庭健康</text>
  14. <text class="wd-center-sub">闭环系统</text>
  15. </view>
  16. <view class="wd-ring">
  17. <view class="wd-node wd-body" @click="goPage('/pages/body-detail/index')">
  18. <text class="wd-icon">🌏</text>
  19. <text class="wd-name">身</text>
  20. <text class="wd-desc">主动健康</text>
  21. </view>
  22. <view class="wd-node wd-wisdom" @click="goPage('/pages/wisdom-detail/index')">
  23. <text class="wd-icon">⚡</text>
  24. <text class="wd-name">智</text>
  25. <text class="wd-desc">赋能未来</text>
  26. </view>
  27. <view class="wd-node wd-wealth" @click="goPage('/pages/wealth/index')">
  28. <text class="wd-icon">💧</text>
  29. <text class="wd-name">富</text>
  30. <text class="wd-desc">利他创富</text>
  31. </view>
  32. <view class="wd-node wd-action" @click="goPage('/pages/action-detail/member-action-detail')">
  33. <text class="wd-icon">🌿</text>
  34. <text class="wd-name">行</text>
  35. <text class="wd-desc">人际和谐</text>
  36. </view>
  37. <view class="wd-node wd-mind" @click="goPage('/pages/mind-detail/index')">
  38. <text class="wd-icon">🔥</text>
  39. <text class="wd-name">心</text>
  40. <text class="wd-desc">大爱相随</text>
  41. </view>
  42. </view>
  43. </view>
  44. <view class="wuxing-relations">
  45. <view class="rel-group">
  46. <text class="rel-title">相生 → 系统前进</text>
  47. <view class="rel-chain">
  48. <text class="rel-item">🌏 身</text><text class="rel-arrow">→</text>
  49. <text class="rel-item">⚡ 智</text><text class="rel-arrow">→</text>
  50. <text class="rel-item">💧 富</text><text class="rel-arrow">→</text>
  51. <text class="rel-item">🌿 行</text><text class="rel-arrow">→</text>
  52. <text class="rel-item">🔥 心</text><text class="rel-arrow">→</text>
  53. <text class="rel-item">🌏 身</text>
  54. </view>
  55. <text class="rel-hint">健康孕育智慧,智慧创造价值,价值滋养关系,关系反哺心灵,心灵守护健康</text>
  56. </view>
  57. <view class="rel-group">
  58. <text class="rel-title">相克 → 系统平衡</text>
  59. <view class="rel-chain">
  60. <text class="rel-item">🌏 身</text><text class="rel-op">克</text><text class="rel-item">💧 富</text><text class="rel-dot">·</text>
  61. <text class="rel-item">⚡ 智</text><text class="rel-op">克</text><text class="rel-item">🌿 行</text><text class="rel-dot">·</text>
  62. <text class="rel-item">💧 富</text><text class="rel-op">克</text><text class="rel-item">🔥 心</text><text class="rel-dot">·</text>
  63. <text class="rel-item">🌿 行</text><text class="rel-op">克</text><text class="rel-item">🌏 身</text><text class="rel-dot">·</text>
  64. <text class="rel-item">🔥 心</text><text class="rel-op">克</text><text class="rel-item">⚡ 智</text>
  65. </view>
  66. <text class="rel-hint">健康不换来金钱,算计不如真诚,财富服务家庭,关爱有度,大爱留理性</text>
  67. </view>
  68. </view>
  69. <view class="wuxing-cards">
  70. <view class="wc-card wc-body" @click="goPage('/pages/body-detail/index')">
  71. <text class="wc-icon">🌏</text>
  72. <text class="wc-name">身 · 主动健康</text>
  73. <text class="wc-text">用最适合的方法,从根源调理全家体质</text>
  74. </view>
  75. <view class="wc-card wc-wisdom" @click="goPage('/pages/wisdom-detail/index')">
  76. <text class="wc-icon">⚡</text>
  77. <text class="wc-name">智 · 赋能未来</text>
  78. <text class="wc-text">心智测评与综合素质培养,开启下一代大智慧</text>
  79. </view>
  80. <view class="wc-card wc-wealth" @click="goPage('/pages/wealth/index')">
  81. <text class="wc-icon">💧</text>
  82. <text class="wc-name">富 · 利他创富</text>
  83. <text class="wc-text">在利他分享中实现家庭第二收入曲线</text>
  84. </view>
  85. <view class="wc-card wc-action" @click="goPage('/pages/action-detail/member-action-detail')">
  86. <text class="wc-icon">🌿</text>
  87. <text class="wc-name">行 · 人际和谐</text>
  88. <text class="wc-text">亲子关系·夫妻关系·社交人脉,以和谐滋养幸福</text>
  89. </view>
  90. <view class="wc-card wc-mind" @click="goPage('/pages/mind-detail/index')">
  91. <text class="wc-icon">🔥</text>
  92. <text class="wc-name">心 · 大爱相随</text>
  93. <text class="wc-text">心理学视角解读自我与家人,升华家族纽带</text>
  94. </view>
  95. </view>
  96. </view>
  97. <!-- 理念卡 -->
  98. <view class="concept-card">
  99. <text class="concept-quote">记录不是目的,看见改变才是。</text>
  100. <text class="concept-desc">每一份饮食、运动、心情的记录,都在为成长提供依据。</text>
  101. </view>
  102. <!-- 示例案例 -->
  103. <view class="case-card">
  104. <view class="case-header">
  105. <text class="case-label">📖 真实案例(脱敏)</text>
  106. </view>
  107. <view class="case-body">
  108. <text class="case-name">小明(10岁,体校生)</text>
  109. <text class="case-change">3个月干预前后:</text>
  110. <view class="case-metrics">
  111. <view class="metric-item">
  112. <text class="metric-up">↑ 5分</text>
  113. <text class="metric-label">身维度</text>
  114. </view>
  115. <view class="metric-item">
  116. <text class="metric-up">↑ 3分</text>
  117. <text class="metric-label">智维度</text>
  118. </view>
  119. <view class="metric-item">
  120. <text class="metric-up">↑ 8天</text>
  121. <text class="metric-label">连续打卡</text>
  122. </view>
  123. </view>
  124. <text class="case-note">通过饮食记录 + 睡眠打卡,免疫力显著提升</text>
  125. </view>
  126. </view>
  127. <!-- 功能预览 -->
  128. <view class="guest-features">
  129. <view class="feat-card" @click="goPage('/pages/health/daily-checkin')">
  130. <text class="feat-icon">✅</text>
  131. <text class="feat-title">每日打卡</text>
  132. <text class="feat-desc">饮食·运动·睡眠·心情</text>
  133. </view>
  134. <view class="feat-card" @click="goPage('/pages/health/survey-questionnaire?dim=all')">
  135. <text class="feat-icon">📋</text>
  136. <text class="feat-title">定期调研</text>
  137. <text class="feat-desc">每周家庭健康问卷</text>
  138. </view>
  139. <view class="feat-card">
  140. <text class="feat-icon">🏆</text>
  141. <text class="feat-title">里程碑</text>
  142. <text class="feat-desc">连续打卡·挑战完成</text>
  143. </view>
  144. <view class="feat-card" @click="goPage('/pages/activity/index')">
  145. <text class="feat-icon">🎯</text>
  146. <text class="feat-title">家庭挑战</text>
  147. <text class="feat-desc">全家参与的互动任务</text>
  148. </view>
  149. </view>
  150. <!-- 登录引导 -->
  151. <view class="login-guide-section">
  152. <LoginGuideCard
  153. title="解锁完整成长档案"
  154. :items="[
  155. '查看干预进度与追踪记录',
  156. '完成每日打卡累积连续记录',
  157. '参与家庭挑战获得里程碑奖励'
  158. ]" />
  159. </view>
  160. </view>
  161. <view v-else>
  162. <PageBanner theme="action" tagline="干预 · 记录 · 成长" quote="知行合一,每天进步一点点" />
  163. <FamilyMemberStrip @select="onMemberSelect" />
  164. <view class="reminder-bar" v-if="hasIncomplete">
  165. <text class="reminder-text">⚠ 今日还有 {{ incompleteCount }} 项未记录</text>
  166. <text class="reminder-close" @click="dismissReminder">✕</text>
  167. </view>
  168. <view class="section">
  169. <view class="progress-card">
  170. <view class="progress-header">
  171. <text class="progress-title">今日干预进度</text>
  172. <text class="progress-date">{{ todayDate }}</text>
  173. </view>
  174. <view class="progress-item" v-for="(item, idx) in progressItems" :key="getProgressKey(item)">
  175. <text class="progress-icon">{{ item.icon }}</text>
  176. <text class="progress-label">{{ item.label }}</text>
  177. <text class="progress-status" :class="item.done ? 'status-done' : 'status-pending'">{{ item.done ? '✔ 已完成' : '' }}</text>
  178. <text class="progress-btn" v-if="!item.done" @click="handleProgressClick(item)">{{ item.actionText || '补录' }}</text>
  179. </view>
  180. <view class="progress-footer">
  181. <text class="footer-text">今日完成 {{ doneCount }}/{{ totalCount }}</text>
  182. <text class="footer-streak" v-if="streakDays > 0">🔥 连续打卡 {{ streakDays }} 天</text>
  183. </view>
  184. </view>
  185. </view>
  186. <view class="section">
  187. <view class="tracking-card">
  188. <view class="tracking-tabs">
  189. <view class="tracking-tab" :class="activeTab === tab.key ? 'tab-active' : ''" v-for="tab in trackingTabs" :key="tab.key" @click="switchTab(tab.key)">
  190. <text class="tab-text" :class="activeTab === tab.key ? 'tab-active-text' : ''">{{ tab.label }}</text>
  191. </view>
  192. </view>
  193. <view class="tracking-content">
  194. <view class="tracking-empty" v-if="currentRecords.length === 0">
  195. <text class="empty-icon">📊</text>
  196. <text class="empty-text">还没有记录</text>
  197. <text class="empty-hint">开始记录{{ activeTabLabel }}吧</text>
  198. </view>
  199. <view class="tracking-record" v-for="(rec, rIdx) in currentRecords" :key="getRecordKey(rec)">
  200. <text class="record-date">{{ rec._date || rec._text }}</text>
  201. <text class="record-content">{{ rec._content }}</text>
  202. </view>
  203. <view class="tracking-more" v-if="currentRecords.length > 0" @click="viewAllRecords">查看全部 ›</view>
  204. </view>
  205. </view>
  206. </view>
  207. <view class="section">
  208. <view class="milestone-card">
  209. <text class="section-title-row">🏆 里程碑</text>
  210. <view class="milestone-grid">
  211. <view class="milestone-item">
  212. <text class="milestone-icon">🔥</text>
  213. <text class="milestone-value">{{ streakDays }}天</text>
  214. <text class="milestone-label">连续打卡</text>
  215. </view>
  216. <view class="milestone-item">
  217. <text class="milestone-icon">🎯</text>
  218. <text class="milestone-value">{{ activeChallenges }}</text>
  219. <text class="milestone-label">进行中挑战</text>
  220. </view>
  221. <view class="milestone-item">
  222. <text class="milestone-icon">📈</text>
  223. <text class="milestone-value">{{ completeness }}%</text>
  224. <text class="milestone-label">档案完整度</text>
  225. </view>
  226. </view>
  227. </view>
  228. </view>
  229. <view class="section">
  230. <view class="survey-card">
  231. <text class="section-title-row">📋 定期调研</text>
  232. <view class="survey-pending" v-if="pendingSurvey">
  233. <text class="survey-alert">⚠ 本周还未完成调研</text>
  234. <text class="survey-title-text">{{ pendingSurvey.title }}</text>
  235. <text class="survey-btn" @click="startSurvey">[开始填写]</text>
  236. </view>
  237. <view class="survey-done" v-else-if="allDone"><text class="survey-done-text">✅ 所有调研已完成</text></view>
  238. <view class="survey-history-list" v-if="surveyHistory.length > 0">
  239. <text class="history-label">历史调研:</text>
  240. <view class="history-item" v-for="(h, hIdx) in surveyHistory" :key="getHistoryKey(h)">
  241. <text class="history-date">{{ h._date }}</text>
  242. <text class="history-content">{{ h._content }}</text>
  243. </view>
  244. <text class="survey-more" @click="viewAllHistory">查看全部调研 ›</text>
  245. </view>
  246. <view class="survey-empty" v-else-if="!pendingSurvey"><text class="survey-empty-text">暂无待完成调研</text></view>
  247. </view>
  248. </view>
  249. <DimensionActivities :isLoggedIn="true" @activityClick="goActivityDetail" @moreActivities="goMoreActivities" style="margin-bottom:60rpx" />
  250. </view>
  251. </view>
  252. </template>
  253. <script>
  254. import PageBanner from '../../components/PageBanner.vue'
  255. import LoginGuideCard from '../../components/LoginGuideCard.vue'
  256. import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
  257. import DimensionActivities from '../../components/DimensionActivities.vue'
  258. import { healthCheckinList, getDietDailyRecords, getSurveyStatus, getSurveyHistory } from '../../utils/api.js'
  259. export default {
  260. components: { PageBanner, LoginGuideCard, FamilyMemberStrip, DimensionActivities },
  261. data() { return { memberId: '', todayDate: '', hasIncomplete: false, incompleteCount: 0, progressItems: [], activeTab: 'diet', trackingTabs: [{ key: 'diet', label: '饮食' }, { key: 'exercise', label: '运动' }, { key: 'sleep', label: '睡眠' }, { key: 'emotion', label: '心情' }, { key: 'survey', label: '调研' }], trackingData: { diet: [], exercise: [], sleep: [], emotion: [], survey: [] }, streakDays: 0, activeChallenges: 0, completeness: 0, pendingSurvey: null, allDone: false, surveyHistory: [], 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. activeTabLabel: function() { var m = { diet: '饮食', exercise: '运动', sleep: '睡眠', emotion: '心情', survey: '调研' }; return m[this.activeTab] || '' },
  266. currentRecords: function() { return this.trackingData[this.activeTab] || [] }
  267. },
  268. onShow() {
  269. this.isLoggedIn = !!uni.getStorageSync('token')
  270. if (this.isLoggedIn) {
  271. this.memberId = uni.getStorageSync('currentChildId') || ''
  272. var now = new Date()
  273. this.todayDate = now.getFullYear() + '-' + this.pad(now.getMonth() + 1) + '-' + this.pad(now.getDate())
  274. this.loadAllData()
  275. }
  276. },
  277. methods: {
  278. onMemberSelect: function(member) {
  279. this.memberId = member && member.id ? member.id : ''
  280. this.loadAllData()
  281. },
  282. pad: function(n) { return n < 10 ? '0' + n : '' + n },
  283. getProgressKey: function(item) { return item && item.label ? item.label : 'p-' + Math.random() },
  284. getRecordKey: function(rec) { return rec && rec._text ? rec._text + rec._content : 'r-' + Math.random() },
  285. getHistoryKey: function(h) { return h && h._date ? h._date + h._content : 'h-' + Math.random() },
  286. dismissReminder: function() { this.hasIncomplete = false },
  287. switchTab: function(key) { this.activeTab = key; this.loadTrackingData(key) },
  288. handleProgressClick: function(item) {
  289. var m = { checkin: '/pages/health/daily-checkin', diet: '/pages/health/diet-index', exercise: '/pages/health/exercise-index', sleep: '/pages/health/sleep-index', emotion: '/pages/mind/index' }
  290. var url = m[item.key]
  291. if (item.key === 'survey' && this.pendingSurvey && this.pendingSurvey.templateId) {
  292. url = '/pages/health/survey-questionnaire?templateId=' + this.pendingSurvey.templateId + '&templateTitle=' + encodeURIComponent(this.pendingSurvey.title) + '&templateDimension=' + (this.pendingSurvey.dimension || '')
  293. }
  294. if (url) uni.navigateTo({ url: url })
  295. },
  296. startSurvey: function() { if (this.pendingSurvey && this.pendingSurvey.templateId) uni.navigateTo({ url: '/pages/health/survey-questionnaire?templateId=' + this.pendingSurvey.templateId + '&templateTitle=' + encodeURIComponent(this.pendingSurvey.title) + '&templateDimension=' + (this.pendingSurvey.dimension || '') }) },
  297. viewAllRecords: function() { var m = { diet: '/pages/health/diet-index', exercise: '/pages/health/exercise-index', sleep: '/pages/health/sleep-index', survey: '/pages/health/survey-history?memberId=' + this.memberId, emotion: '/pages/mind/index' }; if (m[this.activeTab]) uni.navigateTo({ url: m[this.activeTab] }) },
  298. viewAllHistory: function() { uni.navigateTo({ url: '/pages/health/survey-history?memberId=' + this.memberId }) },
  299. goActivityDetail: function(act) { uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id }) },
  300. goMoreActivities: function() { uni.navigateTo({ url: '/pages/activity/index' }) },
  301. goPage: function(url) { uni.navigateTo({ url: url }) },
  302. loadTrackingData: function(key) { return this.trackingData[key] || [] },
  303. async loadAllData() {
  304. var self = this
  305. try { await self.loadProgressItems() } catch(e) {}
  306. try { await self.loadSurveyData() } catch(e) {}
  307. try { await self.loadMilestones() } catch(e) {}
  308. },
  309. async loadProgressItems() {
  310. var self = this
  311. var items = []
  312. var checkinDone = false
  313. try {
  314. var ciRes = await healthCheckinList({ yearMonth: self.todayDate.substring(0, 7) })
  315. if (ciRes.code === 200 && ciRes.data) {
  316. for (var ci = 0; ci < ciRes.data.length; ci++) { if (ciRes.data[ci] && ciRes.data[ci].checkinDate && ciRes.data[ci].checkinDate.substring(0, 10) === self.todayDate) { checkinDone = true; break } }
  317. }
  318. } catch(e) {}
  319. items.push({ key: 'checkin', icon: '✅', label: '今日打卡', done: checkinDone, actionText: '打卡' })
  320. var dietDone = false
  321. try {
  322. var dRes = await getDietDailyRecords({ date: self.todayDate })
  323. if (dRes.code === 200 && dRes.data && dRes.data.length > 0) dietDone = true
  324. } catch(e) {}
  325. items.push({ key: 'diet', icon: '🍽️', label: '饮食记录', done: dietDone, actionText: '补录' })
  326. items.push({ key: 'exercise', icon: '🏃', label: '运动记录', done: false, actionText: '记录' })
  327. items.push({ key: 'emotion', icon: '😊', label: '心情记录', done: false, actionText: '记录' })
  328. items.push({ key: 'survey', icon: '📋', label: '本周调研', done: self.allDone, actionText: self.allDone ? '' : '开始' })
  329. self.progressItems = items
  330. self.incompleteCount = 0
  331. for (var pi = 0; pi < items.length; pi++) if (items[pi] && !items[pi].done) self.incompleteCount++
  332. self.hasIncomplete = self.incompleteCount > 0
  333. },
  334. async loadSurveyData() {
  335. var self = this
  336. try {
  337. var statusRes = await getSurveyStatus({})
  338. if (statusRes.code === 200 && statusRes.data) {
  339. var list = statusRes.data
  340. self.pendingSurvey = null; self.allDone = true; var hasAny = false
  341. for (var si = 0; si < list.length; si++) { var s = list[si]; if (!s) continue; hasAny = true; if (s.status === 'pending') { self.pendingSurvey = { templateId: s.templateId, title: s.title || '定期调研', dimension: s.dimension || '' }; self.allDone = false } }
  342. if (!hasAny) { self.pendingSurvey = null; self.allDone = false }
  343. }
  344. } catch(e) {}
  345. try {
  346. var histRes = await getSurveyHistory({})
  347. if (histRes.code === 200 && histRes.data) {
  348. var hlist = histRes.data; self.surveyHistory = []
  349. for (var hi = 0; hi < Math.min(hlist.length, 3); hi++) { var h = hlist[hi]; self.surveyHistory.push({ _date: h && h.submittedAt ? h.submittedAt.substring(0, 10) : '', _content: '已完成调研' }) }
  350. }
  351. } catch(e) {}
  352. },
  353. async loadMilestones() {
  354. var self = this
  355. try {
  356. var ciRes = await healthCheckinList({})
  357. if (ciRes.code === 200 && ciRes.data) self.streakDays = Math.min(ciRes.data.length, 30)
  358. } catch(e) {}
  359. self.activeChallenges = 0
  360. self.completeness = self.streakDays > 0 ? Math.min(100, Math.round(self.streakDays * 10)) : 0
  361. }
  362. }
  363. }
  364. </script>
  365. <style scoped>
  366. .container{background:#FFF7ED;min-height:100vh;padding-bottom:120rpx}
  367. .section{margin:16rpx 24rpx}
  368. /* 游客态 */
  369. .brand-strap{margin:0 24rpx 16rpx;background:linear-gradient(135deg,#F0FFF4,rgba(16,185,129,0.1));border-radius:16rpx;padding:20rpx 24rpx}
  370. .brand-strap-text{font-size:26rpx;color:#10B981;font-weight:600}
  371. .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)}
  372. .concept-quote{font-size:30rpx;font-weight:bold;color:#333;display:block;margin-bottom:8rpx}
  373. .concept-desc{font-size:24rpx;color:#888;display:block;line-height:1.6}
  374. .case-card{margin:0 24rpx 16rpx;background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  375. .case-header{margin-bottom:12rpx}
  376. .case-label{font-size:24rpx;color:#10B981;font-weight:bold}
  377. .case-body{padding:8rpx 0}
  378. .case-name{font-size:26rpx;color:#333;font-weight:600;display:block;margin-bottom:6rpx}
  379. .case-change{font-size:24rpx;color:#666;display:block;margin-bottom:12rpx}
  380. .case-metrics{display:flex;gap:16rpx;margin-bottom:12rpx}
  381. .metric-item{display:flex;flex-direction:column;align-items:center;flex:1;background:#F0FFF4;border-radius:12rpx;padding:12rpx 8rpx}
  382. .metric-up{font-size:26rpx;font-weight:bold;color:#10B981}
  383. .metric-label{font-size:20rpx;color:#999;margin-top:4rpx}
  384. .case-note{font-size:22rpx;color:#888;display:block}
  385. .guest-features{display:flex;flex-wrap:wrap;margin:0 24rpx 16rpx}
  386. .feat-card{width:calc(50% - 8rpx);background:#fff;border-radius:20rpx;padding:24rpx;margin-bottom:16rpx;margin-right:16rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  387. .feat-icon{font-size:40rpx;display:block;margin-bottom:8rpx}
  388. .feat-title{font-size:26rpx;font-weight:600;color:#333;display:block;margin-bottom:4rpx}
  389. .feat-desc{font-size:22rpx;color:#999;display:block}
  390. .login-guide-section{margin:0 24rpx 24rpx}
  391. /* 登录后原有样式 */
  392. .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}
  393. .reminder-text{font-size:24rpx;color:#E65100}
  394. .reminder-close{font-size:28rpx;color:#999}
  395. .progress-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  396. .progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16rpx}
  397. .progress-title{font-size:28rpx;font-weight:bold;color:#333}
  398. .progress-date{font-size:22rpx;color:#999}
  399. .progress-item{display:flex;align-items:center;padding:12rpx 0;border-bottom:1rpx solid #f5f5f5}
  400. .progress-item:last-of-type{border-bottom:none}
  401. .progress-icon{font-size:28rpx;margin-right:12rpx;flex-shrink:0}
  402. .progress-label{font-size:26rpx;color:#333;flex:1}
  403. .progress-status{font-size:22rpx;margin-right:8rpx;flex-shrink:0}
  404. .status-done{color:#10B981}
  405. .status-pending{color:#ccc}
  406. .progress-btn{font-size:22rpx;color:#F97316;padding:4rpx 16rpx;border:1rpx solid #F97316;border-radius:20rpx;flex-shrink:0}
  407. .progress-footer{display:flex;justify-content:space-between;align-items:center;margin-top:16rpx;padding-top:16rpx;border-top:1rpx solid #f5f5f5}
  408. .footer-text{font-size:24rpx;color:#666;font-weight:bold}
  409. .footer-streak{font-size:24rpx;color:#F97316;font-weight:bold}
  410. .tracking-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  411. .tracking-tabs{display:flex;margin-bottom:16rpx}
  412. .tracking-tab{flex:1;text-align:center;padding:10rpx 0;border-radius:20rpx;margin-right:8rpx;background:#FFF7ED}
  413. .tracking-tab:last-child{margin-right:0}
  414. .tab-active{background:#F97316}
  415. .tab-text{font-size:24rpx;color:#666}
  416. .tab-active-text{color:#fff;font-weight:bold}
  417. .tracking-content{min-height:200rpx}
  418. .tracking-empty{display:flex;flex-direction:column;align-items:center;padding:60rpx 0}
  419. .empty-icon{font-size:60rpx;margin-bottom:12rpx}
  420. .empty-text{font-size:26rpx;color:#999}
  421. .empty-hint{font-size:22rpx;color:#ccc;margin-top:8rpx}
  422. .tracking-record{display:flex;align-items:center;padding:10rpx 0;border-bottom:1rpx solid #f5f5f5}
  423. .record-date{font-size:22rpx;color:#999;width:120rpx;flex-shrink:0}
  424. .record-content{font-size:24rpx;color:#333;flex:1}
  425. .tracking-more{text-align:right;font-size:24rpx;color:#F97316;padding:12rpx 0 0 0}
  426. .milestone-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  427. .section-title-row{font-size:26rpx;font-weight:bold;color:#333;margin-bottom:16rpx;display:block}
  428. .milestone-grid{display:flex;justify-content:space-between}
  429. .milestone-item{display:flex;flex-direction:column;align-items:center;flex:1}
  430. .milestone-icon{font-size:40rpx;margin-bottom:4rpx}
  431. .milestone-value{font-size:28rpx;font-weight:bold;color:#F97316;margin-bottom:2rpx}
  432. .milestone-label{font-size:22rpx;color:#999}
  433. .survey-card{background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  434. .survey-pending{background:#FFF3E0;border-radius:12rpx;padding:16rpx;margin-bottom:12rpx}
  435. .survey-alert{font-size:24rpx;color:#E65100;font-weight:bold;display:block;margin-bottom:4rpx}
  436. .survey-title-text{font-size:24rpx;color:#333;display:block;margin-bottom:8rpx}
  437. .survey-btn{font-size:22rpx;color:#F97316;font-weight:bold}
  438. .survey-done{padding:8rpx 0}
  439. .survey-done-text{font-size:24rpx;color:#10B981}
  440. .survey-history-list{border-top:1rpx solid #f5f5f5;padding-top:12rpx}
  441. .history-label{font-size:22rpx;color:#999;display:block;margin-bottom:6rpx}
  442. .history-item{display:flex;align-items:center;padding:6rpx 0}
  443. .history-date{font-size:22rpx;color:#999;width:120rpx;flex-shrink:0}
  444. .history-content{font-size:24rpx;color:#666;flex:1}
  445. .survey-more{text-align:right;font-size:22rpx;color:#F97316;padding-top:8rpx}
  446. .survey-empty{padding:12rpx 0}
  447. .survey-empty-text{font-size:24rpx;color:#999}
  448. </style>
  449. /* ===== 五维闭环体系样式 ===== */
  450. .wuxing-section { margin: 0 24rpx 16rpx; background: #fff; border-radius: 20rpx; padding: 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
  451. .wuxing-title { font-size: 30rpx; font-weight: bold; color: #333; text-align: center; margin-bottom: 6rpx; }
  452. .wuxing-desc { font-size: 22rpx; color: #999; text-align: center; margin-bottom: 20rpx; }
  453. .wuxing-diagram { position: relative; height: 320rpx; margin: 0 auto 20rpx; }
  454. .wd-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 140rpx; height: 140rpx; background: linear-gradient(135deg, #10B981, #34D399); border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 4rpx 20rpx rgba(16,185,129,0.3); z-index: 2; }
  455. .wd-center-text { font-size: 24rpx; font-weight: bold; color: #fff; }
  456. .wd-center-sub { font-size: 18rpx; color: rgba(255,255,255,0.8); margin-top: 2rpx; }
  457. .wd-ring { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
  458. .wd-node { position: absolute; width: 130rpx; text-align: center; cursor: pointer; }
  459. .wd-node:nth-child(1) { top: 0; left: 50%; transform: translateX(-50%); }
  460. .wd-node:nth-child(2) { top: 25%; right: 0; }
  461. .wd-node:nth-child(3) { bottom: 25%; right: 0; }
  462. .wd-node:nth-child(4) { bottom: 0; left: 50%; transform: translateX(-50%); }
  463. .wd-node:nth-child(5) { bottom: 25%; left: 0; }
  464. .wd-icon { font-size: 40rpx; display: block; }
  465. .wd-name { font-size: 26rpx; font-weight: bold; display: block; margin-top: 4rpx; }
  466. .wd-desc { font-size: 18rpx; color: #999; display: block; }
  467. .wd-node.wd-body .wd-name { color: #FF8C42; }
  468. .wd-node.wd-wisdom .wd-name { color: #6366F1; }
  469. .wd-node.wd-wealth .wd-name { color: #F59E0B; }
  470. .wd-node.wd-action .wd-name { color: #10B981; }
  471. .wd-node.wd-mind .wd-name { color: #FF6B9D; }
  472. .wuxing-relations { margin-top: 8rpx; }
  473. .rel-group { margin-bottom: 16rpx; }
  474. .rel-title { font-size: 24rpx; font-weight: bold; color: #333; margin-bottom: 8rpx; display: block; }
  475. .rel-chain { display: flex; flex-wrap: wrap; align-items: center; gap: 4rpx; margin-bottom: 6rpx; }
  476. .rel-item { font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 8rpx; background: #f5f5f5; }
  477. .rel-arrow { font-size: 20rpx; color: #10B981; font-weight: bold; }
  478. .rel-op { font-size: 20rpx; color: #f56c6c; font-weight: bold; }
  479. .rel-dot { font-size: 20rpx; color: #ccc; }
  480. .rel-hint { font-size: 20rpx; color: #999; line-height: 1.5; display: block; }
  481. .wuxing-cards { display: flex; flex-direction: column; gap: 10rpx; margin-top: 16rpx; }
  482. .wc-card { display: flex; align-items: center; padding: 16rpx 20rpx; border-radius: 16rpx; border: 2rpx solid #f0f0f0; }
  483. .wc-card:active { opacity: 0.85; }
  484. .wc-icon { font-size: 36rpx; margin-right: 16rpx; flex-shrink: 0; }
  485. .wc-name { font-size: 24rpx; font-weight: 600; flex: 1; }
  486. .wc-text { font-size: 20rpx; color: #999; display: block; margin-top: 2rpx; }
  487. .wc-body { border-color: rgba(255,140,66,0.3); background: rgba(255,140,66,0.04); }
  488. .wc-body .wc-name { color: #FF8C42; }
  489. .wc-wisdom { border-color: rgba(99,102,241,0.3); background: rgba(99,102,241,0.04); }
  490. .wc-wisdom .wc-name { color: #6366F1; }
  491. .wc-wealth { border-color: rgba(245,158,11,0.3); background: rgba(245,158,11,0.04); }
  492. .wc-wealth .wc-name { color: #F59E0B; }
  493. .wc-action { border-color: rgba(16,185,129,0.3); background: rgba(16,185,129,0.04); }
  494. .wc-action .wc-name { color: #10B981; }
  495. .wc-mind { border-color: rgba(255,107,157,0.3); background: rgba(255,107,157,0.04); }
  496. .wc-mind .wc-name { color: #FF6B9D; }