From 5ac547a9d585cf2be3483cd83e9e98cedc27eb79 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 1 Jul 2026 12:19:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E5=92=8C=E9=A1=B5=E9=9D=A2=E5=B1=95=E7=A4=BA=E7=BB=86?= =?UTF-8?q?=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.test | 1 + nginx/default.conf.template | 15 ++++++++ src/api/request.ts | 26 ++++++++++---- src/views/events/EventListView.vue | 22 ++++++++++++ src/views/login/LoginView.vue | 10 ++++-- src/views/mine/MineView.vue | 38 ++++++++++++++++++-- src/views/orders/OrderDetailView.vue | 54 +++++++++++++++++++++------- 7 files changed, 143 insertions(+), 23 deletions(-) diff --git a/.env.test b/.env.test index 1ab81b8..66bcd42 100644 --- a/.env.test +++ b/.env.test @@ -1,4 +1,5 @@ VITE_USE_MOCK=false +VITE_APP_BASE=/ VITE_PROXY_TARGET= # VITE_API_BASE_URL=http://8.138.234.141/ingress VITE_API_BASE_URL= diff --git a/nginx/default.conf.template b/nginx/default.conf.template index 82d0204..6d026d0 100644 --- a/nginx/default.conf.template +++ b/nginx/default.conf.template @@ -20,6 +20,21 @@ server { try_files $uri =404; } + location ^~ /hotel-staff-h5/assets/ { + rewrite ^/hotel-staff-h5/(.*)$ /$1 break; + expires 30d; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + location = /hotel-staff-h5 { + return 301 /hotel-staff-h5/; + } + + location ^~ /hotel-staff-h5/ { + try_files $uri $uri/ /index.html; + } + location /auth/ { proxy_pass ${BACKEND_GATEWAY}/auth/; proxy_http_version 1.1; diff --git a/src/api/request.ts b/src/api/request.ts index aee8942..f74d6c4 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -8,6 +8,11 @@ const http = axios.create({ timeout: 15000 }) +export type RequestError = Error & { + cause?: unknown + toastShown?: boolean +} + let isRedirectingToLogin = false const authExpiredTexts = ['请求令牌已过期', '令牌已过期', 'token已过期', 'Token已过期', 'invalid_token'] @@ -20,6 +25,13 @@ const getHeader = (headers: unknown, key: string) => { const hasBearerAuthorization = (headers: unknown) => getHeader(headers, 'Authorization').startsWith('Bearer ') +const createRequestError = (message: string, cause?: unknown, toastShown = false) => { + const requestError = new Error(message) as RequestError + requestError.cause = cause + requestError.toastShown = toastShown + return requestError +} + const isAuthExpired = (status?: number, code?: unknown, message = '', hasBearerToken = false) => { const normalizedCode = String(code || '') return ( @@ -32,7 +44,7 @@ const redirectToLogin = (message: string) => { clearAuthStorage() window.dispatchEvent(new Event(AUTH_EXPIRED_EVENT)) - if (isRedirectingToLogin || window.location.pathname.endsWith('/login')) return + if (isRedirectingToLogin || window.location.pathname.endsWith('/login')) return false isRedirectingToLogin = true showToast(message || '登录已失效,请重新登录') @@ -46,6 +58,7 @@ const redirectToLogin = (message: string) => { } }) }) + return true } http.interceptors.request.use((config) => { @@ -65,9 +78,10 @@ http.interceptors.response.use( } const message = payload.msg || payload.message || '请求失败' if (isAuthExpired(response.status, payload.code, message, hasBearerAuthorization(response.config.headers))) { - redirectToLogin(message) + const toastShown = redirectToLogin(message) + return Promise.reject(createRequestError(message, undefined, toastShown)) } - return Promise.reject(new Error(message)) + return Promise.reject(createRequestError(message)) } return payload }, @@ -81,11 +95,11 @@ http.interceptors.response.use( hasBearerAuthorization(error.config?.headers) ) ) { - redirectToLogin(message) - return Promise.reject(error) + const toastShown = redirectToLogin(message) + return Promise.reject(createRequestError(message, error, toastShown)) } showToast(message) - return Promise.reject(error) + return Promise.reject(createRequestError(message, error, true)) } ) diff --git a/src/views/events/EventListView.vue b/src/views/events/EventListView.vue index 1f2a311..4ca1d36 100644 --- a/src/views/events/EventListView.vue +++ b/src/views/events/EventListView.vue @@ -53,6 +53,7 @@ onMounted(() => loadEvents(true))

事件管理

事件列表

+

查看门店通知、弹窗提醒和正在生效的运营事件。

@@ -96,6 +97,9 @@ onMounted(() => loadEvents(true)) +
+ +
暂无事件
@@ -117,10 +121,24 @@ onMounted(() => loadEvents(true)) padding-top: 12px; } +.list-skeleton { + display: grid; + gap: 10px; + padding-top: 12px; +} + +.list-skeleton :deep(.van-skeleton) { + padding: 13px; + border: 1px solid rgba(202, 214, 208, 0.82); + border-radius: var(--panel-radius); + background: rgba(255, 255, 255, 0.62); +} + .event-card__media { height: 124px; overflow: hidden; margin-bottom: 10px; + border: 1px solid rgba(221, 230, 225, 0.9); border-radius: var(--radius); background: var(--surface-soft); } @@ -133,10 +151,14 @@ onMounted(() => loadEvents(true)) } .event-desc { + min-width: 0; margin: 10px 0; color: var(--text-main); font-size: 14px; line-height: 1.55; + overflow-wrap: anywhere; + white-space: normal; + word-break: break-word; } .event-status-row { diff --git a/src/views/login/LoginView.vue b/src/views/login/LoginView.vue index 3d31dac..ff5e935 100644 --- a/src/views/login/LoginView.vue +++ b/src/views/login/LoginView.vue @@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router' import { showToast } from 'vant' import { BadgeCheck, Phone, ShieldCheck } from 'lucide-vue-next' import { getImageCodeUrl, sendMobileCode } from '@/api/auth' +import type { RequestError } from '@/api/request' import { useAuthStore } from '@/stores/auth' const router = useRouter() @@ -51,6 +52,11 @@ const startCountdown = () => { }, 1000) } +const showErrorToast = (error: unknown, fallback: string) => { + if (error && typeof error === 'object' && (error as RequestError).toastShown) return + showToast(error instanceof Error ? error.message : fallback) +} + const handleSend = async () => { if (!/^1\d{10}$/.test(phone.value)) { showToast('请输入正确手机号') @@ -78,7 +84,7 @@ const handleSend = async () => { } startCountdown() } catch (error) { - showToast(error instanceof Error ? error.message : '验证码发送失败') + showErrorToast(error, '验证码发送失败') refreshImageCode() } finally { sending.value = false @@ -100,7 +106,7 @@ const handleLogin = async () => { const redirect = typeof route.query.redirect === 'string' ? route.query.redirect : '/home' await router.replace(redirect) } catch (error) { - showToast(error instanceof Error ? error.message : '登录失败') + showErrorToast(error, '登录失败') } finally { loading.value = false } diff --git a/src/views/mine/MineView.vue b/src/views/mine/MineView.vue index d588d92..2d33db1 100644 --- a/src/views/mine/MineView.vue +++ b/src/views/mine/MineView.vue @@ -29,8 +29,8 @@ const logout = () => { - - + + @@ -48,13 +48,17 @@ const logout = () => { gap: 12px; padding: 18px 14px; margin-top: 18px; + background: + linear-gradient(135deg, rgba(19, 144, 111, 0.12), rgba(237, 246, 255, 0.72)), + var(--surface); } .avatar { display: grid; width: 58px; height: 58px; - border-radius: var(--radius); + border: 1px solid rgba(19, 144, 111, 0.16); + border-radius: var(--panel-radius); background: var(--primary-soft); color: var(--primary-deep); place-items: center; @@ -76,6 +80,33 @@ const logout = () => { margin-top: 12px; } +.mine-panel :deep(.van-cell) { + align-items: center; + background: transparent; +} + +.mine-panel :deep(.van-cell__title) { + flex: 0 0 86px; + color: var(--text-main); + font-weight: 650; +} + +.mine-panel :deep(.van-cell__value) { + min-width: 0; + color: var(--text-muted); + text-align: right; +} + +.mine-panel :deep(.id-cell .van-cell__value) { + flex: 1; + overflow: hidden; + font-size: 13px; + font-variant-numeric: tabular-nums; + text-overflow: ellipsis; + white-space: nowrap; + word-break: keep-all; +} + .cell-icon { margin: 2px 8px 0 0; color: var(--primary); @@ -83,5 +114,6 @@ const logout = () => { .logout-button { margin-top: 14px; + background: rgba(255, 255, 255, 0.78); } diff --git a/src/views/orders/OrderDetailView.vue b/src/views/orders/OrderDetailView.vue index c23ba6f..4fc03f7 100644 --- a/src/views/orders/OrderDetailView.vue +++ b/src/views/orders/OrderDetailView.vue @@ -1,7 +1,7 @@