diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 048a000..7e0cf5b 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,4 +1,5 @@ import "element-plus/dist/index.css"; +import "vant/lib/index.css"; import "./styles/base.css"; import ElementPlus from "element-plus"; diff --git a/frontend/src/styles/base.css b/frontend/src/styles/base.css index 4ed42e6..0828008 100644 --- a/frontend/src/styles/base.css +++ b/frontend/src/styles/base.css @@ -1405,3 +1405,14 @@ h1 { width: 100%; } } + +/* ── Vant Toast 保护规则 ────────────────────────────────── + .van-popup 声明 background: var(--van-popup-background) = #fff (白色) + .van-toast 声明 background: var(--van-toast-background) = rgba(0,0,0,.7) (黑色) + 两个类同优先级,但 .van-popup 在 vant/lib/index.css 中后声明 → 覆盖黑色背景 + 导致 Toast 白底白字看不见。用双类选择器提升优先级强制使用黑色背景。 + ────────────────────────────────────────────────────────── */ +.van-popup.van-toast { + background: var(--van-toast-background) !important; + color: var(--van-toast-text-color) !important; +} diff --git a/frontend/src/views/mobile/MobileHomeView.vue b/frontend/src/views/mobile/MobileHomeView.vue index d4c0f9e..07c9cda 100644 --- a/frontend/src/views/mobile/MobileHomeView.vue +++ b/frontend/src/views/mobile/MobileHomeView.vue @@ -155,7 +155,7 @@ async function onRefresh() { refreshing.value = true; try { listings.value = await fetchListings(); - showToast("刷新成功"); + showToast({ message: "刷新成功", icon: "passed" }); } catch { // 静默处理 } finally { diff --git a/frontend/src/views/mobile/MobileListingDetailView.vue b/frontend/src/views/mobile/MobileListingDetailView.vue index ff34f4f..353a59e 100644 --- a/frontend/src/views/mobile/MobileListingDetailView.vue +++ b/frontend/src/views/mobile/MobileListingDetailView.vue @@ -20,7 +20,7 @@ onMounted(async () => { try { listing.value = await fetchListing(String(route.params.id)); } catch { - showToast("加载失败"); + showToast({ message: "加载失败", icon: "warning-o" }); } finally { loading.value = false; } @@ -52,10 +52,10 @@ async function handleCreateOrder() { ordering.value = true; try { const order = await createOrder(listing.value.id, rentHours.value); - showToast("订单已创建,账号已锁定"); + showToast({ message: "订单已创建,账号已锁定", icon: "passed" }); await router.push(`/m/orders`); } catch (error) { - showToast(readError(error, "下单失败")); + showToast({ message: readError(error, "下单失败"), icon: "cross" }); } finally { ordering.value = false; } diff --git a/frontend/src/views/mobile/MobileLoginView.vue b/frontend/src/views/mobile/MobileLoginView.vue index df3c35c..7a42c55 100644 --- a/frontend/src/views/mobile/MobileLoginView.vue +++ b/frontend/src/views/mobile/MobileLoginView.vue @@ -40,17 +40,23 @@ onUnmounted(() => { /* --------- 业务方法 --------- */ async function handleSendCode() { if (!form.phone.trim()) { - showToast("请输入手机号"); + showToast({ message: "请输入手机号", icon: "warning-o" }); return; } sending.value = true; try { await sendSmsCode(form.phone); - showToast("验证码已发送,开发环境请查看后端日志"); + showToast({ + message: "验证码已发送,开发环境请查看后端日志", + icon: "passed", + }); startCountDown(); } catch (error) { - showToast(readError(error, "验证码发送失败,请稍后重试")); + showToast({ + message: readError(error, "验证码发送失败,请稍后重试"), + icon: "cross", + }); } finally { sending.value = false; } @@ -79,7 +85,7 @@ async function handleLogin() { await session.login(form.phone, form.code); await router.push("/m/profile"); } catch { - showToast("登录失败,请检查手机号和验证码"); + showToast({ message: "登录失败,请检查手机号和验证码", icon: "cross" }); } finally { loading.value = false; } diff --git a/frontend/src/views/mobile/MobileOrdersView.vue b/frontend/src/views/mobile/MobileOrdersView.vue index 205cc9e..b4f0225 100644 --- a/frontend/src/views/mobile/MobileOrdersView.vue +++ b/frontend/src/views/mobile/MobileOrdersView.vue @@ -24,7 +24,7 @@ async function loadOrders() { try { orders.value = await fetchOrders(); } catch { - showToast("订单加载失败,请稍后重试"); + showToast({ message: "订单加载失败,请稍后重试", icon: "warning-o" }); } finally { loading.value = false; } diff --git a/frontend/src/views/mobile/MobileProfileView.vue b/frontend/src/views/mobile/MobileProfileView.vue index ddec191..dcbf877 100644 --- a/frontend/src/views/mobile/MobileProfileView.vue +++ b/frontend/src/views/mobile/MobileProfileView.vue @@ -34,13 +34,13 @@ function onSettingClick(action: string) { showSettings.value = false; switch (action) { case "profile": - showToast("资料更改功能开发中"); + showToast({ message: "资料更改功能开发中", icon: "info-o" }); break; case "realname": router.push("/m/realname"); break; case "password": - showToast("密码管理功能开发中"); + showToast({ message: "密码管理功能开发中", icon: "info-o" }); break; case "cancel-account": showDialog({ @@ -52,15 +52,15 @@ function onSettingClick(action: string) { cancelButtonText: "再想想", }) .then(() => { - showToast("注销功能开发中"); + showToast({ message: "注销功能开发中", icon: "info-o" }); }) .catch(() => {}); break; case "privacy": - showToast("隐私政策页面开发中"); + showToast({ message: "隐私政策页面开发中", icon: "info-o" }); break; case "terms": - showToast("用户协议页面开发中"); + showToast({ message: "用户协议页面开发中", icon: "info-o" }); break; } } diff --git a/frontend/src/views/mobile/MobileRealnameView.vue b/frontend/src/views/mobile/MobileRealnameView.vue index d387c22..62e8060 100644 --- a/frontend/src/views/mobile/MobileRealnameView.vue +++ b/frontend/src/views/mobile/MobileRealnameView.vue @@ -31,11 +31,11 @@ async function loadStatus() { async function handleSubmit() { if (!form.name.trim()) { - showToast("请输入真实姓名"); + showToast({ message: "请输入真实姓名", icon: "warning-o" }); return; } if (!form.idNo.trim() || form.idNo.length < 15) { - showToast("请输入有效的证件号"); + showToast({ message: "请输入有效的证件号", icon: "warning-o" }); return; } loading.value = true; @@ -56,7 +56,7 @@ async function handleSubmit() { } ).response?.data?.message || "实名认证失败" : "实名认证失败"; - showToast(msg); + showToast({ message: msg, icon: "cross" }); } finally { loading.value = false; } diff --git a/frontend/src/views/mobile/MobileRegisterView.vue b/frontend/src/views/mobile/MobileRegisterView.vue index ce39452..949491b 100644 --- a/frontend/src/views/mobile/MobileRegisterView.vue +++ b/frontend/src/views/mobile/MobileRegisterView.vue @@ -42,17 +42,23 @@ onUnmounted(() => { /* --------- 业务方法 --------- */ async function handleSendCode() { if (!form.phone.trim()) { - showToast("请输入手机号"); + showToast({ message: "请输入手机号", icon: "warning-o" }); return; } sending.value = true; try { await sendSmsCode(form.phone); - showToast("验证码已发送,开发环境请查看后端日志"); + showToast({ + message: "验证码已发送,开发环境请查看后端日志", + icon: "passed", + }); startCountDown(); } catch (error) { - showToast(readError(error, "验证码发送失败,请稍后重试")); + showToast({ + message: readError(error, "验证码发送失败,请稍后重试"), + icon: "cross", + }); } finally { sending.value = false; } @@ -69,7 +75,10 @@ function readError(error: unknown, fallback: string) { async function handleRegister() { if (!agreed.value) { - showToast("请先阅读并同意用户协议和隐私政策"); + showToast({ + message: "请先阅读并同意用户协议和隐私政策", + icon: "warning-o", + }); return; } @@ -78,7 +87,7 @@ async function handleRegister() { await session.login(form.phone, form.code); await router.push("/m/profile"); } catch { - showToast("注册失败,请检查手机号和验证码"); + showToast({ message: "注册失败,请检查手机号和验证码", icon: "cross" }); } finally { loading.value = false; } diff --git a/frontend/src/views/mobile/MobileSellerListingCreateView.vue b/frontend/src/views/mobile/MobileSellerListingCreateView.vue index a769fda..79ae1c7 100644 --- a/frontend/src/views/mobile/MobileSellerListingCreateView.vue +++ b/frontend/src/views/mobile/MobileSellerListingCreateView.vue @@ -52,9 +52,9 @@ async function handleScreenshotUpload(event: Event) { try { const uploaded = await uploadFile(file, "listing"); screenshotUrls.value = [...screenshotUrls.value, uploaded.url]; - showToast("截图已上传"); + showToast({ message: "截图已上传", icon: "passed" }); } catch (error) { - showToast(readError(error, "截图上传失败")); + showToast({ message: readError(error, "截图上传失败"), icon: "cross" }); } finally { uploading.value = false; input.value = ""; @@ -68,16 +68,19 @@ function removeScreenshot(index: number) { /* 提交 */ async function handleSubmit() { if (!form.title.trim()) { - showToast("请输入标题"); + showToast({ message: "请输入标题", icon: "warning-o" }); return; } loading.value = true; try { await createListing({ ...form, screenshot_urls: screenshotUrls.value }); - showToast("发布成功"); + showToast({ message: "发布成功", icon: "passed" }); await router.push("/m/profile"); } catch (error) { - showToast(readError(error, "发布失败,请确认已登录并完成实名认证")); + showToast({ + message: readError(error, "发布失败,请确认已登录并完成实名认证"), + icon: "cross", + }); } finally { loading.value = false; }