index.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <template>
  2. <view class="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_tip')">
  22. <view class="section-header">
  23. <text class="section-title">每日心理</text>
  24. </view>
  25. <view class="tip-card">
  26. <view class="tip-left-bar"></view>
  27. <view class="tip-body">
  28. <text class="tip-title">{{ dailyTip.title }}</text>
  29. <text class="tip-content">{{ dailyTip.content }}</text>
  30. <view class="tip-footer">
  31. <text class="tip-source">{{ dailyTip.source }}</text>
  32. </view>
  33. </view>
  34. </view>
  35. </view>
  36. <!-- 推荐阅读 -->
  37. <view class="section" v-if="sectionVisible('recommended_reading')">
  38. <view class="section-header">
  39. <text class="section-title">推荐阅读</text>
  40. </view>
  41. <view class="article-list">
  42. <view class="article-card" v-for="article in articles" :key="article.id" @click="goArticleDetail(article.id)">
  43. <view class="article-category" :style="{ background: article.categoryColor }">
  44. <text class="article-category-text">{{ article.category }}</text>
  45. </view>
  46. <text class="article-title">{{ article.title }}</text>
  47. <text class="article-summary">{{ article.summary }}</text>
  48. <view class="article-meta">
  49. <view class="meta-item">
  50. <text class="meta-icon">📖</text>
  51. <text class="meta-text">{{ article.views }}</text>
  52. </view>
  53. <view class="meta-item">
  54. <text class="meta-icon">👍</text>
  55. <text class="meta-text">{{ article.likes }}</text>
  56. </view>
  57. <text class="article-date">{{ article.date }}</text>
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. <!-- 会员专属内容(后台可配置,登录后可见) -->
  63. <view class="section" v-if="sectionVisible('premium_content')">
  64. <view class="section-header">
  65. <text class="section-title">👑 会员专属</text>
  66. </view>
  67. <view class="premium-card">
  68. <text class="premium-text">深度心智测评报告、个性化成长方案等会员专属内容</text>
  69. </view>
  70. </view>
  71. <!-- 底部占位 -->
  72. <view class="bottom-spacer"></view>
  73. </view>
  74. </template>
  75. <script>
  76. import { getVisibleSections, getFeaturedArticles } from '../../utils/api.js'
  77. export default {
  78. data() {
  79. return {
  80. visibleSections: [],
  81. funcList: [
  82. { icon: '\u{1F4D6}', label: '阅读' },
  83. { icon: '\u{1F9E9}', label: '认知' },
  84. { icon: '\u{1F4AA}', label: '专注' },
  85. { icon: '\u{1F3AF}', label: '决策' },
  86. { icon: '\u{1F31F}', label: '心智测评' }
  87. ],
  88. dailyTip: {
  89. title: '成长型思维',
  90. content: '拥有成长型思维的孩子相信自己的能力可以通过努力提升,他们更愿意接受挑战,从失败中学习。作为家长,多鼓励孩子的努力而非天赋,可以帮助孩子建立成长型思维。',
  91. source: '—— 卡罗尔·德韦克《终身成长》'
  92. },
  93. articles: []
  94. }
  95. },
  96. onShow() {
  97. this.resetTabBar()
  98. this.loadSectionConfig()
  99. this.loadFeaturedArticles()
  100. },
  101. methods: {
  102. resetTabBar() {
  103. uni.setTabBarItem({ index: 1, text: '身体' })
  104. uni.setTabBarItem({ index: 2, text: '心智' })
  105. },
  106. async loadSectionConfig() {
  107. var token = uni.getStorageSync('token')
  108. var role = null
  109. if (token) {
  110. role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  111. }
  112. var res
  113. try {
  114. res = await getVisibleSections({ pageKey: 'mind', role: role })
  115. if (res.code === 200 && res.data) {
  116. this.visibleSections = res.data.map(function(s) { return s.sectionKey })
  117. }
  118. } catch (e) {
  119. // 默认显示所有公开区块
  120. this.visibleSections = ['func_entries', 'daily_tip', 'recommended_reading']
  121. }
  122. },
  123. sectionVisible(sectionKey) {
  124. return this.visibleSections.indexOf(sectionKey) !== -1
  125. },
  126. async loadFeaturedArticles() {
  127. try {
  128. const res = await getFeaturedArticles({ size: 5 })
  129. if (res.code === 200 && res.data) {
  130. const gradientColors = [
  131. 'linear-gradient(135deg, #8B5CF6, #A78BFA)',
  132. 'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
  133. 'linear-gradient(135deg, #10B981, #34D399)',
  134. 'linear-gradient(135deg, #8B5CF6, #C084FC)',
  135. 'linear-gradient(135deg, #F97316, #FB923C)'
  136. ]
  137. let list = Array.isArray(res.data) ? res.data : (res.data.records || [])
  138. this.articles = list.map(function(item, index) {
  139. return {
  140. id: item.id,
  141. category: item.categoryName || '成长文章',
  142. categoryColor: gradientColors[index % gradientColors.length],
  143. title: item.title || '',
  144. summary: item.summary || '',
  145. views: item.viewCount ? String(item.viewCount) : '0',
  146. likes: '0',
  147. date: item.publishedAt ? item.publishedAt.slice(0, 10) : ''
  148. }
  149. })
  150. }
  151. } catch (e) {
  152. // 静默处理,保持空列表
  153. }
  154. },
  155. goArticleDetail(id) {
  156. var token = uni.getStorageSync('token')
  157. if (token) {
  158. uni.navigateTo({ url: '/pages/mind/article-detail?id=' + id })
  159. } else {
  160. uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/mind/index') })
  161. }
  162. }
  163. }
  164. }
  165. </script>
  166. <style scoped>
  167. .container {
  168. min-height: 100vh;
  169. background: #f5f7fa;
  170. padding-bottom: 120rpx;
  171. }
  172. /* ===== 品牌头部 ===== */
  173. .brand-header {
  174. display: flex;
  175. flex-direction: column;
  176. align-items: center;
  177. padding: 30rpx 30rpx 16rpx;
  178. background: linear-gradient(180deg, #1A2A4A 0%, #1E3A5F 60%, #f5f7fa 100%);
  179. }
  180. .brand-logo {
  181. width: 80rpx;
  182. height: 80rpx;
  183. border-radius: 18rpx;
  184. margin-bottom: 8rpx;
  185. }
  186. .brand-title {
  187. font-size: 36rpx;
  188. font-weight: bold;
  189. color: #8FC5E8;
  190. letter-spacing: 4rpx;
  191. }
  192. .brand-slogan {
  193. font-size: 22rpx;
  194. color: rgba(143, 197, 232, 0.6);
  195. margin-top: 6rpx;
  196. }
  197. /* ===== 功能入口 ===== */
  198. .func-section {
  199. margin: 20rpx 30rpx;
  200. }
  201. .func-grid {
  202. display: flex;
  203. flex-direction: row;
  204. justify-content: space-between;
  205. background: #fff;
  206. border-radius: 20rpx;
  207. padding: 24rpx 16rpx;
  208. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  209. }
  210. .func-item {
  211. display: flex;
  212. flex-direction: column;
  213. align-items: center;
  214. flex: 1;
  215. }
  216. .func-icon-wrap {
  217. width: 80rpx;
  218. height: 80rpx;
  219. border-radius: 40rpx;
  220. background: linear-gradient(135deg, #D6EAF8, #EBF2FA);
  221. display: flex;
  222. align-items: center;
  223. justify-content: center;
  224. margin-bottom: 10rpx;
  225. }
  226. .func-icon {
  227. font-size: 40rpx;
  228. }
  229. .func-label {
  230. font-size: 22rpx;
  231. color: #666;
  232. font-weight: 500;
  233. }
  234. /* ===== 通用区块 ===== */
  235. .section {
  236. margin: 20rpx 30rpx;
  237. }
  238. .section-header {
  239. margin-bottom: 20rpx;
  240. }
  241. .section-title {
  242. font-size: 30rpx;
  243. font-weight: bold;
  244. color: #333;
  245. }
  246. /* ===== 每日心理 ===== */
  247. .tip-card {
  248. background: #fff;
  249. border-radius: 20rpx;
  250. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  251. display: flex;
  252. flex-direction: row;
  253. overflow: hidden;
  254. }
  255. .tip-left-bar {
  256. width: 6rpx;
  257. background: linear-gradient(180deg, #5B9BD5, #8FC5E8);
  258. flex-shrink: 0;
  259. }
  260. .tip-body {
  261. flex: 1;
  262. padding: 28rpx 24rpx;
  263. }
  264. .tip-title {
  265. font-size: 28rpx;
  266. font-weight: bold;
  267. color: #5B9BD5;
  268. display: block;
  269. margin-bottom: 12rpx;
  270. }
  271. .tip-content {
  272. font-size: 24rpx;
  273. color: #666;
  274. line-height: 1.6;
  275. display: block;
  276. margin-bottom: 16rpx;
  277. }
  278. .tip-footer {
  279. display: flex;
  280. flex-direction: row;
  281. justify-content: flex-end;
  282. }
  283. .tip-source {
  284. font-size: 22rpx;
  285. color: #bbb;
  286. }
  287. /* ===== 推荐阅读 ===== */
  288. .article-list {
  289. display: flex;
  290. flex-direction: column;
  291. gap: 20rpx;
  292. }
  293. .article-card {
  294. background: #fff;
  295. border-radius: 20rpx;
  296. padding: 28rpx 24rpx;
  297. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  298. }
  299. .article-category {
  300. display: inline-block;
  301. padding: 4rpx 18rpx;
  302. border-radius: 20rpx;
  303. margin-bottom: 14rpx;
  304. }
  305. .article-category-text {
  306. font-size: 20rpx;
  307. color: #fff;
  308. font-weight: 500;
  309. }
  310. .article-title {
  311. font-size: 28rpx;
  312. font-weight: bold;
  313. color: #333;
  314. display: block;
  315. margin-bottom: 10rpx;
  316. }
  317. .article-summary {
  318. font-size: 24rpx;
  319. color: #888;
  320. line-height: 1.5;
  321. display: block;
  322. margin-bottom: 20rpx;
  323. }
  324. .article-meta {
  325. display: flex;
  326. flex-direction: row;
  327. align-items: center;
  328. justify-content: space-between;
  329. }
  330. .meta-item {
  331. display: flex;
  332. flex-direction: row;
  333. align-items: center;
  334. margin-right: 20rpx;
  335. }
  336. .meta-icon {
  337. font-size: 22rpx;
  338. margin-right: 4rpx;
  339. }
  340. .meta-text {
  341. font-size: 22rpx;
  342. color: #bbb;
  343. }
  344. .article-date {
  345. font-size: 22rpx;
  346. color: #bbb;
  347. margin-left: auto;
  348. }
  349. /* ===== 会员专属 ===== */
  350. .premium-card {
  351. background: linear-gradient(135deg, #D6EAF8, #EBF2FA);
  352. border-radius: 20rpx;
  353. padding: 30rpx 24rpx;
  354. text-align: center;
  355. }
  356. .premium-text {
  357. font-size: 26rpx;
  358. color: #3A7CC4;
  359. line-height: 1.5;
  360. }
  361. /* ===== 底部 ===== */
  362. .bottom-spacer {
  363. height: 120rpx;
  364. }
  365. </style>