登陆注册界面优化了

This commit is contained in:
yml
2026-05-22 20:38:52 +08:00
parent 5f1a7f46a8
commit aa9410b990
9 changed files with 1864 additions and 379 deletions
@@ -0,0 +1,447 @@
<script setup lang="ts">
import { onUnmounted, reactive, ref } from "vue";
import { RouterLink, useRouter } from "vue-router";
import { showToast } from "vant";
import { sendSmsCode } from "@/api/auth";
import { useSessionStore } from "@/stores/session";
const router = useRouter();
const session = useSessionStore();
const loading = ref(false);
const sending = ref(false);
const agreed = ref(false);
const form = reactive({
phone: "",
code: "",
inviteCode: "",
});
/* --------- 验证码倒计时 --------- */
const countDown = ref(0);
let timer: ReturnType<typeof setInterval> | null = null;
function startCountDown() {
countDown.value = 60;
timer = setInterval(() => {
countDown.value--;
if (countDown.value <= 0) {
clearInterval(timer!);
timer = null;
}
}, 1000);
}
onUnmounted(() => {
if (timer) {
clearInterval(timer);
timer = null;
}
});
/* --------- 业务方法 --------- */
async function handleSendCode() {
if (!form.phone.trim()) {
showToast("请输入手机号");
return;
}
sending.value = true;
try {
await sendSmsCode(form.phone);
showToast("验证码已发送,开发环境请查看后端日志");
startCountDown();
} catch (error) {
showToast(readError(error, "验证码发送失败,请稍后重试"));
} finally {
sending.value = false;
}
}
function readError(error: unknown, fallback: string) {
if (typeof error === "object" && error && "response" in error) {
const response = (error as { response?: { data?: { message?: string } } })
.response;
return response?.data?.message || fallback;
}
return fallback;
}
async function handleRegister() {
if (!agreed.value) {
showToast("请先阅读并同意用户协议和隐私政策");
return;
}
loading.value = true;
try {
await session.login(form.phone, form.code);
await router.push("/m/profile");
} catch {
showToast("注册失败,请检查手机号和验证码");
} finally {
loading.value = false;
}
}
</script>
<template>
<main class="mobile-register-shell">
<section class="register-card">
<!-- 品牌区域 -->
<div class="register-top">
<div class="monster-logo">锤</div>
<div>
<p>加入大锤商行</p>
<small>注册后即可租号、下单、发布账号</small>
</div>
</div>
<!-- 登录/注册 Tab 切换 -->
<div class="auth-switch" aria-label="登录注册切换">
<RouterLink to="/m/login">登录</RouterLink>
<RouterLink to="/m/register" class="active">注册</RouterLink>
</div>
<p class="register-eyebrow">CREATE ACCOUNT</p>
<h1>手机号注册</h1>
<p class="register-subtitle">
先用短信验证码创建账号,后续再补充实名和收款信息。
</p>
<!-- 手机号输入 -->
<van-field
v-model="form.phone"
type="tel"
maxlength="11"
inputmode="tel"
placeholder="请输入手机号"
clearable
class="register-field"
>
<template #left-icon>
<span class="field-prefix">+86</span>
</template>
</van-field>
<!-- 验证码输入 -->
<van-field
v-model="form.code"
type="digit"
maxlength="6"
placeholder="6 位验证码"
class="register-field"
:left-icon="'shield-o'"
>
<template #button>
<van-button
size="small"
type="primary"
plain
round
:disabled="sending || countDown > 0"
:loading="sending"
@click="handleSendCode"
>
{{
countDown > 0
? `${countDown}s`
: sending
? "发送中"
: "发送验证码"
}}
</van-button>
</template>
</van-field>
<!-- 邀请码输入 -->
<van-field
v-model="form.inviteCode"
maxlength="16"
placeholder="选填,有邀请码填入"
clearable
class="register-field"
:left-icon="'friends-o'"
/>
<!-- 用户协议 -->
<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
round
class="register-button"
:loading="loading"
loading-text="注册中..."
@click="handleRegister"
>
注册并登录
</van-button>
<!-- 底部登录入口 -->
<RouterLink
to="/m/login"
class="login-entry"
custom
v-slot="{ navigate }"
>
<van-button
plain
type="primary"
block
round
size="small"
class="login-link-button"
@click="navigate"
>
已有账号?去登录
</van-button>
</RouterLink>
</section>
</main>
</template>
<style scoped>
/* ========== Vant 主题变量覆盖 ========== */
:root {
--van-primary-color: #1777ff;
--van-primary-color-light: #5fd4ff;
--van-button-primary-background: linear-gradient(135deg, #1777ff, #5fd4ff);
--van-button-primary-border-color: transparent;
}
/* ========== 页面容器 ========== */
.mobile-register-shell {
min-height: 100dvh;
box-sizing: border-box;
overflow: hidden;
background: radial-gradient(
circle at 50% 0,
#eafff9 0,
#f7fbff 38%,
#ffffff 86%
);
color: #151c28;
padding: 40px 24px;
}
.register-card {
display: flex;
min-height: calc(100dvh - 80px);
flex-direction: column;
justify-content: center;
}
/* ========== 品牌区域 ========== */
.register-top {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.monster-logo {
display: grid;
width: 48px;
height: 48px;
flex: 0 0 auto;
place-items: center;
border-radius: 16px;
background: linear-gradient(145deg, #1777ff, #5fd4ff);
color: #ffffff;
font-size: 21px;
font-weight: 900;
box-shadow: 0 10px 22px rgba(23, 119, 255, 0.2);
}
.register-top p {
margin: 0 0 2px;
font-size: 20px;
font-weight: 900;
}
.register-top small,
.register-subtitle {
color: #667386;
}
/* ========== Tab 切换 ========== */
.auth-switch {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin-bottom: 16px;
border-radius: 15px;
background: #eef4fb;
padding: 5px;
}
.auth-switch a {
border-radius: 11px;
color: #667386;
padding: 10px 0;
text-align: center;
font-size: 14px;
font-weight: 900;
text-decoration: none;
}
.auth-switch a.active {
background: #ffffff;
color: #111827;
box-shadow: 0 8px 18px rgba(23, 35, 61, 0.08);
}
/* ========== 标题区 ========== */
.register-eyebrow {
margin: 0 0 5px;
color: #02846e;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.04em;
}
.register-card h1 {
margin: 0;
font-size: 28px;
line-height: 1.15;
}
.register-subtitle {
margin: 7px 0 14px;
font-size: 13px;
line-height: 1.45;
}
/* ========== Vant Field 覆盖样式 ========== */
.register-field {
margin-top: 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.92);
box-shadow: inset 0 1px 3px rgba(23, 35, 61, 0.04);
overflow: hidden;
}
.register-field :deep(.van-field__control) {
font-size: 15px;
color: #151c28;
}
.register-field :deep(.van-field__label) {
width: auto;
margin-right: 8px;
color: #333b48;
font-weight: 700;
font-size: 13px;
}
.register-field :deep(.van-field__left-icon) {
color: #1777ff;
margin-right: 0;
}
.register-field :deep(.van-field__right-icon) {
color: #999;
}
.field-prefix {
color: #1777ff;
font-weight: 800;
font-size: 14px;
margin-right: 4px;
}
/* ========== 协议行 ========== */
.agreement-row {
display: flex;
align-items: center;
margin-top: 16px;
margin-bottom: 16px;
}
.agreement-row :deep(.van-checkbox__label) {
margin-left: 6px;
color: #333b48;
font-size: 12px;
line-height: 1.4;
}
.agreement-row :deep(.van-checkbox__label b) {
color: #1777ff;
font-weight: 600;
}
/* ========== 注册按钮 ========== */
.register-button {
margin-top: 6px;
height: 46px;
font-size: 15px;
font-weight: 800;
background: linear-gradient(135deg, #1777ff, #5fd4ff) !important;
border: none !important;
box-shadow: 0 8px 20px rgba(23, 119, 255, 0.25);
}
/* ========== 登录入口按钮 ========== */
.login-link-button {
margin-top: 12px;
font-size: 13px;
font-weight: 800;
}
.login-entry {
text-decoration: none;
}
/* ========== 响应式 ========== */
@media (max-height: 620px) {
.mobile-register-shell {
padding-top: 10px;
padding-bottom: 10px;
}
.register-card {
min-height: calc(100dvh - 20px);
}
.register-top {
margin-bottom: 10px;
}
.monster-logo {
width: 42px;
height: 42px;
border-radius: 14px;
}
.auth-switch {
margin-bottom: 11px;
}
.register-card h1 {
font-size: 24px;
}
.register-field {
margin-top: 8px;
}
.register-button {
height: 42px;
margin-top: 10px;
}
}
@media (min-width: 520px) {
.mobile-register-shell {
max-width: 430px;
margin: 0 auto;
box-shadow: 0 0 0 1px rgba(23, 35, 61, 0.08);
}
}
</style>