index.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <view class="body-container">
  3. <!-- 品牌头部 -->
  4. <view class="brand-header">
  5. <image class="brand-logo" src="/static/logo.png" mode="aspectFit" />
  6. <text class="brand-title">身体能量</text>
  7. <text class="brand-slogan">关注儿童身心健康,从每一天开始</text>
  8. </view>
  9. <!-- 功能入口 -->
  10. <view class="func-section" v-if="sectionVisible('func_entries')">
  11. <view class="func-grid">
  12. <view class="func-item" v-for="item in funcList" :key="item.label">
  13. <view class="func-icon-wrap">
  14. <text class="func-icon">{{ item.icon }}</text>
  15. </view>
  16. <text class="func-label">{{ item.label }}</text>
  17. </view>
  18. </view>
  19. </view>
  20. <!-- 今日身体数据 -->
  21. <view class="section" v-if="sectionVisible('daily_stats')">
  22. <view class="section-header">
  23. <text class="section-title">今日身体数据</text>
  24. </view>
  25. <view class="stats-card">
  26. <view class="stat-item">
  27. <text class="stat-value">{{ dailyStats.steps }}</text>
  28. <text class="stat-unit">步</text>
  29. <text class="stat-label">今日步数</text>
  30. </view>
  31. <view class="stat-divider"></view>
  32. <view class="stat-item">
  33. <text class="stat-value">{{ dailyStats.water }}</text>
  34. <text class="stat-unit">L</text>
  35. <text class="stat-label">饮水量</text>
  36. </view>
  37. <view class="stat-divider"></view>
  38. <view class="stat-item">
  39. <text class="stat-value">{{ dailyStats.sleep }}</text>
  40. <text class="stat-unit">h</text>
  41. <text class="stat-label">睡眠时长</text>
  42. </view>
  43. </view>
  44. </view>
  45. <!-- 健康小贴士 -->
  46. <view class="section" v-if="sectionVisible('health_tips')">
  47. <view class="section-header">
  48. <text class="section-title">健康小贴士</text>
  49. </view>
  50. <view class="tips-list">
  51. <view class="tip-card" v-for="item in healthTips" :key="item.id">
  52. <text class="tip-title">{{ item.title }}</text>
  53. <text class="tip-summary">{{ item.summary }}</text>
  54. <view class="tip-footer">
  55. <text class="tip-date">{{ item.date }}</text>
  56. <text class="tip-tag">阅读更多</text>
  57. </view>
  58. </view>
  59. </view>
  60. </view>
  61. <!-- 规划师推荐(服务商可见,后台可配置) -->
  62. <view class="section" v-if="sectionVisible('teacher_tips')">
  63. <view class="section-header">
  64. <text class="section-title">👨‍🏫 规划师推荐</text>
  65. </view>
  66. <view class="tips-list">
  67. <view class="tip-card" v-for="item in teacherTips" :key="item.id">
  68. <text class="tip-title">{{ item.title }}</text>
  69. <text class="tip-summary">{{ item.summary }}</text>
  70. <view class="tip-footer">
  71. <text class="tip-date">{{ item.date }}</text>
  72. <text class="tip-tag">查看详情</text>
  73. </view>
  74. </view>
  75. </view>
  76. </view>
  77. <!-- 底部占位 -->
  78. <view class="bottom-spacer"></view>
  79. </view>
  80. </template>
  81. <script>
  82. import { getVisibleSections } from '../../utils/api.js'
  83. export default {
  84. data() {
  85. return {
  86. visibleSections: [],
  87. funcList: [
  88. { icon: '\u{1F3C3}', label: '运动' },
  89. { icon: '\u{1F957}', label: '饮食' },
  90. { icon: '\u{1F634}', label: '作息' },
  91. { icon: '\u{1F9A0}', label: '菌群' },
  92. { icon: '\u{1F9D8}', label: '冥想' }
  93. ],
  94. dailyStats: {
  95. steps: '6,582',
  96. water: '1.2',
  97. sleep: '8.5'
  98. },
  99. healthTips: [
  100. { id: 1, title: '儿童每日运动指南', summary: '不同年龄段儿童每天需要多少运动量?科学运动助力健康成长。', date: '2024-01-15' },
  101. { id: 2, title: '五行饮食搭配法则', summary: '根据五行理论合理搭配膳食,均衡营养从每一餐开始。', date: '2024-01-12' },
  102. { id: 3, title: '优质睡眠从小养成', summary: '帮助孩子建立规律的作息习惯,提升睡眠质量。', date: '2024-01-10' },
  103. { id: 4, title: '肠道菌群与免疫力', summary: '了解益生菌对儿童健康的重要作用,从内部提升抵抗力。', date: '2024-01-08' }
  104. ],
  105. teacherTips: [
  106. { id: 1, title: '儿童体态评估与纠正', summary: '专业体态评估方法,及早发现并纠正不良体态。', date: '2024-01-14' },
  107. { id: 2, title: '感统训练家庭方案', summary: '针对不同年龄段的感觉统合训练建议,在家也能做。', date: '2024-01-11' }
  108. ]
  109. }
  110. },
  111. onShow() {
  112. this.resetTabBar()
  113. this.loadSectionConfig()
  114. },
  115. methods: {
  116. resetTabBar() {
  117. uni.setTabBarItem({ index: 1, text: '身体' })
  118. uni.setTabBarItem({ index: 2, text: '心智' })
  119. },
  120. async loadSectionConfig() {
  121. var token = uni.getStorageSync('token')
  122. var role = null
  123. if (token) {
  124. role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  125. }
  126. var res
  127. try {
  128. res = await getVisibleSections({ pageKey: 'body', role: role })
  129. if (res.code === 200 && res.data) {
  130. this.visibleSections = res.data.map(function(s) { return s.sectionKey })
  131. }
  132. } catch (e) {
  133. this.visibleSections = ['func_entries', 'daily_stats', 'health_tips']
  134. }
  135. },
  136. sectionVisible(sectionKey) {
  137. return this.visibleSections.indexOf(sectionKey) !== -1
  138. }
  139. }
  140. }
  141. </script>
  142. <style scoped>
  143. .body-container {
  144. min-height: 100vh;
  145. background: #f5f7fa;
  146. padding-bottom: 120rpx;
  147. }
  148. /* ===== 品牌头部 ===== */
  149. .brand-header {
  150. display: flex;
  151. flex-direction: column;
  152. align-items: center;
  153. padding: 30rpx 30rpx 16rpx;
  154. background: linear-gradient(180deg, #1A2A4A 0%, #1E3A5F 60%, #f5f7fa 100%);
  155. }
  156. .brand-logo {
  157. width: 80rpx;
  158. height: 80rpx;
  159. border-radius: 18rpx;
  160. margin-bottom: 8rpx;
  161. }
  162. .brand-title {
  163. font-size: 36rpx;
  164. font-weight: bold;
  165. color: #8FC5E8;
  166. letter-spacing: 4rpx;
  167. }
  168. .brand-slogan {
  169. font-size: 22rpx;
  170. color: rgba(143, 197, 232, 0.6);
  171. margin-top: 6rpx;
  172. }
  173. /* ===== 功能入口 ===== */
  174. .func-section {
  175. margin: 20rpx 30rpx;
  176. }
  177. .func-grid {
  178. display: flex;
  179. flex-direction: row;
  180. justify-content: space-between;
  181. background: #fff;
  182. border-radius: 20rpx;
  183. padding: 24rpx 16rpx;
  184. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  185. }
  186. .func-item {
  187. display: flex;
  188. flex-direction: column;
  189. align-items: center;
  190. flex: 1;
  191. }
  192. .func-icon-wrap {
  193. width: 80rpx;
  194. height: 80rpx;
  195. border-radius: 40rpx;
  196. background: linear-gradient(135deg, #D6EAF8, #EBF2FA);
  197. display: flex;
  198. align-items: center;
  199. justify-content: center;
  200. margin-bottom: 10rpx;
  201. }
  202. .func-icon {
  203. font-size: 40rpx;
  204. }
  205. .func-label {
  206. font-size: 22rpx;
  207. color: #666;
  208. font-weight: 500;
  209. }
  210. /* ===== 通用区块 ===== */
  211. .section {
  212. margin: 20rpx 30rpx;
  213. }
  214. .section-header {
  215. margin-bottom: 20rpx;
  216. }
  217. .section-title {
  218. font-size: 30rpx;
  219. font-weight: bold;
  220. color: #333;
  221. }
  222. /* ===== 今日数据 ===== */
  223. .stats-card {
  224. background: #fff;
  225. border-radius: 20rpx;
  226. padding: 30rpx 20rpx;
  227. display: flex;
  228. flex-direction: row;
  229. align-items: center;
  230. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  231. }
  232. .stat-item {
  233. flex: 1;
  234. display: flex;
  235. flex-direction: column;
  236. align-items: center;
  237. }
  238. .stat-value {
  239. font-size: 44rpx;
  240. font-weight: bold;
  241. color: #5B9BD5;
  242. }
  243. .stat-unit {
  244. font-size: 22rpx;
  245. color: #999;
  246. margin-top: -4rpx;
  247. }
  248. .stat-label {
  249. font-size: 24rpx;
  250. color: #666;
  251. margin-top: 8rpx;
  252. }
  253. .stat-divider {
  254. width: 1rpx;
  255. height: 80rpx;
  256. background: #f0f0f0;
  257. }
  258. /* ===== 健康小贴士 ===== */
  259. .tips-list {
  260. display: flex;
  261. flex-direction: column;
  262. gap: 20rpx;
  263. }
  264. .tip-card {
  265. background: #fff;
  266. border-radius: 20rpx;
  267. padding: 24rpx;
  268. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  269. }
  270. .tip-title {
  271. font-size: 28rpx;
  272. font-weight: bold;
  273. color: #333;
  274. display: block;
  275. margin-bottom: 10rpx;
  276. }
  277. .tip-summary {
  278. font-size: 24rpx;
  279. color: #888;
  280. line-height: 1.5;
  281. display: block;
  282. margin-bottom: 16rpx;
  283. }
  284. .tip-footer {
  285. display: flex;
  286. flex-direction: row;
  287. align-items: center;
  288. justify-content: space-between;
  289. }
  290. .tip-date {
  291. font-size: 22rpx;
  292. color: #bbb;
  293. }
  294. .tip-tag {
  295. font-size: 22rpx;
  296. color: #5B9BD5;
  297. font-weight: 500;
  298. }
  299. /* ===== 底部 ===== */
  300. .bottom-spacer {
  301. height: 120rpx;
  302. }
  303. </style>