index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. <template>
  2. <view class="page">
  3. <!-- 用户信息卡片 -->
  4. <view class="user-card">
  5. <view class="user-info">
  6. <image v-if="userStore.userInfo?.avatar" :src="userStore.userInfo.avatar" class="avatar" mode="aspectFill" />
  7. <view v-else class="avatar-placeholder">
  8. <text class="avatar-text">👤</text>
  9. </view>
  10. <view class="user-text">
  11. <text class="nickname">{{ userStore.userInfo?.nickname || '未登录' }}</text>
  12. <view class="member-badge" :class="memberClass">
  13. <text class="member-text">{{ memberText }}</text>
  14. </view>
  15. </view>
  16. </view>
  17. <!-- 使用统计 -->
  18. <view class="stats-row" v-if="userStore.memberStatus">
  19. <view class="stat-item">
  20. <text class="stat-value">{{ userStore.memberStatus.quota.dailyRemaining }}</text>
  21. <text class="stat-label">今日剩余次数</text>
  22. </view>
  23. <view class="stat-divider" />
  24. <view class="stat-item">
  25. <text class="stat-value">{{ userStore.memberStatus.quota.wordLimit === 0 ? '∞' : userStore.memberStatus.quota.wordLimit }}</text>
  26. <text class="stat-label">单次字数上限</text>
  27. </view>
  28. </view>
  29. </view>
  30. <!-- 今日创作概况 -->
  31. <view class="today-card" v-if="userStore.isLoggedIn">
  32. <view class="today-header">
  33. <text class="today-title">今日创作概况</text>
  34. <text class="today-date">{{ todayDate }}</text>
  35. </view>
  36. <view class="today-stats">
  37. <view class="today-stat-item">
  38. <text class="today-stat-value">{{ todayStats.audioCount }}</text>
  39. <text class="today-stat-label">生成音频</text>
  40. </view>
  41. <view class="today-stat-divider" />
  42. <view class="today-stat-item">
  43. <text class="today-stat-value">{{ todayStats.bookCount }}</text>
  44. <text class="today-stat-label">生成书籍</text>
  45. </view>
  46. <view class="today-stat-divider" />
  47. <view class="today-stat-item">
  48. <text class="today-stat-value">{{ todayStats.usageQuota }}</text>
  49. <text class="today-stat-label">使用额度</text>
  50. </view>
  51. </view>
  52. </view>
  53. <!-- 最近的创作 -->
  54. <view class="recent-card" v-if="recentCreations.length > 0">
  55. <view class="recent-header">
  56. <text class="recent-title">最近的创作</text>
  57. <text class="recent-more" @click="goHistory">查看更多 ›</text>
  58. </view>
  59. <view class="recent-list">
  60. <view
  61. v-for="item in recentCreations"
  62. :key="item._id"
  63. class="recent-item"
  64. hover-class="recent-item-hover"
  65. @click="goToDetail(item)"
  66. >
  67. <view class="recent-cover" :style="{ background: getCoverGradient(item.voiceId) }">
  68. <text class="cover-letter">{{ getTitleLetter(item.title) }}</text>
  69. <text class="cover-title-small">{{ item.title }}</text>
  70. </view>
  71. <view class="recent-info">
  72. <text class="recent-title-text">{{ item.title }}</text>
  73. <text class="recent-meta">{{ item.wordCount }}字 · {{ formatDate(item.createdAt) }}</text>
  74. </view>
  75. <text class="recent-arrow">›</text>
  76. </view>
  77. </view>
  78. </view>
  79. <!-- 升级会员推广卡片 -->
  80. <view class="upgrade-card" v-if="userStore.isLoggedIn && !userStore.isMember" @click="goMember">
  81. <view class="upgrade-left">
  82. <text class="upgrade-icon">👑</text>
  83. <view class="upgrade-text">
  84. <text class="upgrade-title">升级会员</text>
  85. <text class="upgrade-desc">解锁更多创作次数和高级音色</text>
  86. </view>
  87. </view>
  88. <text class="upgrade-btn">立即开通</text>
  89. </view>
  90. <!-- 功能入口 -->
  91. <view class="menu-section">
  92. <view class="menu-item" @click="goBookGenerator">
  93. <text class="menu-icon">📖</text>
  94. <text class="menu-text">书籍生成</text>
  95. <text class="menu-tag">新功能</text>
  96. <text class="menu-arrow">›</text>
  97. </view>
  98. <view class="menu-item" @click="goMember">
  99. <text class="menu-icon">👑</text>
  100. <text class="menu-text">会员中心</text>
  101. <text class="menu-arrow">›</text>
  102. </view>
  103. <view class="menu-item" @click="goSettings">
  104. <text class="menu-icon">⚙️</text>
  105. <text class="menu-text">设置</text>
  106. <text class="menu-arrow">›</text>
  107. </view>
  108. <view class="menu-item" @click="goLogs">
  109. <text class="menu-icon">📊</text>
  110. <text class="menu-text">日志监控</text>
  111. <text class="menu-arrow">›</text>
  112. </view>
  113. <view class="menu-item" @click="goHistory">
  114. <text class="menu-icon">📜</text>
  115. <text class="menu-text">历史记录</text>
  116. <text class="menu-arrow">›</text>
  117. </view>
  118. <view class="menu-item" @click="showAbout">
  119. <text class="menu-icon">ℹ️</text>
  120. <text class="menu-text">关于我们</text>
  121. <text class="menu-arrow">›</text>
  122. </view>
  123. <view class="menu-item" @click="showFeedback">
  124. <text class="menu-icon">💬</text>
  125. <text class="menu-text">意见反馈</text>
  126. <text class="menu-arrow">›</text>
  127. </view>
  128. </view>
  129. <!-- 退出登录 -->
  130. <view class="logout-section" v-if="userStore.isLoggedIn">
  131. <button class="logout-btn" @click="handleLogout">退出登录</button>
  132. </view>
  133. </view>
  134. </template>
  135. <script setup lang="ts">
  136. import { computed, ref, onMounted } from 'vue';
  137. import { onShow } from '@dcloudio/uni-app';
  138. import { useUserStore } from '../../store/user';
  139. import { get } from '../../utils/request';
  140. import type { AudioItem } from '../../types';
  141. import { getCoverGradient, getTitleLetter } from '../../composables/useCoverStyle';
  142. const userStore = useUserStore();
  143. // 今日创作概况
  144. const todayStats = ref({
  145. audioCount: 0,
  146. bookCount: 0,
  147. usageQuota: 0
  148. });
  149. // 最近的创作
  150. const recentCreations = ref<AudioItem[]>([]);
  151. // 今日日期
  152. const todayDate = computed(() => {
  153. const now = new Date();
  154. return `${now.getMonth() + 1}月${now.getDate()}日`;
  155. });
  156. // 计算属性
  157. const memberClass = computed(() => {
  158. const level = userStore.memberStatus?.level || 0;
  159. return `level-${level}`;
  160. });
  161. const memberText = computed(() => {
  162. return userStore.memberStatus?.levelName || '免费用户';
  163. });
  164. // 获取今日创作概况
  165. async function fetchTodayStats() {
  166. try {
  167. const now = new Date();
  168. const startDate = new Date(now.setHours(0, 0, 0, 0)).toISOString();
  169. // 获取今日音频数
  170. const audioResult = await get<{ list: AudioItem[]; total: number }>('/history', {
  171. startDate,
  172. pageSize: 100
  173. });
  174. // 获取今日书籍数(从书籍生成记录获取)
  175. let bookCount = 0;
  176. try {
  177. const bookResult = await get<{ list: any[]; total: number }>('/book-generator/list', {
  178. startDate,
  179. pageSize: 100
  180. });
  181. bookCount = bookResult.total || 0;
  182. } catch (e) {
  183. // 书籍接口可能不存在
  184. console.log('[Mine] 书籍接口不存在', e);
  185. }
  186. todayStats.value = {
  187. audioCount: audioResult.total || 0,
  188. bookCount,
  189. usageQuota: userStore.memberStatus?.quota.dailyRemaining || 0
  190. };
  191. } catch (error) {
  192. console.log('[Mine] 获取今日概况失败', error);
  193. }
  194. }
  195. // 获取最近的创作
  196. async function fetchRecentCreations() {
  197. try {
  198. const result = await get<{ list: AudioItem[]; total: number }>('/history', {
  199. pageSize: 3,
  200. sortBy: 'createdAt',
  201. sortOrder: 'desc'
  202. });
  203. recentCreations.value = (result.list || []).slice(0, 3);
  204. } catch (error) {
  205. console.log('[Mine] 获取最近创作失败', error);
  206. }
  207. }
  208. // 格式化日期
  209. function formatDate(dateStr: string): string {
  210. const date = new Date(dateStr);
  211. const now = new Date();
  212. const diff = now.getTime() - date.getTime();
  213. const oneDay = 24 * 60 * 60 * 1000;
  214. if (diff < oneDay) return '今天';
  215. if (diff < 2 * oneDay) return '昨天';
  216. return `${date.getMonth() + 1}/${date.getDate()}`;
  217. }
  218. // 跳转到历史记录页
  219. function goToDetail(item: AudioItem) {
  220. uni.navigateTo({ url: '/pages/history/index' });
  221. }
  222. // 页面显示
  223. onShow(() => {
  224. if (userStore.isLoggedIn) {
  225. userStore.fetchMemberStatus();
  226. fetchTodayStats();
  227. fetchRecentCreations();
  228. } else {
  229. // 未登录,跳转到登录页
  230. uni.navigateTo({ url: '/pages/login/index' });
  231. }
  232. });
  233. // 跳转会员页
  234. function goMember() {
  235. uni.navigateTo({ url: '/pages/member/index' });
  236. }
  237. // 跳转设置页
  238. function goSettings() {
  239. uni.navigateTo({ url: '/pages/settings/index' });
  240. }
  241. // 跳转日志监控页
  242. function goLogs() {
  243. uni.navigateTo({ url: '/pages/logs/index' });
  244. }
  245. // 跳转历史页
  246. function goHistory() {
  247. uni.switchTab({ url: '/pages/history/index' });
  248. }
  249. // 跳转书籍生成页
  250. function goBookGenerator() {
  251. uni.switchTab({ url: '/pages/book-generator/index' });
  252. }
  253. // 关于我们
  254. function showAbout() {
  255. uni.showModal({
  256. title: '关于声工坊',
  257. content: '声工坊是一款智能文本转语音工具,帮助您轻松制作高质量音频内容。\n\n版本: 1.0.0',
  258. showCancel: false,
  259. });
  260. }
  261. // 意见反馈
  262. function showFeedback() {
  263. uni.showModal({
  264. title: '意见反馈',
  265. content: '如有问题或建议,请联系客服:\n邮箱:support@example.com',
  266. showCancel: false,
  267. });
  268. }
  269. // 退出登录
  270. function handleLogout() {
  271. uni.showModal({
  272. title: '确认退出',
  273. content: '确定要退出登录吗?',
  274. success: (res) => {
  275. if (res.confirm) {
  276. userStore.logout();
  277. uni.showToast({ title: '已退出登录', icon: 'success' });
  278. // 关闭所有页面,打开登录页
  279. uni.reLaunch({ url: '/pages/login/index' });
  280. }
  281. },
  282. });
  283. }
  284. </script>
  285. <style scoped>
  286. .page {
  287. min-height: 100vh;
  288. background: #f9fafb;
  289. padding-bottom: 120rpx;
  290. }
  291. /* 夜间模式适配 */
  292. @media (prefers-color-scheme: dark) {
  293. .page {
  294. background: #1a1a2e;
  295. }
  296. }
  297. .user-card {
  298. margin: 32rpx;
  299. background: linear-gradient(135deg, #4f46e5 0%, #818cf8 100%);
  300. border-radius: 24rpx;
  301. padding: 48rpx 32rpx;
  302. box-shadow: 0 8rpx 32rpx rgba(79, 70, 229, 0.3);
  303. }
  304. .user-info {
  305. display: flex;
  306. align-items: center;
  307. margin-bottom: 32rpx;
  308. }
  309. .avatar {
  310. width: 96rpx;
  311. height: 96rpx;
  312. border-radius: 50%;
  313. border: 4rpx solid rgba(255, 255, 255, 0.3);
  314. }
  315. .avatar-placeholder {
  316. width: 96rpx;
  317. height: 96rpx;
  318. border-radius: 50%;
  319. background: rgba(255, 255, 255, 0.2);
  320. display: flex;
  321. align-items: center;
  322. justify-content: center;
  323. border: 4rpx solid rgba(255, 255, 255, 0.3);
  324. }
  325. .avatar-text {
  326. font-size: 48rpx;
  327. }
  328. .user-text {
  329. margin-left: 24rpx;
  330. }
  331. .nickname {
  332. display: block;
  333. font-size: 36rpx;
  334. font-weight: 600;
  335. color: #ffffff;
  336. margin-bottom: 8rpx;
  337. }
  338. .member-badge {
  339. display: inline-block;
  340. padding: 4rpx 16rpx;
  341. border-radius: 16rpx;
  342. background: rgba(255, 255, 255, 0.2);
  343. }
  344. .member-badge.level-1 {
  345. background: linear-gradient(135deg, #f97316 0%, #fb923c 100%);
  346. }
  347. .member-badge.level-2 {
  348. background: linear-gradient(135deg, #fbbf24 0%, #fcd34d 100%);
  349. }
  350. .member-text {
  351. font-size: 22rpx;
  352. color: #ffffff;
  353. }
  354. .stats-row {
  355. display: flex;
  356. align-items: center;
  357. background: rgba(255, 255, 255, 0.1);
  358. border-radius: 16rpx;
  359. padding: 24rpx;
  360. }
  361. .stat-item {
  362. flex: 1;
  363. text-align: center;
  364. }
  365. .stat-value {
  366. display: block;
  367. font-size: 40rpx;
  368. font-weight: 700;
  369. color: #ffffff;
  370. }
  371. .stat-label {
  372. display: block;
  373. font-size: 22rpx;
  374. color: rgba(255, 255, 255, 0.8);
  375. margin-top: 4rpx;
  376. }
  377. .stat-divider {
  378. width: 2rpx;
  379. height: 60rpx;
  380. background: rgba(255, 255, 255, 0.2);
  381. }
  382. /* 今日创作概况卡片 */
  383. .today-card {
  384. margin: 0 32rpx 24rpx;
  385. background: #ffffff;
  386. border-radius: 20rpx;
  387. padding: 32rpx;
  388. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  389. }
  390. @media (prefers-color-scheme: dark) {
  391. .today-card {
  392. background: #2d2d44;
  393. }
  394. }
  395. .today-header {
  396. display: flex;
  397. justify-content: space-between;
  398. align-items: center;
  399. margin-bottom: 24rpx;
  400. }
  401. .today-title {
  402. font-size: 30rpx;
  403. font-weight: 600;
  404. color: #1f2937;
  405. }
  406. @media (prefers-color-scheme: dark) {
  407. .today-title {
  408. color: #f9fafb;
  409. }
  410. }
  411. .today-date {
  412. font-size: 24rpx;
  413. color: #9ca3af;
  414. }
  415. .today-stats {
  416. display: flex;
  417. align-items: center;
  418. }
  419. .today-stat-item {
  420. flex: 1;
  421. text-align: center;
  422. }
  423. .today-stat-value {
  424. display: block;
  425. font-size: 44rpx;
  426. font-weight: 700;
  427. color: #4f46e5;
  428. }
  429. @media (prefers-color-scheme: dark) {
  430. .today-stat-value {
  431. color: #818cf8;
  432. }
  433. }
  434. .today-stat-label {
  435. display: block;
  436. font-size: 22rpx;
  437. color: #6b7280;
  438. margin-top: 4rpx;
  439. }
  440. @media (prefers-color-scheme: dark) {
  441. .today-stat-label {
  442. color: #9ca3af;
  443. }
  444. }
  445. .today-stat-divider {
  446. width: 2rpx;
  447. height: 60rpx;
  448. background: #e5e7eb;
  449. }
  450. @media (prefers-color-scheme: dark) {
  451. .today-stat-divider {
  452. background: #374151;
  453. }
  454. }
  455. /* 最近的创作卡片 */
  456. .recent-card {
  457. margin: 0 32rpx 24rpx;
  458. background: #ffffff;
  459. border-radius: 20rpx;
  460. padding: 32rpx;
  461. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  462. }
  463. @media (prefers-color-scheme: dark) {
  464. .recent-card {
  465. background: #2d2d44;
  466. }
  467. }
  468. .recent-header {
  469. display: flex;
  470. justify-content: space-between;
  471. align-items: center;
  472. margin-bottom: 24rpx;
  473. }
  474. .recent-title {
  475. font-size: 30rpx;
  476. font-weight: 600;
  477. color: #1f2937;
  478. }
  479. @media (prefers-color-scheme: dark) {
  480. .recent-title {
  481. color: #f9fafb;
  482. }
  483. }
  484. .recent-more {
  485. font-size: 24rpx;
  486. color: #4f46e5;
  487. }
  488. @media (prefers-color-scheme: dark) {
  489. .recent-more {
  490. color: #818cf8;
  491. }
  492. }
  493. .recent-list {
  494. display: flex;
  495. flex-direction: column;
  496. }
  497. .recent-item {
  498. display: flex;
  499. align-items: center;
  500. padding: 16rpx 0;
  501. border-bottom: 1rpx solid #f3f4f6;
  502. }
  503. .recent-item:last-child {
  504. border-bottom: none;
  505. }
  506. .recent-item-hover {
  507. background-color: #f3f4f6;
  508. border-radius: 8rpx;
  509. }
  510. @media (prefers-color-scheme: dark) {
  511. .recent-item {
  512. border-bottom-color: #374151;
  513. }
  514. .recent-item-hover {
  515. background-color: #374151;
  516. }
  517. .recent-item:last-child {
  518. border-bottom: none;
  519. }
  520. }
  521. .recent-cover {
  522. width: 80rpx;
  523. height: 80rpx;
  524. border-radius: 12rpx;
  525. display: flex;
  526. align-items: center;
  527. justify-content: center;
  528. flex-shrink: 0;
  529. position: relative;
  530. overflow: hidden;
  531. }
  532. .cover-letter {
  533. position: absolute;
  534. top: -8rpx;
  535. left: 4rpx;
  536. font-size: 60rpx;
  537. font-weight: 900;
  538. color: rgba(255, 255, 255, 0.15);
  539. line-height: 1;
  540. pointer-events: none;
  541. }
  542. .cover-title-small {
  543. position: absolute;
  544. bottom: 4rpx;
  545. left: 8rpx;
  546. right: 8rpx;
  547. font-size: 16rpx;
  548. color: rgba(255, 255, 255, 0.8);
  549. font-weight: 500;
  550. overflow: hidden;
  551. text-overflow: ellipsis;
  552. white-space: nowrap;
  553. }
  554. .cover-icon {
  555. font-size: 32rpx;
  556. }
  557. .recent-info {
  558. flex: 1;
  559. margin-left: 20rpx;
  560. overflow: hidden;
  561. }
  562. .recent-title-text {
  563. display: block;
  564. font-size: 28rpx;
  565. font-weight: 500;
  566. color: #1f2937;
  567. white-space: nowrap;
  568. overflow: hidden;
  569. text-overflow: ellipsis;
  570. }
  571. @media (prefers-color-scheme: dark) {
  572. .recent-title-text {
  573. color: #f9fafb;
  574. }
  575. }
  576. .recent-meta {
  577. display: block;
  578. font-size: 22rpx;
  579. color: #9ca3af;
  580. margin-top: 4rpx;
  581. }
  582. .recent-arrow {
  583. font-size: 32rpx;
  584. color: #d1d5db;
  585. margin-left: 16rpx;
  586. }
  587. @media (prefers-color-scheme: dark) {
  588. .recent-arrow {
  589. color: #4b5563;
  590. }
  591. }
  592. /* 升级会员卡片 */
  593. .upgrade-card {
  594. margin: 0 32rpx 24rpx;
  595. background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
  596. border-radius: 20rpx;
  597. padding: 28rpx 32rpx;
  598. display: flex;
  599. align-items: center;
  600. justify-content: space-between;
  601. box-shadow: 0 4rpx 16rpx rgba(251, 191, 36, 0.2);
  602. }
  603. @media (prefers-color-scheme: dark) {
  604. .upgrade-card {
  605. background: linear-gradient(135deg, #92400e 0%, #78350f 100%);
  606. }
  607. }
  608. .upgrade-left {
  609. display: flex;
  610. align-items: center;
  611. }
  612. .upgrade-icon {
  613. font-size: 40rpx;
  614. margin-right: 16rpx;
  615. }
  616. .upgrade-text {
  617. display: flex;
  618. flex-direction: column;
  619. }
  620. .upgrade-title {
  621. display: block;
  622. font-size: 30rpx;
  623. font-weight: 600;
  624. color: #92400e;
  625. }
  626. @media (prefers-color-scheme: dark) {
  627. .upgrade-title {
  628. color: #fef3c7;
  629. }
  630. }
  631. .upgrade-desc {
  632. display: block;
  633. font-size: 22rpx;
  634. color: #b45309;
  635. margin-top: 4rpx;
  636. }
  637. @media (prefers-color-scheme: dark) {
  638. .upgrade-desc {
  639. color: #fde68a;
  640. }
  641. }
  642. .upgrade-btn {
  643. font-size: 24rpx;
  644. color: #ffffff;
  645. background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
  646. padding: 12rpx 24rpx;
  647. border-radius: 24rpx;
  648. font-weight: 500;
  649. }
  650. /* 功能菜单 */
  651. .menu-section {
  652. margin: 32rpx;
  653. background: #ffffff;
  654. border-radius: 24rpx;
  655. overflow: hidden;
  656. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  657. }
  658. @media (prefers-color-scheme: dark) {
  659. .menu-section {
  660. background: #2d2d44;
  661. }
  662. }
  663. .menu-item {
  664. display: flex;
  665. align-items: center;
  666. padding: 32rpx;
  667. border-bottom: 1rpx solid #f3f4f6;
  668. }
  669. .menu-item:last-child {
  670. border-bottom: none;
  671. }
  672. @media (prefers-color-scheme: dark) {
  673. .menu-item {
  674. border-bottom-color: #374151;
  675. }
  676. .menu-item:last-child {
  677. border-bottom: none;
  678. }
  679. }
  680. .menu-icon {
  681. font-size: 40rpx;
  682. margin-right: 24rpx;
  683. }
  684. .menu-text {
  685. flex: 1;
  686. font-size: 30rpx;
  687. color: #1f2937;
  688. }
  689. @media (prefers-color-scheme: dark) {
  690. .menu-text {
  691. color: #f9fafb;
  692. }
  693. }
  694. .menu-tag {
  695. font-size: 20rpx;
  696. padding: 4rpx 12rpx;
  697. background: linear-gradient(135deg, #f97316 0%, #fb923c 100%);
  698. color: #ffffff;
  699. border-radius: 8rpx;
  700. margin-right: 16rpx;
  701. }
  702. .menu-arrow {
  703. font-size: 32rpx;
  704. color: #9ca3af;
  705. }
  706. @media (prefers-color-scheme: dark) {
  707. .menu-arrow {
  708. color: #6b7280;
  709. }
  710. }
  711. .logout-section {
  712. margin: 48rpx 32rpx;
  713. }
  714. .logout-btn {
  715. width: 100%;
  716. height: 88rpx;
  717. background: #ffffff;
  718. border-radius: 16rpx;
  719. border: 2rpx solid #ef4444;
  720. display: flex;
  721. align-items: center;
  722. justify-content: center;
  723. }
  724. .logout-btn::after {
  725. border: none;
  726. }
  727. .logout-btn text {
  728. font-size: 30rpx;
  729. color: #ef4444;
  730. }
  731. @media (prefers-color-scheme: dark) {
  732. .logout-btn {
  733. background: #2d2d44;
  734. }
  735. }
  736. </style>