index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. <template>
  2. <view class="body-container">
  3. <!-- Tabbar 切换过渡页 -->
  4. <tab-transition v-if="showTabTransition" dimCode="body" ref="tabTransition" @close="showTabTransition = false" />
  5. <!-- 品牌头部 -->
  6. <PageBanner theme="body"
  7. tagline="科学管理全家健康"
  8. quote="身体健康是幸福的基础" />
  9. <!-- 品牌定位语(登录前显示) -->
  10. <view class="brand-strap" v-if="!isLoggedIn">
  11. <text class="brand-strap-text">科学管理全家健康数据</text>
  12. </view>
  13. <!-- 雷达图(始终显示,无用户数据时仅显示人群平均值) -->
  14. <view class="section">
  15. <view class="section-header">
  16. <text class="section-title">七维健康图</text>
  17. <text class="avg-badge" v-if="!dimensionData">同龄同性平均值 · 上传报告后显示个人数据</text>
  18. </view>
  19. <RadarChart
  20. v-if="radarDimensions && radarDimensions.length >= 3"
  21. :dimensions="radarDimensions"
  22. :scores="radarScores"
  23. :avg-scores="radarAvgScores"
  24. fillColor="#FF8C42"
  25. gridColor="#FED7AA"
  26. labelColor="#9A3412"
  27. :width="580"
  28. :height="580"
  29. @dimensionClick="goDimensionDetail" />
  30. </view>
  31. <!-- 家庭成员选择条 -->
  32. <FamilyMemberStrip
  33. v-if="isLoggedIn"
  34. :members="familyMembersVisible"
  35. :selectedMemberId="selectedMemberId"
  36. @select="onFamilyMemberSelect"
  37. @removed="loadFamilyMembersVisible" />
  38. <!-- 游客引导语(每月更换) -->
  39. <view class="monthly-guidance" v-if="!isLoggedIn">
  40. <text class="guidance-icon">🌿</text>
  41. <text class="guidance-text">{{ monthlyGuidance }}</text>
  42. </view>
  43. <!-- 登录后:五维能量条 -->
  44. <FamilyEnergyBar
  45. v-if="isLoggedIn"
  46. dimensionCode="body"
  47. :sandboxData="sandboxData"
  48. :dualDimension="dualDimension" />
  49. <!-- 功能入口 -->
  50. <view class="func-section" v-if="sectionVisible('func_entries')">
  51. <view class="func-grid">
  52. <view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
  53. <view class="func-icon-wrap">
  54. <text class="func-icon">{{ item.icon }}</text>
  55. </view>
  56. <text class="func-label">{{ item.label }}</text>
  57. </view>
  58. </view>
  59. </view>
  60. <!-- ===== 精准营养(上传报告+产品+文章) ===== -->
  61. <view class="section nutrition-section" v-if="isLoggedIn">
  62. <view class="section-header">
  63. <text class="section-title">🥗 精准营养</text>
  64. </view>
  65. <!-- 上传报告入口 -->
  66. <view class="nutrition-report-card" @click="goToUploadReport">
  67. <view class="nutrition-report-left">
  68. <text class="nutrition-icon-large">📋</text>
  69. </view>
  70. <view class="nutrition-report-right">
  71. <text class="nutrition-title">上传健康报告</text>
  72. <text class="nutrition-desc">上传体检报告或肠道检测报告,AI智能解读分析,获取个性化营养方案</text>
  73. <view class="nutrition-report-tags">
  74. <text class="tag">体检报告</text>
  75. <text class="tag">肠道检测</text>
  76. <text class="tag">AI解读</text>
  77. </view>
  78. </view>
  79. <text class="nutrition-arrow">›</text>
  80. </view>
  81. </view>
  82. <!-- 身维度介绍卡片(登录后显示) -->
  83. <DimensionIntroCard dimCode="body" v-if="isLoggedIn" learn-more-url="/pages/body-detail/dimension-intro" />
  84. <!-- 底部占位 -->
  85. <view class="bottom-spacer"></view>
  86. <AIFloatingAvatar />
  87. </view>
  88. </template>
  89. <script>
  90. import TabTransition from '../../components/tab-transition.vue'
  91. import PageBanner from '../../components/PageBanner.vue'
  92. import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
  93. import RadarChart from '../../components/RadarChart.vue'
  94. import AIFloatingAvatar from '../../components/AIFloatingAvatar.vue'
  95. import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
  96. import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
  97. import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getVisibleFamilyMembers, getDimensionOverview } from '../../utils/api.js'
  98. export default {
  99. components: { TabTransition, PageBanner, FamilyEnergyBar, RadarChart, AIFloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
  100. data() {
  101. return {
  102. isLoggedIn: false,
  103. role: null,
  104. showTabTransition: true,
  105. activeChildId: null,
  106. selectedMemberId: null,
  107. activeChildName: '',
  108. userName: '',
  109. children: [],
  110. familyMembersVisible: [],
  111. // 接口数据是否已检查(避免"暂无数据"闪烁)
  112. childrenChecked: false,
  113. membersChecked: false,
  114. sandboxData: null,
  115. dualDimension: null,
  116. visibleSections: [],
  117. dimensionData: null,
  118. /** Mock 平均值(7维):生长发育/睡眠质量/视力健康/免疫力/营养均衡/肠胃健康/运动活力 */
  119. radarAvgScores: [72, 68, 75, 70, 73, 69, 71],
  120. funcList: [
  121. { icon: '\u{1F3C3}', label: '运动', needLogin: true, page: '/pages/body/exercise-record' },
  122. { icon: '\u{1F957}', label: '饮食', needLogin: true, page: '/pages/diet/index' },
  123. { icon: '\u{1F634}', label: '作息', needLogin: true, page: '/pages/body/sleep-record' },
  124. { icon: '\u{1F9D8}', label: '冥想', needLogin: true, page: '/pages/health/meditation-index' },
  125. { icon: '\u{1F4CA}', label: '舌诊', needLogin: true, page: '/pages/health/report-upload?reportType=tongue' },
  126. // AI问答功能已屏蔽: { icon: '\u{1F3E5}', label: '健康管家', needLogin: true, page: '/pages/ai/chat?mode=butler&fromPage=pages/body/index' },
  127. { icon: '\u{1F9EC}', label: '健康维度', needLogin: true, page: 'health-dimensions' }
  128. ]
  129. }
  130. },
  131. computed: {
  132. /** 七维雷达图:维度标签(API无数据时使用默认标签) */
  133. radarDimensions: function() {
  134. var dims = this.dimensionData && this.dimensionData.dimensions
  135. if (dims && dims.length > 0) {
  136. var result = []
  137. for (var i = 0; i < dims.length; i++) {
  138. var d = dims[i]
  139. if (!d) continue
  140. result.push({ label: d.label || '', key: d.dimension || '' })
  141. }
  142. return result
  143. }
  144. // 无数据时使用默认七维标签
  145. return [
  146. { label: '生长发育', key: 'growth' },
  147. { label: '睡眠质量', key: 'sleep' },
  148. { label: '视力健康', key: 'vision' },
  149. { label: '免疫力', key: 'immunity' },
  150. { label: '营养均衡', key: 'nutrition' },
  151. { label: '肠胃健康', key: 'gut' },
  152. { label: '运动活力', key: 'exercise' }
  153. ]
  154. },
  155. /** 七维雷达图:分值(无数据时返回空数组,仅显示平均值) */
  156. radarScores: function() {
  157. var dims = this.dimensionData && this.dimensionData.dimensions
  158. if (!dims) return []
  159. var result = []
  160. for (var i = 0; i < dims.length; i++) {
  161. var d = dims[i]
  162. if (!d) { result.push(0); continue }
  163. var s = d.score
  164. if (typeof s !== 'number' || isNaN(s) || s === null) s = 0
  165. result.push(s)
  166. }
  167. return result
  168. },
  169. /** 每月引导语 */
  170. monthlyGuidance: function() {
  171. var month = new Date().getMonth()
  172. var messages = [
  173. '一月:新年新健康,从一次体检开始',
  174. '二月:春暖花开,适合调整作息',
  175. '三月:植树节到,守护呼吸健康',
  176. '四月:踏青正当时,关注视力保护',
  177. '五月:劳动节后,重视营养均衡',
  178. '六月:夏日来临,关注肠胃健康',
  179. '七月:暑期到来,规律运动不可少',
  180. '八月:秋前调养,提升免疫力',
  181. '九月:开学季,关注孩子生长发育',
  182. '十月:国庆长假,注意饮食控制',
  183. '十一月:秋冬交替,保暖防寒',
  184. '十二月:年终总结,规划来年健康'
  185. ]
  186. return messages[month] || messages[0]
  187. }
  188. },
  189. onShow() {
  190. this.showTabTransition = true
  191. var token = uni.getStorageSync('token')
  192. this.isLoggedIn = !!token
  193. if (this.isLoggedIn) {
  194. this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  195. this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
  196. }
  197. this.loadSectionConfig()
  198. if (this.isLoggedIn) {
  199. this.loadChildren()
  200. this.loadFamilyMembersVisible()
  201. }
  202. // 游客也能浏览活动和商品
  203. // 游客也能浏览文章
  204. this._watchPageReady()
  205. },
  206. methods: {
  207. loadSectionConfig: function() {
  208. var baseSections = ['func_entries']
  209. if (!uni.getStorageSync('token')) {
  210. this.visibleSections = baseSections
  211. return
  212. }
  213. var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
  214. var self = this
  215. getVisibleSections({ pageKey: 'body', role: role }).then(function(res) {
  216. if (res.code === 200 && res.data) {
  217. var keys = res.data.map(function(s) { return s.sectionKey })
  218. var merged = baseSections.slice()
  219. keys.forEach(function(k) { if (merged.indexOf(k) === -1) merged.push(k) })
  220. self.visibleSections = merged
  221. }
  222. }).catch(function(e) {
  223. self.visibleSections = ['func_entries']
  224. })
  225. },
  226. loadChildren: function() {
  227. var self = this
  228. getChildren().then(function(res) {
  229. if (res.code === 200 && res.data) {
  230. self.children = res.data
  231. var savedChildId = uni.getStorageSync('currentChildId')
  232. var found = false
  233. for (var i = 0; i < self.children.length; i++) {
  234. if (self.children[i].memberId === savedChildId) {
  235. self.activeChildId = savedChildId
  236. self.activeChildName = self.children[i].name || self.children[i].nickname || '孩子'
  237. found = true
  238. break
  239. }
  240. }
  241. if (!found && self.children.length > 0) {
  242. self.activeChildId = self.children[0].memberId
  243. self.activeChildName = self.children[0].name || self.children[0].nickname || '孩子'
  244. uni.setStorageSync('currentChildId', self.activeChildId)
  245. }
  246. if (self.activeChildId) {
  247. self.loadDashboardData()
  248. }
  249. }
  250. self.childrenChecked = true
  251. }).catch(function(e) {
  252. console.log('获取孩子列表失败', e)
  253. self.childrenChecked = true
  254. })
  255. },
  256. loadDashboardData: function() {
  257. if (!this.activeChildId) return
  258. this.loadEnergyData()
  259. this.loadSandboxData()
  260. this.loadHealthDimensions()
  261. },
  262. loadHealthDimensions: function() {
  263. var memberId = this.activeChildId || (this.userInfo && this.userInfo.userId)
  264. if (!memberId) {
  265. console.log('[body] loadHealthDimensions skipped: no memberId')
  266. return
  267. }
  268. var self = this
  269. getDimensionOverview({ memberId: memberId }).then(function(res) {
  270. if (res && res.data) {
  271. self.dimensionData = res.data
  272. console.log('[body] dimensionData loaded:', res.data.dimensions ? res.data.dimensions.length : 0, 'dimensions')
  273. } else if (res) {
  274. console.warn('[body] dimension API error:', res.message || res.code)
  275. }
  276. }).catch(function(e) {
  277. console.error('[body] loadHealthDimensions failed', e)
  278. })
  279. },
  280. loadSandboxData: function() {
  281. var self = this
  282. getFamilyEnergySandbox().then(function(res) {
  283. if (res && res.data) {
  284. self.sandboxData = res.data
  285. }
  286. }).catch(function(e) {
  287. console.log('获取沙盘数据失败', e)
  288. })
  289. },
  290. loadEnergyData: function() {
  291. var self = this
  292. getEnergyOverview(this.activeChildId).then(function(res) {
  293. if (res && res.data) {
  294. var dims = res.data.dimensions
  295. if (dims && dims.length > 0) {
  296. for (var i = 0; i < dims.length; i++) {
  297. if (dims[i].code === 'body') {
  298. self.dualDimension = dims[i]
  299. break
  300. }
  301. }
  302. }
  303. }
  304. }).catch(function(e) {
  305. console.log('获取能量概览失败', e)
  306. })
  307. },
  308. onFamilyMemberSelect: function(member) {
  309. this.selectedMemberId = member.id
  310. },
  311. onChildChanged: function(child) {
  312. this.activeChildId = child.memberId
  313. this.activeChildName = child.name || child.nickname || '孩子'
  314. this.loadDashboardData()
  315. },
  316. loadFamilyMembersVisible: function() {
  317. var self = this
  318. getVisibleFamilyMembers().then(function(res) {
  319. self.familyMembersVisible = res.data || []
  320. self.membersChecked = true
  321. }).catch(function(e) {
  322. console.log('获取可见家庭成员失败', e)
  323. self.familyMembersVisible = []
  324. self.membersChecked = true
  325. })
  326. },
  327. scrollToSection: function(section) {
  328. uni.pageScrollTo({ selector: '#' + section, duration: 300 })
  329. },
  330. sectionVisible: function(key) {
  331. return this.visibleSections.length === 0 || this.visibleSections.indexOf(key) !== -1
  332. },
  333. onFuncClick: function(item) {
  334. if (item.needLogin && !this.isLoggedIn) {
  335. uni.showModal({
  336. title: '提示',
  337. content: '请先登录后使用此功能',
  338. success: function(res) {
  339. if (res.confirm) uni.navigateTo({ url: '/pages/login/login' })
  340. }
  341. })
  342. return
  343. }
  344. if (item.page === 'health-dimensions') {
  345. if (!this.activeChildId) {
  346. uni.showToast({ title: '请先选择孩子', icon: 'none' })
  347. return
  348. }
  349. uni.navigateTo({ url: '/pages/body-detail/health-dimensions?memberId=' + this.activeChildId })
  350. return
  351. }
  352. if (item.page) uni.navigateTo({ url: item.page })
  353. },
  354. goDimensionDetail: function(dimKey) {
  355. uni.navigateTo({ url: '/pages/body-detail/dimension-detail?dimension=' + dimKey + '&memberId=' + this.activeChildId })
  356. },
  357. goToCheckin: function() {
  358. var id = this.activeChildId || uni.getStorageSync('currentChildId')
  359. if (!id) {
  360. uni.showToast({ title: '请先选择成员', icon: 'none' })
  361. return
  362. }
  363. uni.navigateTo({ url: '/pages/body-detail/checkin?memberId=' + id })
  364. },
  365. goToNutritionProfile: function() {
  366. var id = this.activeChildId || uni.getStorageSync('currentChildId')
  367. if (!id) {
  368. uni.showToast({ title: '请先选择成员', icon: 'none' })
  369. return
  370. }
  371. uni.navigateTo({ url: '/pages/health/nutrition-profile?memberId=' + id })
  372. },
  373. goToUploadReport: function() {
  374. var id = this.activeChildId || uni.getStorageSync('currentChildId') || ''
  375. uni.navigateTo({ url: '/pages/health/report-upload?memberId=' + id + '&from=body' })
  376. },
  377. _watchPageReady() {
  378. var self = this
  379. var unwatch = this.$watch(function() {
  380. if (self.isLoggedIn) {
  381. return !!(self.dualDimension || self.sandboxData)
  382. } }, function(val) {
  383. if (val) {
  384. if (unwatch) unwatch()
  385. if (self.$refs.tabTransition) self.$refs.tabTransition.markReady()
  386. }
  387. })
  388. }
  389. }
  390. }
  391. </script>
  392. <style scoped>
  393. .body-container {
  394. min-height: 100vh;
  395. background: #f5f7fa;
  396. padding-bottom: 120rpx;
  397. }
  398. /* ===== 功能入口 ===== */
  399. .func-section {
  400. margin: 20rpx 30rpx;
  401. }
  402. .func-grid {
  403. display: flex;
  404. flex-direction: row;
  405. justify-content: space-between;
  406. background: #fff;
  407. border-radius: 20rpx;
  408. padding: 24rpx 16rpx;
  409. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  410. }
  411. .func-item {
  412. display: flex;
  413. flex-direction: column;
  414. align-items: center;
  415. flex: 1;
  416. }
  417. .func-icon-wrap {
  418. width: 80rpx;
  419. height: 80rpx;
  420. border-radius: 40rpx;
  421. background: linear-gradient(135deg, #D6EAF8, #EBF2FA);
  422. display: flex;
  423. align-items: center;
  424. justify-content: center;
  425. margin-bottom: 10rpx;
  426. }
  427. .func-icon {
  428. font-size: 40rpx;
  429. }
  430. .func-label {
  431. font-size: 22rpx;
  432. color: #666;
  433. font-weight: 500;
  434. }
  435. /* ===== 通用区块 ===== */
  436. .section {
  437. margin: 20rpx 30rpx;
  438. }
  439. .section-header {
  440. display: flex;
  441. flex-direction: row;
  442. align-items: center;
  443. justify-content: space-between;
  444. margin-bottom: 16rpx;
  445. }
  446. .section-title {
  447. font-size: 30rpx;
  448. font-weight: bold;
  449. color: #333;
  450. }
  451. .avg-badge {
  452. font-size: 20rpx;
  453. color: #F97316;
  454. background: #FFF7ED;
  455. padding: 4rpx 12rpx;
  456. border-radius: 20rpx;
  457. margin-left: 12rpx;
  458. }
  459. /* ===== 快捷操作 ===== */
  460. .quick-actions {
  461. display: flex;
  462. flex-direction: row;
  463. margin-top: 30rpx;
  464. }
  465. .action-btn {
  466. flex: 1;
  467. background: #fff;
  468. border-radius: 16rpx;
  469. padding: 24rpx;
  470. display: flex;
  471. flex-direction: column;
  472. align-items: center;
  473. margin-right: 20rpx;
  474. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  475. }
  476. .action-btn:last-child {
  477. margin-right: 0;
  478. }
  479. .action-icon {
  480. font-size: 48rpx;
  481. margin-bottom: 8rpx;
  482. }
  483. .action-text {
  484. font-size: 26rpx;
  485. color: #666;
  486. }
  487. /* ===== 底部 ===== */
  488. .bottom-spacer {
  489. height: 120rpx;
  490. }
  491. .func-item:active, .action-btn:active {
  492. opacity: 0.7;
  493. }
  494. /* ===== 精准营养 ===== */
  495. .nutrition-section {
  496. margin-top: 20rpx;
  497. }
  498. .nutrition-report-card {
  499. display: flex;
  500. flex-direction: row;
  501. align-items: center;
  502. background: linear-gradient(135deg, #E8F5E9, #F1F8E9);
  503. border-radius: 20rpx;
  504. padding: 28rpx 24rpx;
  505. margin-bottom: 20rpx;
  506. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  507. }
  508. .nutrition-report-left {
  509. width: 100rpx;
  510. height: 100rpx;
  511. background: rgba(255,255,255,0.7);
  512. border-radius: 20rpx;
  513. display: flex;
  514. align-items: center;
  515. justify-content: center;
  516. margin-right: 20rpx;
  517. flex-shrink: 0;
  518. }
  519. .nutrition-icon-large {
  520. font-size: 52rpx;
  521. }
  522. .nutrition-report-right {
  523. flex: 1;
  524. }
  525. .nutrition-title {
  526. font-size: 30rpx;
  527. font-weight: bold;
  528. color: #2E7D32;
  529. display: block;
  530. margin-bottom: 6rpx;
  531. }
  532. .nutrition-desc {
  533. font-size: 22rpx;
  534. color: #558B2F;
  535. line-height: 1.5;
  536. display: block;
  537. margin-bottom: 10rpx;
  538. }
  539. .nutrition-report-tags {
  540. display: flex;
  541. flex-direction: row;
  542. gap: 10rpx;
  543. }
  544. .nutrition-report-tags .tag {
  545. font-size: 20rpx;
  546. color: #33691E;
  547. background: rgba(255,255,255,0.6);
  548. padding: 4rpx 14rpx;
  549. border-radius: 20rpx;
  550. }
  551. .nutrition-arrow {
  552. font-size: 40rpx;
  553. color: #A5D6A7;
  554. margin-left: 12rpx;
  555. }
  556. .nutrition-links {
  557. margin-bottom: 20rpx;
  558. }
  559. .links-label {
  560. font-size: 26rpx;
  561. font-weight: bold;
  562. color: #333;
  563. display: block;
  564. margin-bottom: 12rpx;
  565. }
  566. .links-row {
  567. display: flex;
  568. flex-direction: row;
  569. gap: 16rpx;
  570. }
  571. .link-item {
  572. flex: 1;
  573. display: flex;
  574. flex-direction: row;
  575. align-items: center;
  576. background: #fff;
  577. border-radius: 16rpx;
  578. padding: 20rpx 18rpx;
  579. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  580. gap: 12rpx;
  581. }
  582. .link-item:active {
  583. opacity: 0.7;
  584. }
  585. .link-icon {
  586. font-size: 36rpx;
  587. flex-shrink: 0;
  588. }
  589. .link-texts {
  590. flex: 1;
  591. }
  592. .link-name {
  593. font-size: 24rpx;
  594. font-weight: 500;
  595. color: #333;
  596. display: block;
  597. margin-bottom: 4rpx;
  598. }
  599. .link-desc {
  600. font-size: 20rpx;
  601. color: #999;
  602. display: block;
  603. }
  604. .link-arrow {
  605. font-size: 32rpx;
  606. color: #CCC;
  607. flex-shrink: 0;
  608. }
  609. /* ===== 推荐阅读 ===== */
  610. .article-list {
  611. display: flex;
  612. flex-direction: column;
  613. gap: 16rpx;
  614. }
  615. .article-card {
  616. background: #fff;
  617. border-radius: 16rpx;
  618. padding: 24rpx;
  619. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  620. }
  621. .article-card:active {
  622. opacity: 0.8;
  623. }
  624. .article-category {
  625. display: inline-block;
  626. padding: 4rpx 14rpx;
  627. border-radius: 8rpx;
  628. margin-bottom: 10rpx;
  629. }
  630. .article-category-text {
  631. font-size: 20rpx;
  632. color: #fff;
  633. }
  634. .article-title {
  635. display: block;
  636. font-size: 28rpx;
  637. font-weight: bold;
  638. color: #333;
  639. margin-bottom: 8rpx;
  640. line-height: 1.4;
  641. }
  642. .article-summary {
  643. display: block;
  644. font-size: 24rpx;
  645. color: #888;
  646. line-height: 1.5;
  647. margin-bottom: 12rpx;
  648. overflow: hidden;
  649. text-overflow: ellipsis;
  650. display: -webkit-box;
  651. -webkit-line-clamp: 2;
  652. -webkit-box-orient: vertical;
  653. }
  654. .article-meta {
  655. display: flex;
  656. align-items: center;
  657. gap: 16rpx;
  658. }
  659. .meta-item {
  660. display: flex;
  661. align-items: center;
  662. gap: 4rpx;
  663. }
  664. .meta-icon {
  665. font-size: 24rpx;
  666. }
  667. .meta-text {
  668. font-size: 22rpx;
  669. color: #999;
  670. }
  671. .article-date {
  672. font-size: 22rpx;
  673. color: #bbb;
  674. }
  675. /* 品牌定位语 */
  676. .brand-strap {
  677. margin: 16rpx 30rpx 0;
  678. padding: 20rpx 28rpx;
  679. background: linear-gradient(135deg, #FFF7ED, rgba(255,140,66,0.12));
  680. border-radius: 16rpx;
  681. border-left: 4rpx solid #FF8C42;
  682. }
  683. .brand-strap-text {
  684. font-size: 26rpx;
  685. color: #9A3412;
  686. font-weight: 500;
  687. }
  688. /* 每月引导语 */
  689. .monthly-guidance {
  690. margin: 16rpx 30rpx 0;
  691. padding: 20rpx 28rpx;
  692. background: linear-gradient(135deg, #FFF7ED, rgba(255,140,66,0.08));
  693. border-radius: 16rpx;
  694. border-left: 4rpx solid #FF8C42;
  695. display: flex;
  696. align-items: center;
  697. gap: 12rpx;
  698. }
  699. .guidance-icon {
  700. font-size: 32rpx;
  701. }
  702. .guidance-text {
  703. font-size: 26rpx;
  704. color: #9A3412;
  705. font-weight: 500;
  706. }
  707. </style>