Files
hfb_sys/frontend/src/features/auth/views/MobileLoginView.vue
T
yml2213 b00049de2d chore: 修复 auth 和 admin 视图导入路径
- 修复 AdminLoginView 使用 features/admin
- 修复 LoginView, MobileLoginView, MobileRegisterView 使用 features/auth
- 修复 MobileProfileView, MobileRealnameView 使用正确路径
- 修复 useSmsCountdown 导入到 shared/composables

类型错误:79 → ?
模块错误:38 → 31
2026-06-04 10:07:39 +08:00

344 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { reactive, ref } from "vue";
import { RouterLink, useRoute, useRouter } from "vue-router";
import { showToast, showDialog } from "vant";
import { useSessionStore } from "@/stores/session";
import { useSmsCountdown } from "@/shared/composables/useSmsCountdown";
const router = useRouter();
const route = useRoute();
const session = useSessionStore();
const loading = ref(false);
const agreed = ref(false);
const form = reactive({
phone: "",
code: "",
});
const { countDown, sending, handleSendCode, readError } = useSmsCountdown();
async function handleLogin() {
if (!agreed.value) {
showDialog({
title: "提示",
message: "请先阅读并同意用户协议和隐私政策",
});
return;
}
loading.value = true;
try {
await session.login(form.phone, form.code);
const redirect = typeof route.query.redirect === "string" ? route.query.redirect : "/m/profile";
await router.replace(redirect);
} catch {
showToast({ message: "登录失败,请检查手机号和验证码", icon: "cross" });
} finally {
loading.value = false;
}
}
</script>
<template>
<main class="mobile-login-shell">
<header class="page-header">
<button class="back-btn" type="button" @click="router.back()">
<van-icon name="arrow-left" :size="20" />
</button>
</header>
<section class="auth-body">
<div class="brand-lockup">
<div class="brand-logo"></div>
<strong>大锤商行</strong>
</div>
<div class="form-section">
<h1>登录</h1>
<label class="auth-input-row">
<input
v-model="form.phone"
type="tel"
maxlength="11"
placeholder="手机号"
/>
</label>
<label class="auth-input-row code-row">
<input
v-model="form.code"
type="text"
inputmode="numeric"
maxlength="6"
placeholder="验证码"
/>
<span class="code-action">
<van-button
size="small"
plain
:disabled="sending || countDown > 0"
:loading="sending"
class="code-btn"
@click="handleSendCode(form.phone)"
>
{{
countDown > 0
? `${countDown}s`
: sending
? "发送中"
: "发送验证码"
}}
</van-button>
</span>
</label>
<div class="assist-row">
<span>验证码登录</span>
<RouterLink to="/m/register">没有账号<b>立即注册</b></RouterLink>
</div>
<div class="agreement-row">
<van-checkbox v-model="agreed" shape="square" icon-size="16px">
我已阅读并同意<b>用户协议</b><b>隐私政策</b>
</van-checkbox>
</div>
<van-button
type="primary"
block
class="primary-button"
:loading="loading"
loading-text="登录中..."
@click="handleLogin"
>
登录
</van-button>
<RouterLink to="/m/register" class="secondary-entry">
还没有账号创建一个
</RouterLink>
</div>
</section>
</main>
</template>
<style scoped>
.mobile-login-shell {
min-height: 100dvh;
background:
radial-gradient(circle at 50% 12%, rgba(20, 119, 255, 0.1), transparent 34%),
linear-gradient(180deg, #f8fbff 0%, #ffffff 70%, #f7fafc 100%);
color: #111827;
}
.page-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 10;
display: flex;
align-items: center;
height: 48px;
margin: 0;
padding: 0 18px;
background: transparent;
}
.back-btn {
display: grid;
width: 34px;
height: 34px;
place-items: center;
border: none;
border-radius: 999px;
background: none;
color: #111827;
}
.auth-body {
box-sizing: border-box;
display: flex;
min-height: 100dvh;
flex-direction: column;
justify-content: center;
padding: 54px 34px 28px;
}
.brand-lockup {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin: 0 auto clamp(34px, 6vh, 54px);
}
.brand-logo {
display: grid;
width: 46px;
height: 46px;
place-items: center;
border-radius: 14px;
background: linear-gradient(135deg, #1477ff, #2f8dff);
box-shadow: 0 14px 32px rgba(20, 119, 255, 0.18);
color: #ffffff;
font-size: 19px;
font-weight: 900;
}
.brand-lockup strong {
display: block;
color: #111827;
font-size: 18px;
font-weight: 900;
letter-spacing: 0;
}
.form-section {
width: min(100%, 420px);
margin: 0 auto;
}
.form-section h1 {
margin: 0 0 22px;
color: #05070a;
font-size: 22px;
font-weight: 900;
line-height: 1.2;
}
.auth-input-row {
display: flex;
align-items: center;
min-height: 48px;
margin-bottom: 10px;
padding: 0 16px;
border: 1px solid #e1e5eb;
border-radius: 10px;
background: rgba(255, 255, 255, 0.88);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.03);
}
.auth-input-row.code-row {
gap: 8px;
}
.auth-input-row input {
width: 100%;
min-width: 0;
border: none;
background: transparent;
color: #0f172a;
font-size: 16px;
outline: none;
}
.auth-input-row input::placeholder {
color: #b6beca;
}
.code-action {
display: flex;
justify-content: flex-end;
}
.code-btn {
min-width: 82px;
height: 32px;
border-color: #1477ff !important;
border-radius: 8px;
color: #1477ff !important;
font-weight: 700;
}
.assist-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
margin: 14px 0 24px;
color: #111827;
font-size: 13px;
}
.assist-row span {
min-width: 0;
color: #1477ff;
}
.assist-row a {
flex-shrink: 0;
color: #22252b;
text-decoration: none;
}
.assist-row b {
color: #05070a;
font-weight: 900;
}
.agreement-row {
display: flex;
align-items: center;
margin: 0 0 20px;
}
.agreement-row :deep(.van-checkbox__label) {
margin-left: 8px;
color: #252b36;
font-size: 12px;
line-height: 1.4;
}
.agreement-row :deep(.van-checkbox__label b) {
color: #1477ff;
font-weight: 700;
}
.primary-button {
height: 48px;
border: none !important;
border-radius: 10px !important;
background: #1477ff !important;
box-shadow: 0 12px 24px rgba(20, 119, 255, 0.18);
font-size: 17px;
font-weight: 900;
}
.secondary-entry {
display: grid;
min-height: 46px;
margin-top: 12px;
place-items: center;
border: 1px solid #e4e8ee;
border-radius: 10px;
background: rgba(255, 255, 255, 0.82);
color: #1477ff;
font-size: 15px;
font-weight: 900;
text-align: center;
text-decoration: none;
}
@media (max-height: 700px) {
.auth-body {
justify-content: flex-start;
padding-top: 52px;
}
.brand-lockup {
margin-bottom: 26px;
}
.form-section h1 {
margin-bottom: 18px;
}
.assist-row {
margin-bottom: 18px;
}
}
</style>