From 89dbdc63db4fd377cddc37252d87105103bb3280 Mon Sep 17 00:00:00 2001 From: XCool <2350444842@qq.com> Date: Thu, 16 Oct 2025 16:21:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/request.js | 4 +- src/assets/styles/global.css | 251 ++++++++++++++++++++++++++++++++++- src/store/index.js | 147 ++++++++++++-------- src/views/CapsuleDetail.vue | 8 -- src/views/Compose.vue | 183 +++++++++++++++---------- src/views/Home.vue | 48 ++++--- src/views/Login.vue | 20 +-- src/views/Profile.vue | 38 ++---- src/views/Register.vue | 14 -- src/views/Sent.vue | 17 ++- src/views/Timeline.vue | 2 - vue.config.js | 2 +- 12 files changed, 512 insertions(+), 222 deletions(-) diff --git a/src/api/request.js b/src/api/request.js index 078ac7d..a2d3ad2 100644 --- a/src/api/request.js +++ b/src/api/request.js @@ -30,8 +30,8 @@ api.interceptors.response.use( response => { const res = response.data - // 如果响应码不是200,则判断为错误 - if (res.code !== 200) { + // 如果响应中的success不是true,则判断为错误 + if (res.success !== true) { showFailToast(res.message || '请求失败') // 401: 未登录或token过期 diff --git a/src/assets/styles/global.css b/src/assets/styles/global.css index 9451504..9e895ef 100644 --- a/src/assets/styles/global.css +++ b/src/assets/styles/global.css @@ -8,6 +8,8 @@ --text-secondary: #a0b3d0; --glass-bg: rgba(255, 255, 255, 0.1); --glass-border: rgba(255, 255, 255, 0.2); + --button-gradient: linear-gradient(135deg, #00D4FF, #0099CC); + --button-hover-gradient: linear-gradient(135deg, #0099CC, #006699); } * { @@ -17,10 +19,15 @@ } body { - font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; + font-family: 'SF Pro Display', 'PingFang SC', 'Helvetica Neue', 'Microsoft YaHei', Arial, sans-serif; background-color: var(--primary-color); color: var(--text-primary); overflow-x: hidden; + font-size: 16px; + line-height: 1.6; + letter-spacing: 0.02em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } /* 深空背景 */ @@ -187,6 +194,80 @@ body { color: var(--accent-color); } +/* 字体大小 */ +.text-xs { font-size: 12px; } +.text-sm { font-size: 14px; } +.text-base { font-size: 16px; } +.text-lg { font-size: 18px; } +.text-xl { font-size: 20px; } +.text-2xl { font-size: 24px; } +.text-3xl { font-size: 30px; } +.text-4xl { font-size: 36px; } + +/* 字体粗细 */ +.font-light { font-weight: 300; } +.font-normal { font-weight: 400; } +.font-medium { font-weight: 500; } +.font-semibold { font-weight: 600; } +.font-bold { font-weight: 700; } + +/* 标题样式 */ +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.3; + margin-bottom: 0.5em; + letter-spacing: -0.01em; +} + +h1 { font-size: 2.5rem; } +h2 { font-size: 2rem; } +h3 { font-size: 1.75rem; } +h4 { font-size: 1.5rem; } +h5 { font-size: 1.25rem; } +h6 { font-size: 1.125rem; } + +/* 段落样式 */ +p { + margin-bottom: 1em; + line-height: 1.6; +} + +/* 文本阴影 */ +.text-shadow { + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.text-shadow-lg { + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +/* 文本发光效果 */ +.text-glow { + text-shadow: 0 0 10px rgba(0, 212, 255, 0.5); +} + +.text-glow-lg { + text-shadow: 0 0 20px rgba(0, 212, 255, 0.7); +} + +/* 特殊文本效果 */ +.gradient-text { + background: linear-gradient(135deg, var(--accent-color), #0099CC); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 600; +} + +/* 代码文本 */ +code { + font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace; + background-color: rgba(255, 255, 255, 0.1); + padding: 0.2em 0.4em; + border-radius: 4px; + font-size: 0.9em; +} + /* 间距工具类 */ .mt-10 { margin-top: 10px; } .mt-20 { margin-top: 20px; } @@ -352,6 +433,9 @@ input, textarea, .van-field__control { font-size: 16px !important; transition: all 0.3s ease !important; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1) !important; + font-family: inherit !important; + letter-spacing: 0.01em !important; + line-height: 1.5 !important; } input:focus, textarea:focus, .van-field__control:focus { @@ -375,6 +459,9 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder { .van-field__label { color: var(--text-secondary) !important; font-weight: 500 !important; + font-size: 14px !important; + letter-spacing: 0.01em !important; + line-height: 1.4 !important; } .van-field--error .van-field__control { @@ -406,9 +493,11 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder { .header h2 { margin: 0; - font-size: 18px; - font-weight: bold; + font-size: 20px; + font-weight: 600; color: var(--text-primary); + letter-spacing: -0.01em; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .header .van-icon { @@ -447,4 +536,160 @@ input::placeholder, textarea::placeholder, .van-field__control::placeholder { .custom-tabbar .van-tabbar-item__text { font-size: 12px !important; font-weight: 500 !important; + letter-spacing: 0.01em !important; + line-height: 1.2 !important; +} + +/* 全局按钮样式优化 */ +.van-button { + border-radius: 12px !important; + font-weight: 600 !important; + letter-spacing: 0.5px !important; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important; + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) !important; + border: none !important; + position: relative !important; + overflow: hidden !important; + font-family: inherit !important; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; +} + +.van-button::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: -100% !important; + width: 100% !important; + height: 100% !important; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent) !important; + transition: left 0.5s !important; +} + +.van-button:hover::before { + left: 100% !important; +} + +.van-button--primary { + background: var(--button-gradient) !important; + color: white !important; +} + +.van-button--primary:hover { + background: var(--button-hover-gradient) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(0, 212, 255, 0.4) !important; +} + +.van-button--success { + background: linear-gradient(135deg, #28a745, #20c997) !important; + color: white !important; +} + +.van-button--success:hover { + background: linear-gradient(135deg, #218838, #1ea085) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(40, 167, 69, 0.4) !important; +} + +.van-button--danger { + background: linear-gradient(135deg, #dc3545, #f86c6b) !important; + color: white !important; +} + +.van-button--danger:hover { + background: linear-gradient(135deg, #c82333, #f63c3c) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(220, 53, 69, 0.4) !important; +} + +.van-button--warning { + background: linear-gradient(135deg, #ffc107, #ff922b) !important; + color: #212529 !important; +} + +.van-button--warning:hover { + background: linear-gradient(135deg, #e0a800, #ff8c00) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(255, 193, 7, 0.4) !important; +} + +.van-button--default { + background: rgba(255, 255, 255, 0.1) !important; + color: var(--text-primary) !important; + border: 1px solid rgba(255, 255, 255, 0.2) !important; + backdrop-filter: blur(10px) !important; +} + +.van-button--default:hover { + background: rgba(255, 255, 255, 0.15) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(255, 255, 255, 0.1) !important; +} + +.van-button--plain { + background: transparent !important; + border: 1px solid var(--accent-color) !important; + color: var(--accent-color) !important; +} + +.van-button--plain:hover { + background: rgba(0, 212, 255, 0.1) !important; + transform: translateY(-2px) !important; + box-shadow: 0 6px 20px rgba(0, 212, 255, 0.2) !important; +} + +.van-button:active { + transform: translateY(0) !important; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important; +} + +.van-button--large { + height: 50px !important; + font-size: 16px !important; +} + +.van-button--normal { + height: 44px !important; + font-size: 14px !important; +} + +.van-button--small { + height: 32px !important; + font-size: 12px !important; +} + +.van-button--round { + border-radius: 22px !important; +} + +.van-button--round.van-button--large { + border-radius: 25px !important; +} + +.van-button--round.van-button--small { + border-radius: 16px !important; +} + +/* 特殊按钮样式 */ +.fab-button { + width: 60px !important; + height: 60px !important; + border-radius: 50% !important; + background: var(--button-gradient) !important; + border: none !important; + box-shadow: 0 4px 15px rgba(0, 212, 255, 0.4) !important; + font-size: 18px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + transition: all 0.3s ease !important; +} + +.fab-button:hover { + transform: scale(1.1) !important; + box-shadow: 0 6px 20px rgba(0, 212, 255, 0.6) !important; +} + +.fab-button:active { + transform: scale(0.95) !important; } \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index ed26c5a..36613a7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -7,9 +7,10 @@ export const userState = reactive({ token: '', refreshToken: '', userInfo: { - userId: '', + id: '', username: '', email: '', + nickname: '', avatar: '' }, subscription: { @@ -75,21 +76,23 @@ export const userActions = { // 登录 async login(credentials) { const res = await api.auth.login(credentials) - const { token, refreshToken, ...userInfo } = res.data + const { token, refreshToken, user } = res.data // 保存到本地存储 localStorage.setItem('token', token) localStorage.setItem('refreshToken', refreshToken) - localStorage.setItem('userInfo', JSON.stringify(userInfo)) + localStorage.setItem('userInfo', JSON.stringify(user)) // 更新状态 userState.isLoggedIn = true userState.token = token userState.refreshToken = refreshToken - userState.userInfo = userInfo + userState.userInfo = user - // 获取用户订阅信息 - await this.getSubscription() + // 获取用户订阅信息(不阻塞登录流程) + this.getSubscription().catch(err => { + console.error('登录后获取订阅信息失败:', err) + }) return res }, @@ -97,21 +100,24 @@ export const userActions = { // 注册 async register(userData) { const res = await api.auth.register(userData) - const { token, refreshToken, ...userInfo } = res.data + // 根据后端实际返回的数据结构提取数据 + const { token, refreshToken, user } = res.data // 保存到本地存储 localStorage.setItem('token', token) localStorage.setItem('refreshToken', refreshToken) - localStorage.setItem('userInfo', JSON.stringify(userInfo)) + localStorage.setItem('userInfo', JSON.stringify(user)) // 更新状态 userState.isLoggedIn = true userState.token = token userState.refreshToken = refreshToken - userState.userInfo = userInfo + userState.userInfo = user - // 获取用户订阅信息 - await this.getSubscription() + // 获取订阅信息(不阻塞注册流程) + this.getSubscription().catch(err => { + console.error('注册后获取订阅信息失败:', err) + }) return res }, @@ -132,49 +138,53 @@ export const userActions = { userState.token = '' userState.refreshToken = '' userState.userInfo = { - userId: '', + id: '', username: '', email: '', + nickname: '', avatar: '' } }, // 刷新token - async refreshToken() { - try { - const refreshToken = userState.refreshToken || localStorage.getItem('refreshToken') - if (!refreshToken) { - throw new Error('没有刷新令牌') + async refreshToken() { + try { + const refreshToken = userState.refreshToken || localStorage.getItem('refreshToken') + if (!refreshToken) { + throw new Error('没有刷新令牌') + } + + const res = await api.auth.refreshToken(refreshToken) + // 根据后端实际返回的数据结构提取数据 + const { token: newToken, refreshToken: newRefreshToken } = res.data + + // 更新本地存储 + localStorage.setItem('token', newToken) + localStorage.setItem('refreshToken', newRefreshToken) + + // 更新状态 + userState.token = newToken + userState.refreshToken = newRefreshToken + + return res + } catch (error) { + // 刷新失败,退出登录 + await this.logout() + throw error } - - const res = await api.auth.refreshToken(refreshToken) - const { token: newToken, refreshToken: newRefreshToken } = res.data - - // 更新本地存储 - localStorage.setItem('token', newToken) - localStorage.setItem('refreshToken', newRefreshToken) - - // 更新状态 - userState.token = newToken - userState.refreshToken = newRefreshToken - - return res - } catch (error) { - // 刷新失败,退出登录 - await this.logout() - throw error - } - }, + }, // 获取用户订阅信息 async getSubscription() { try { const res = await api.user.getSubscription() - userState.subscription = res.data + // 根据后端实际返回的数据结构提取数据 + userState.subscription = res.data.data return res } catch (error) { console.error('获取订阅信息失败:', error) - throw error + // 不抛出错误,避免影响登录流程 + return null } }, @@ -182,11 +192,14 @@ export const userActions = { async fetchUserInfo() { if (!userState.token) return; - const response = await api.user.getUserInfo(); - if (response.data.code === 200) { - userState.userInfo = response.data.data; - localStorage.setItem('userInfo', JSON.stringify(response.data.data)); + const response = await api.user.getUserProfile(); + // 根据后端实际返回的数据结构提取数据 + if (response.data) { + userState.userInfo = response.data; + localStorage.setItem('userInfo', JSON.stringify(response.data)); + return response.data; } + return null; }, // 初始化用户状态(从本地存储恢复) @@ -203,8 +216,10 @@ export const userActions = { userState.refreshToken = refreshToken || '' userState.userInfo = userInfo - // 获取订阅信息 - this.getSubscription() + // 获取订阅信息(不阻塞初始化流程) + this.getSubscription().catch(err => { + console.error('初始化获取订阅信息失败:', err) + }) } catch (error) { console.error('解析用户信息失败:', error) // 清除无效数据 @@ -229,18 +244,30 @@ export const mailActions = { // 根据类型更新不同的列表 if (type === 'INBOX') { - mailState.inboxList = res.data.list + if (page === 1) { + mailState.inboxList = res.data?.list || [] + } else { + mailState.inboxList = [...mailState.inboxList, ...(res.data?.list || [])] + } } else if (type === 'SENT') { - mailState.sentList = res.data.list + if (page === 1) { + mailState.sentList = res.data?.list || [] + } else { + mailState.sentList = [...mailState.sentList, ...(res.data?.list || [])] + } } else if (type === 'DRAFT') { - mailState.draftList = res.data.list + if (page === 1) { + mailState.draftList = res.data?.list || [] + } else { + mailState.draftList = [...mailState.draftList, ...(res.data?.list || [])] + } } // 更新分页信息 mailState.pagination = { - page: res.data.page, - size: res.data.size, - total: res.data.total + page: res.data?.page || page, + size: res.data?.size || size, + total: res.data?.total || 0 } return res @@ -359,12 +386,26 @@ export const capsuleActions = { try { capsuleState.loading = true const res = await api.capsule.getCapsules() - capsuleState.capsules = res.data.capsules - capsuleState.scene = res.data.scene - capsuleState.background = res.data.background + + // 添加空值检查 + if (res && res.data) { + capsuleState.capsules = res.data.capsules || [] + capsuleState.scene = res.data.scene || 'SPACE' + capsuleState.background = res.data.background || '' + } else { + // 如果没有数据,设置为空数组 + capsuleState.capsules = [] + capsuleState.scene = 'SPACE' + capsuleState.background = '' + } + return res } catch (error) { console.error('获取胶囊列表失败:', error) + // 出错时也要设置为空数组,避免页面出错 + capsuleState.capsules = [] + capsuleState.scene = 'SPACE' + capsuleState.background = '' throw error } finally { capsuleState.loading = false diff --git a/src/views/CapsuleDetail.vue b/src/views/CapsuleDetail.vue index 4ef83c7..fda698b 100644 --- a/src/views/CapsuleDetail.vue +++ b/src/views/CapsuleDetail.vue @@ -565,30 +565,22 @@ export default { } .open-button { - background: linear-gradient(135deg, var(--accent-color), #0099CC); - border: none; color: white; font-weight: bold; height: 50px; } .reply-button { - background: linear-gradient(135deg, #4ECDC4, #2A9D8F); - border: none; color: white; font-weight: bold; } .edit-button { - background: linear-gradient(135deg, #FFD166, #F77F00); - border: none; color: white; font-weight: bold; } .delete-button { - background: linear-gradient(135deg, #E63946, #A61E4D); - border: none; color: white; font-weight: bold; } diff --git a/src/views/Compose.vue b/src/views/Compose.vue index 20dc2b8..96108b3 100644 --- a/src/views/Compose.vue +++ b/src/views/Compose.vue @@ -17,7 +17,7 @@
{{ greetingText }}
+{{ greetingText }}
{{ capsule.title }}
-{{ formatDate(capsule.deliveryDate) }}
+{{ capsule.title }}
+{{ formatDate(capsule.deliveryDate) }}
暂无新通知
+暂无新通知
{{ notification.message }}
- {{ formatTime(notification.time) }} +{{ notification.message }}
+ {{ formatTime(notification.time) }}写给未来,不负当下
+写给未来,不负当下
{{ mail.content.substring(0, 50) }}...
+{{ (mail.content || '').substring(0, 50) }}...
-