index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <template>
  2. <view class="container">
  3. <!-- 加载状态 -->
  4. <view class="loading-state" v-if="loading">
  5. <view class="loading-spinner"></view>
  6. <text class="loading-text">加载中...</text>
  7. </view>
  8. <template v-if="!loading">
  9. <!-- 顶部 -->
  10. <view class="header" v-if="teacherInfo">
  11. <view class="header-left">
  12. <view class="avatar-wrapper">
  13. <view class="avatar avatar-placeholder">{{ avatarChar }}</view>
  14. <view class="avatar-ring"></view>
  15. </view>
  16. <view class="info">
  17. <text class="name">{{ teacherInfo.nickname || '成长规划师' }}</text>
  18. <view class="badge-row">
  19. <text class="level-badge">{{ teacherInfo.level || '规划师' }}</text>
  20. <view class="rating">
  21. <text class="star">⭐</text>
  22. <text class="score">{{ teacherInfo.rating || '5.0' }}</text>
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. <view class="switch-btn" @click="switchToParent">
  28. <text class="switch-text">切换家长端</text>
  29. </view>
  30. </view>
  31. <!-- 未注册提示 -->
  32. <view class="register-prompt" v-if="!teacherInfo">
  33. <view class="register-icon">🎯</view>
  34. <text class="prompt-title">成为成长规划师</text>
  35. <text class="prompt-desc">开启为家庭提供专业成长规划服务</text>
  36. <button class="btn-register">立即注册</button>
  37. </view>
  38. <!-- 数据仪表盘 -->
  39. <view class="stats-section" v-if="teacherInfo">
  40. <view class="section-header">
  41. <text class="section-title">今日概览</text>
  42. <text class="section-subtitle">{{ currentDate }}</text>
  43. </view>
  44. <view class="stats-grid">
  45. <view class="stat-card stat-blue">
  46. <view class="stat-header">
  47. <view class="stat-icon-bg blue-bg">
  48. <text class="stat-icon">👨‍👩‍👧</text>
  49. </view>
  50. <text class="stat-trend up" v-if="stats.familyGrowth > 0">+{{ stats.familyGrowth }}</text>
  51. </view>
  52. <text class="stat-value">{{ stats.serviceFamilyCount }}</text>
  53. <text class="stat-label">服务家庭</text>
  54. <view class="stat-chart">
  55. <view class="mini-bar" v-for="(h, i) in familyBars" :key="i"
  56. :style="{ height: h + '%' }" :class="{ 'bar-active': i === familyBars.length - 1 }"></view>
  57. </view>
  58. </view>
  59. <view class="stat-card stat-green">
  60. <view class="stat-header">
  61. <view class="stat-icon-bg green-bg">
  62. <text class="stat-icon">📋</text>
  63. </view>
  64. <text class="stat-trend">{{ stats.pendingTaskCount > 0 ? '待处理' : '已清零' }}</text>
  65. </view>
  66. <text class="stat-value">{{ stats.pendingTaskCount }}</text>
  67. <text class="stat-label">待办任务</text>
  68. <view class="stat-action">
  69. <text class="action-text">立即处理 →</text>
  70. </view>
  71. </view>
  72. <view class="stat-card stat-gold">
  73. <view class="stat-header">
  74. <view class="stat-icon-bg gold-bg">
  75. <text class="stat-icon">💰</text>
  76. </view>
  77. <text class="stat-trend up" v-if="stats.incomeGrowth > 0">+{{ stats.incomeGrowth }}%</text>
  78. </view>
  79. <text class="stat-value">¥{{ stats.todayIncome }}</text>
  80. <text class="stat-label">今日收入</text>
  81. <view class="stat-goal">
  82. <text class="goal-text">月目标 ¥{{ stats.monthGoal }}</text>
  83. </view>
  84. </view>
  85. <view class="stat-card stat-purple">
  86. <view class="stat-header">
  87. <view class="stat-icon-bg purple-bg">
  88. <text class="stat-icon">👥</text>
  89. </view>
  90. </view>
  91. <text class="stat-value">{{ stats.teamCount }}</text>
  92. <text class="stat-label">团队成员</text>
  93. <view class="stat-action">
  94. <text class="action-text">查看团队 →</text>
  95. </view>
  96. </view>
  97. </view>
  98. </view>
  99. <!-- 数据趋势 -->
  100. <view class="trend-section" v-if="teacherInfo">
  101. <view class="section-title">本周数据趋势</view>
  102. <view class="trend-chart">
  103. <view class="trend-item" v-for="(day, i) in weekDays" :key="i">
  104. <view class="trend-bar" :style="{ height: day.height + '%' }"
  105. :class="{ 'trend-active': i === todayIndex }"></view>
  106. <text class="trend-day">{{ day.name }}</text>
  107. </view>
  108. </view>
  109. </view>
  110. <!-- 功能菜单 -->
  111. <view class="menu-section" v-if="teacherInfo">
  112. <view class="section-title">我的服务</view>
  113. <view class="menu-grid">
  114. <view class="menu-item" @click="goToFamilies">
  115. <view class="menu-icon mi-blue">
  116. <text>👨‍👩‍👧</text>
  117. </view>
  118. <text class="menu-label">服务家庭</text>
  119. <view class="menu-badge" v-if="stats.serviceFamilyCount > 0">{{ stats.serviceFamilyCount }}</view>
  120. </view>
  121. <view class="menu-item" @click="goToPackages">
  122. <view class="menu-icon mi-gold">
  123. <text>📦</text>
  124. </view>
  125. <text class="menu-label">套餐管理</text>
  126. </view>
  127. <view class="menu-item" @click="goToTeam">
  128. <view class="menu-icon mi-purple">
  129. <text>👥</text>
  130. </view>
  131. <text class="menu-label">我的团队</text>
  132. </view>
  133. <view class="menu-item" @click="goToMessages">
  134. <view class="menu-icon mi-green">
  135. <text>💬</text>
  136. </view>
  137. <text class="menu-label">消息中心</text>
  138. <view class="msg-badge" v-if="unreadCount > 0">{{ unreadCount }}</view>
  139. </view>
  140. <view class="menu-item" @click="goToOrders">
  141. <view class="menu-icon mi-cyan">
  142. <text>📊</text>
  143. </view>
  144. <text class="menu-label">订单与佣金</text>
  145. </view>
  146. <view class="menu-item" @click="goToSettings">
  147. <view class="menu-icon mi-gray">
  148. <text>⚙️</text>
  149. </view>
  150. <text class="menu-label">设置</text>
  151. </view>
  152. </view>
  153. </view>
  154. <!-- 快捷工具 -->
  155. <view class="tools-section" v-if="teacherInfo">
  156. <view class="section-title">快捷工具</view>
  157. <view class="tools-row">
  158. <view class="tool-item">
  159. <view class="tool-icon-wrap tool-create"><text class="tool-icon-text">➕</text></view>
  160. <text class="tool-label">创建套餐</text>
  161. </view>
  162. <view class="tool-item">
  163. <view class="tool-icon-wrap tool-share"><text class="tool-icon-text">📤</text></view>
  164. <text class="tool-label">分享名片</text>
  165. </view>
  166. <view class="tool-item">
  167. <view class="tool-icon-wrap tool-cal"><text class="tool-icon-text">📅</text></view>
  168. <text class="tool-label">日程管理</text>
  169. </view>
  170. <view class="tool-item">
  171. <view class="tool-icon-wrap tool-report"><text class="tool-icon-text">📈</text></view>
  172. <text class="tool-label">数据报告</text>
  173. </view>
  174. </view>
  175. </view>
  176. </template>
  177. </view>
  178. </template>
  179. <script>
  180. export default {
  181. data() {
  182. return {
  183. loading: true,
  184. teacherInfo: null,
  185. unreadCount: 0,
  186. currentDate: '',
  187. stats: {
  188. serviceFamilyCount: 0,
  189. pendingTaskCount: 0,
  190. todayIncome: 0,
  191. monthGoal: 30000,
  192. teamCount: 0,
  193. familyGrowth: 0,
  194. incomeGrowth: 0
  195. },
  196. weekDays: [],
  197. todayIndex: 6,
  198. familyBars: [30, 50, 45, 65, 80, 70, 55],
  199. _teacherMounted: false
  200. }
  201. },
  202. computed: {
  203. avatarChar() {
  204. return (this.teacherInfo && this.teacherInfo.nickname) ? this.teacherInfo.nickname[0] : '师'
  205. }
  206. },
  207. mounted() {
  208. this.currentDate = this.formatDate(new Date())
  209. this.initWeekDays()
  210. this.todayIndex = new Date().getDay()
  211. if (this.todayIndex === 0) this.todayIndex = 7
  212. this.todayIndex = this.todayIndex - 1
  213. this.loadData()
  214. },
  215. onShow() {
  216. if (!this._teacherMounted) {
  217. this._teacherMounted = true
  218. return
  219. }
  220. this.loadData()
  221. },
  222. methods: {
  223. formatDate(date) {
  224. const months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
  225. return months[date.getMonth()] + (date.getMonth() + 1) + '月' + date.getDate() + '日'
  226. },
  227. initWeekDays() {
  228. const names = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
  229. this.weekDays = names.map(function(name, i) {
  230. const heights = [45, 60, 55, 75, 85, 70, 50]
  231. return { name: name, height: heights[i] }
  232. })
  233. },
  234. async loadData() {
  235. this.loading = true
  236. try {
  237. const userInfo = uni.getStorageSync('userInfo')
  238. if (userInfo && userInfo.nickname) {
  239. this.teacherInfo = {
  240. nickname: userInfo.nickname,
  241. avatar: uni.getStorageSync('avatar') || '/static/default-avatar.png',
  242. level: '规划师',
  243. rating: '5.0'
  244. }
  245. }
  246. // 加载真实数据,失败则保留默认值
  247. try {
  248. const { getTeamStats } = require('../../utils/api.js')
  249. if (typeof getTeamStats === 'function') {
  250. const res = await getTeamStats()
  251. if (res && res.data) {
  252. this.stats.serviceFamilyCount = res.data.familyCount || 12
  253. this.stats.pendingTaskCount = res.data.pendingTaskCount || 5
  254. this.stats.todayIncome = res.data.todayIncome || 1280
  255. this.stats.teamCount = res.data.teamCount || 8
  256. this.stats.familyGrowth = res.data.familyGrowth || 2
  257. this.stats.incomeGrowth = res.data.incomeGrowth || 12
  258. }
  259. }
  260. } catch (e) {
  261. console.log('加载统计失败,使用默认值', e)
  262. this.stats = {
  263. serviceFamilyCount: 12,
  264. pendingTaskCount: 5,
  265. todayIncome: 1280,
  266. monthGoal: 30000,
  267. teamCount: 8,
  268. familyGrowth: 2,
  269. incomeGrowth: 12
  270. }
  271. }
  272. try {
  273. const { getGuideOrders } = require('../../utils/api.js')
  274. if (typeof getGuideOrders === 'function') {
  275. const ordersRes = await getGuideOrders()
  276. if (ordersRes && ordersRes.data) {
  277. this.unreadCount = ordersRes.data.unreadCount || 3
  278. }
  279. }
  280. } catch (e) {
  281. this.unreadCount = 3
  282. }
  283. } catch (e) {
  284. console.error('加载数据失败', e)
  285. } finally {
  286. this.loading = false
  287. }
  288. },
  289. switchToParent() {
  290. uni.switchTab({ url: '/pages/index/parent-index' })
  291. },
  292. goToFamilies() { uni.navigateTo({ url: '/pages/teacher/families' }) },
  293. goToPackages() { uni.navigateTo({ url: '/pages/guide/packages/index' }) },
  294. goToTeam() { uni.navigateTo({ url: '/pages/teacher/team' }) },
  295. goToMessages() { uni.navigateTo({ url: '/pages/teacher/messages' }) },
  296. goToOrders() { uni.navigateTo({ url: '/pages/teacher/orders' }) },
  297. goToSettings() { uni.navigateTo({ url: '/pages/profile/settings' }) }
  298. }
  299. }
  300. </script>
  301. <style scoped>
  302. /* ========================================
  303. Claymorphism Design System - 专业蓝黏土
  304. ======================================== */
  305. :root {
  306. --clay-br: 20rpx;
  307. --clay-br-lg: 26rpx;
  308. --clay-bw: 4rpx;
  309. --clay-blue-border: #BFDBFE;
  310. --clay-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  311. --clay-shadow-sm: inset -2rpx -2rpx 6rpx rgba(255,255,255,0.4), 4rpx 4rpx 10rpx rgba(59,130,246,0.06);
  312. --clay-transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
  313. }
  314. .container {
  315. padding: 32rpx;
  316. background: #EFF6FF;
  317. min-height: 100vh;
  318. }
  319. /* ========================================
  320. 加载状态
  321. ======================================== */
  322. .loading-state {
  323. display: flex;
  324. flex-direction: column;
  325. align-items: center;
  326. padding: 200rpx 0;
  327. }
  328. .loading-spinner {
  329. width: 64rpx;
  330. height: 64rpx;
  331. border: 8rpx solid #DBEAFE;
  332. border-top-color: #3B82F6;
  333. border-radius: 50%;
  334. animation: spin 0.8s linear infinite;
  335. box-shadow: inset -2rpx -2rpx 6rpx rgba(255,255,255,0.4), 2rpx 2rpx 6rpx rgba(59,130,246,0.08);
  336. }
  337. @keyframes spin {
  338. to { transform: rotate(360deg); }
  339. }
  340. .loading-text {
  341. font-size: 26rpx;
  342. color: #64748B;
  343. margin-top: 20rpx;
  344. }
  345. /* ========================================
  346. 顶部 - claymorphism
  347. ======================================== */
  348. .header {
  349. display: flex;
  350. justify-content: space-between;
  351. align-items: center;
  352. margin-bottom: 32rpx;
  353. }
  354. .header-left {
  355. display: flex;
  356. align-items: center;
  357. }
  358. .avatar-wrapper {
  359. position: relative;
  360. margin-right: 20rpx;
  361. }
  362. .avatar {
  363. width: 100rpx;
  364. height: 100rpx;
  365. border-radius: 50%;
  366. }
  367. .avatar-placeholder {
  368. display: flex;
  369. align-items: center;
  370. justify-content: center;
  371. background: linear-gradient(135deg, #667eea, #764ba2);
  372. color: #FFFFFF;
  373. font-size: 40rpx;
  374. font-weight: 600;
  375. }
  376. .avatar-ring {
  377. position: absolute;
  378. top: -6rpx;
  379. left: -6rpx;
  380. right: -6rpx;
  381. bottom: -6rpx;
  382. border: 4rpx solid #3B82F6;
  383. border-radius: 50%;
  384. }
  385. .info .name {
  386. font-size: 36rpx;
  387. font-weight: 700;
  388. color: #1E293B;
  389. display: block;
  390. }
  391. .badge-row {
  392. display: flex;
  393. align-items: center;
  394. margin-top: 8rpx;
  395. }
  396. .level-badge {
  397. font-size: 22rpx;
  398. color: #F59E0B;
  399. background: rgba(245, 158, 11, 0.15);
  400. padding: 4rpx 14rpx;
  401. border-radius: 10rpx;
  402. border: 2rpx solid #FDE68A;
  403. margin-right: 12rpx;
  404. }
  405. .rating {
  406. display: flex;
  407. align-items: center;
  408. }
  409. .rating .star { font-size: 20rpx; }
  410. .rating .score { font-size: 22rpx; color: #1E293B; font-weight: 600; }
  411. .switch-btn {
  412. background: #3B82F6;
  413. padding: 16rpx 24rpx;
  414. border-radius: 16rpx;
  415. border: 3rpx solid #2563EB;
  416. box-shadow: inset -2rpx -2rpx 6rpx rgba(255,255,255,0.25), 3rpx 3rpx 10rpx rgba(59,130,246,0.25);
  417. flex-shrink: 0;
  418. transition: var(--clay-transition);
  419. }
  420. .switch-btn:active {
  421. box-shadow: inset 2rpx 2rpx 6rpx rgba(0,0,0,0.1), 1rpx 1rpx 4rpx rgba(59,130,246,0.15);
  422. transform: scale(0.96);
  423. }
  424. .switch-text {
  425. color: #FFFFFF;
  426. font-size: 24rpx;
  427. font-weight: 500;
  428. }
  429. /* ========================================
  430. 注册提示 - claymorphism
  431. ======================================== */
  432. .register-prompt {
  433. background: #FFFFFF;
  434. border-radius: 26rpx;
  435. padding: 48rpx;
  436. text-align: center;
  437. border: 4rpx solid #BFDBFE;
  438. box-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  439. }
  440. .register-icon { font-size: 80rpx; margin-bottom: 24rpx; }
  441. .prompt-title {
  442. font-size: 36rpx;
  443. font-weight: 700;
  444. color: #1E293B;
  445. display: block;
  446. margin-bottom: 12rpx;
  447. }
  448. .prompt-desc {
  449. font-size: 28rpx;
  450. color: #64748B;
  451. display: block;
  452. margin-bottom: 32rpx;
  453. }
  454. .btn-register {
  455. background: #3B82F6;
  456. color: #FFFFFF;
  457. font-size: 28rpx;
  458. padding: 20rpx 48rpx;
  459. border-radius: 16rpx;
  460. border: 3rpx solid #2563EB;
  461. box-shadow: inset -2rpx -2rpx 6rpx rgba(255,255,255,0.25), 3rpx 3rpx 10rpx rgba(59,130,246,0.25);
  462. transition: var(--clay-transition);
  463. }
  464. .btn-register:active {
  465. box-shadow: inset 2rpx 2rpx 6rpx rgba(0,0,0,0.1), 1rpx 1rpx 4rpx rgba(59,130,246,0.15);
  466. transform: scale(0.97);
  467. }
  468. /* ========================================
  469. 数据仪表盘 - claymorphism
  470. ======================================== */
  471. .stats-section { margin-bottom: 32rpx; }
  472. .section-header {
  473. display: flex;
  474. justify-content: space-between;
  475. align-items: center;
  476. margin-bottom: 24rpx;
  477. }
  478. .section-title {
  479. font-size: 32rpx;
  480. font-weight: 700;
  481. color: #1E293B;
  482. }
  483. .section-subtitle {
  484. font-size: 24rpx;
  485. color: #64748B;
  486. }
  487. .stats-grid {
  488. display: flex;
  489. flex-wrap: wrap;
  490. gap: 16rpx;
  491. }
  492. .stat-card {
  493. width: calc(50% - 8rpx);
  494. background: #FFFFFF;
  495. border-radius: 24rpx;
  496. padding: 24rpx;
  497. box-sizing: border-box;
  498. border: 4rpx solid #BFDBFE;
  499. box-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  500. transition: var(--clay-transition);
  501. }
  502. .stat-card:active {
  503. box-shadow: inset 2rpx 2rpx 8rpx rgba(0,0,0,0.04), 3rpx 3rpx 10rpx rgba(59,130,246,0.05);
  504. }
  505. .stat-header {
  506. display: flex;
  507. justify-content: space-between;
  508. align-items: center;
  509. margin-bottom: 14rpx;
  510. }
  511. .stat-icon-bg {
  512. width: 60rpx;
  513. height: 60rpx;
  514. border-radius: 18rpx;
  515. display: flex;
  516. align-items: center;
  517. justify-content: center;
  518. border: 3rpx solid transparent;
  519. box-shadow: inset -2rpx -2rpx 5rpx rgba(255,255,255,0.4), 3rpx 3rpx 8rpx rgba(0,0,0,0.04);
  520. }
  521. .stat-icon { font-size: 30rpx; }
  522. .blue-bg { background: rgba(59, 130, 246, 0.18); border-color: #BFDBFE; }
  523. .green-bg { background: rgba(16, 185, 129, 0.18); border-color: #A7F3D0; }
  524. .gold-bg { background: rgba(245, 158, 11, 0.18); border-color: #FDE68A; }
  525. .purple-bg { background: rgba(139, 92, 246, 0.18); border-color: #DDD6FE; }
  526. .stat-trend {
  527. font-size: 22rpx;
  528. color: #64748B;
  529. }
  530. .stat-trend.up { color: #10B981; }
  531. .stat-value {
  532. font-size: 40rpx;
  533. font-weight: 800;
  534. display: block;
  535. }
  536. .stat-label {
  537. font-size: 24rpx;
  538. color: #64748B;
  539. display: block;
  540. margin-top: 4rpx;
  541. }
  542. .stat-blue .stat-value { color: #3B82F6; }
  543. .stat-green .stat-value { color: #10B981; }
  544. .stat-gold .stat-value { color: #F59E0B; }
  545. .stat-purple .stat-value { color: #8B5CF6; }
  546. .stat-chart {
  547. display: flex;
  548. align-items: flex-end;
  549. gap: 6rpx;
  550. height: 48rpx;
  551. margin-top: 14rpx;
  552. }
  553. .mini-bar {
  554. flex: 1;
  555. background: #E2E8F0;
  556. border-radius: 4rpx;
  557. transition: height 0.3s;
  558. }
  559. .mini-bar.bar-active {
  560. background: #3B82F6;
  561. }
  562. .stat-action {
  563. margin-top: 14rpx;
  564. }
  565. .action-text {
  566. font-size: 22rpx;
  567. color: #3B82F6;
  568. font-weight: 500;
  569. }
  570. .stat-goal {
  571. margin-top: 14rpx;
  572. font-size: 20rpx;
  573. color: #F59E0B;
  574. }
  575. /* ========================================
  576. 数据趋势 - claymorphism
  577. ======================================== */
  578. .trend-section { margin-bottom: 32rpx; }
  579. .trend-chart {
  580. display: flex;
  581. justify-content: space-between;
  582. align-items: flex-end;
  583. background: #FFFFFF;
  584. border-radius: 24rpx;
  585. padding: 28rpx 24rpx;
  586. height: 220rpx;
  587. border: 4rpx solid #BFDBFE;
  588. box-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  589. }
  590. .trend-item {
  591. display: flex;
  592. flex-direction: column;
  593. align-items: center;
  594. flex: 1;
  595. }
  596. .trend-bar {
  597. width: 36rpx;
  598. background: linear-gradient(180deg, #93C5FD, #60A5FA);
  599. border-radius: 8rpx 8rpx 0 0;
  600. margin-bottom: 12rpx;
  601. transition: height 0.3s;
  602. }
  603. .trend-bar.trend-active {
  604. background: linear-gradient(180deg, #3B82F6, #2563EB);
  605. }
  606. .trend-day {
  607. font-size: 22rpx;
  608. color: #64748B;
  609. }
  610. /* ========================================
  611. 功能菜单 - claymorphism
  612. ======================================== */
  613. .menu-section { margin-bottom: 32rpx; }
  614. .menu-grid {
  615. display: flex;
  616. flex-wrap: wrap;
  617. background: #FFFFFF;
  618. border-radius: 24rpx;
  619. padding: 16rpx;
  620. border: 4rpx solid #BFDBFE;
  621. box-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  622. }
  623. .menu-item {
  624. width: 33.33%;
  625. display: flex;
  626. flex-direction: column;
  627. align-items: center;
  628. padding: 28rpx 0;
  629. position: relative;
  630. transition: var(--clay-transition);
  631. }
  632. .menu-item:active {
  633. transform: scale(0.93);
  634. }
  635. .menu-icon {
  636. width: 88rpx;
  637. height: 88rpx;
  638. border-radius: 24rpx;
  639. display: flex;
  640. align-items: center;
  641. justify-content: center;
  642. font-size: 38rpx;
  643. margin-bottom: 12rpx;
  644. border: 3rpx solid transparent;
  645. box-shadow: inset -2rpx -2rpx 5rpx rgba(255,255,255,0.4), 3rpx 3rpx 8rpx rgba(0,0,0,0.04);
  646. transition: var(--clay-transition);
  647. }
  648. .menu-item:active .menu-icon {
  649. box-shadow: inset 2rpx 2rpx 6rpx rgba(0,0,0,0.06), 1rpx 1rpx 3rpx rgba(0,0,0,0.03);
  650. }
  651. .mi-blue { background: rgba(59, 130, 246, 0.2); border-color: #BFDBFE; }
  652. .mi-gold { background: rgba(245, 158, 11, 0.2); border-color: #FDE68A; }
  653. .mi-purple { background: rgba(139, 92, 246, 0.2); border-color: #DDD6FE; }
  654. .mi-green { background: rgba(16, 185, 129, 0.2); border-color: #A7F3D0; }
  655. .mi-cyan { background: rgba(6, 182, 212, 0.2); border-color: #A5F3FC; }
  656. .mi-gray { background: rgba(100, 116, 139, 0.2); border-color: #CBD5E1; }
  657. .menu-label {
  658. font-size: 24rpx;
  659. color: #475569;
  660. font-weight: 500;
  661. }
  662. .menu-badge {
  663. position: absolute;
  664. top: 16rpx;
  665. right: 28rpx;
  666. background: #3B82F6;
  667. color: #FFFFFF;
  668. font-size: 20rpx;
  669. padding: 4rpx 12rpx;
  670. border-radius: 20rpx;
  671. border: 2rpx solid #60A5FA;
  672. }
  673. .msg-badge {
  674. position: absolute;
  675. top: 16rpx;
  676. right: 28rpx;
  677. background: #EF4444;
  678. color: #FFFFFF;
  679. font-size: 20rpx;
  680. min-width: 32rpx;
  681. text-align: center;
  682. padding: 4rpx 8rpx;
  683. border-radius: 20rpx;
  684. border: 2rpx solid #FCA5A5;
  685. }
  686. /* ========================================
  687. 快捷工具 - claymorphism
  688. ======================================== */
  689. .tools-section { margin-bottom: 48rpx; }
  690. .tools-row {
  691. display: flex;
  692. justify-content: space-between;
  693. background: #FFFFFF;
  694. border-radius: 24rpx;
  695. padding: 28rpx 16rpx;
  696. border: 4rpx solid #BFDBFE;
  697. box-shadow: inset -3rpx -3rpx 8rpx rgba(255,255,255,0.5), 6rpx 6rpx 18rpx rgba(59,130,246,0.08);
  698. }
  699. .tool-item {
  700. display: flex;
  701. flex-direction: column;
  702. align-items: center;
  703. flex: 1;
  704. transition: var(--clay-transition);
  705. }
  706. .tool-item:active {
  707. transform: scale(0.92);
  708. }
  709. .tool-icon-wrap {
  710. width: 72rpx;
  711. height: 72rpx;
  712. border-radius: 20rpx;
  713. display: flex;
  714. align-items: center;
  715. justify-content: center;
  716. margin-bottom: 10rpx;
  717. border: 3rpx solid transparent;
  718. box-shadow: inset -2rpx -2rpx 5rpx rgba(255,255,255,0.4), 3rpx 3rpx 8rpx rgba(0,0,0,0.04);
  719. transition: var(--clay-transition);
  720. }
  721. .tool-item:active .tool-icon-wrap {
  722. box-shadow: inset 2rpx 2rpx 6rpx rgba(0,0,0,0.06), 1rpx 1rpx 3rpx rgba(0,0,0,0.03);
  723. }
  724. .tool-icon-text { font-size: 34rpx; }
  725. .tool-create { background: rgba(59, 130, 246, 0.2); border-color: #BFDBFE; }
  726. .tool-share { background: rgba(16, 185, 129, 0.2); border-color: #A7F3D0; }
  727. .tool-cal { background: rgba(245, 158, 11, 0.2); border-color: #FDE68A; }
  728. .tool-report { background: rgba(139, 92, 246, 0.2); border-color: #DDD6FE; }
  729. .tool-label {
  730. font-size: 22rpx;
  731. color: #475569;
  732. }
  733. </style>