feat:移动端增加注册模块

This commit is contained in:
jiangrui
2025-03-12 12:27:34 +08:00
parent dca0f1f0c1
commit 8fa1ed8fc2
2 changed files with 248 additions and 79 deletions

View File

@@ -50,12 +50,15 @@ declare module 'vue' {
VanCheckbox: typeof import('vant/es')['Checkbox']
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
VanLoading: typeof import('vant/es')['Loading']
VanOverlay: typeof import('vant/es')['Overlay']
VanPopup: typeof import('vant/es')['Popup']
VanSearch: typeof import('vant/es')['Search']
VanSwitch: typeof import('vant/es')['Switch']
VanTab: typeof import('vant/es')['Tab']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']

View File

@@ -11,64 +11,140 @@
<h1 class="login__title">Cloud Saver</h1>
</header>
<!-- 登录表单 -->
<van-form class="login__form" @submit="handleSubmit">
<van-cell-group inset class="login__form-group">
<!-- 用户名输入框 -->
<van-field
v-model="formData.username"
name="username"
label="用户名"
placeholder="请输入用户名"
:rules="[{ required: true, message: '请填写用户名' }]"
autocomplete="username"
@keyup.enter="focusPassword"
>
<template #left-icon>
<van-icon name="user-o" />
</template>
</van-field>
<!-- 添加 Tab 切换 -->
<van-tabs v-model:active="activeTab" class="login__tabs">
<!-- 登录面板 -->
<van-tab title="登录" name="login">
<van-form class="login__form" @submit="handleLogin">
<van-cell-group inset class="login__form-group">
<!-- 用户名输入框 -->
<van-field
v-model="loginForm.username"
name="username"
label="用户名"
placeholder="请输入用户名"
:rules="[{ required: true, message: '请填写用户名' }]"
autocomplete="username"
@keyup.enter="focusLoginPassword"
>
<template #left-icon>
<van-icon name="user-o" />
</template>
</van-field>
<!-- 密码输入框 -->
<van-field
ref="passwordRef"
v-model="formData.password"
type="password"
name="password"
label="密码"
placeholder="请输入密码"
:rules="[{ required: true, message: '请填写密码' }]"
autocomplete="current-password"
@keyup.enter="handleSubmit"
>
<template #left-icon>
<van-icon name="lock" />
</template>
</van-field>
<!-- 密码输入框 -->
<van-field
ref="loginPasswordRef"
v-model="loginForm.password"
type="password"
name="password"
label="密码"
placeholder="请输入密码"
:rules="[{ required: true, message: '请填写密码' }]"
autocomplete="current-password"
@keyup.enter="handleLogin"
>
<template #left-icon>
<van-icon name="lock" />
</template>
</van-field>
<!-- 优化记住密码选项 -->
<div class="login__remember">
<van-checkbox v-model="rememberPassword" class="remember-checkbox">
记住密码
</van-checkbox>
</div>
</van-cell-group>
<!-- 优化记住密码选项 -->
<div class="login__remember">
<van-checkbox v-model="rememberPassword" class="remember-checkbox">
记住密码
</van-checkbox>
</div>
</van-cell-group>
<!-- 登录按钮 -->
<div class="login__submit">
<van-button
:loading="isLoading"
:disabled="isLoading"
round
block
type="primary"
native-type="submit"
class="login__button"
>
{{ isLoading ? "登录中..." : "登录" }}
</van-button>
</div>
</van-form>
<!-- 登录按钮 -->
<div class="login__submit">
<van-button
:loading="isLoading"
:disabled="isLoading"
round
block
type="primary"
native-type="submit"
class="login__button"
>
{{ isLoading ? "登录中..." : "登录" }}
</van-button>
</div>
</van-form>
</van-tab>
<!-- 注册面板 -->
<van-tab title="注册" name="register">
<van-form class="login__form" @submit="handleRegister">
<van-cell-group inset class="login__form-group">
<van-field
v-model="registerForm.username"
name="username"
label="用户名"
placeholder="请输入用户名"
:rules="usernameRules"
>
<template #left-icon>
<van-icon name="user-o" />
</template>
</van-field>
<van-field
v-model="registerForm.password"
type="password"
name="password"
label="密码"
placeholder="请输入密码"
:rules="passwordRules"
>
<template #left-icon>
<van-icon name="lock" />
</template>
</van-field>
<van-field
v-model="registerForm.confirmPassword"
type="password"
name="confirmPassword"
label="确认密码"
placeholder="请确认密码"
:rules="confirmPasswordRules"
>
<template #left-icon>
<van-icon name="lock" />
</template>
</van-field>
<van-field
v-model="registerForm.registerCode"
name="registerCode"
label="注册码"
placeholder="请输入注册码"
:rules="registerCodeRules"
>
<template #left-icon>
<van-icon name="certificate" />
</template>
</van-field>
</van-cell-group>
<div class="login__submit">
<van-button
:loading="isLoading"
:disabled="isLoading"
round
block
type="primary"
native-type="submit"
class="login__button"
>
{{ isLoading ? "注册中..." : "注册" }}
</van-button>
</div>
</van-form>
</van-tab>
</van-tabs>
</main>
</div>
</template>
@@ -77,7 +153,7 @@
import { ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import { showNotify } from "vant";
import type { FieldInstance } from "vant";
import type { FieldInstance, FieldRule } from "vant";
import { userApi } from "@/api/user";
import logo from "@/assets/images/logo.png";
import { STORAGE_KEYS } from "@/constants/storage";
@@ -88,21 +164,42 @@ interface LoginForm {
password: string;
}
interface RegisterForm {
username: string;
password: string;
confirmPassword: string;
registerCode: string;
}
// 响应式数据
const formData = ref<LoginForm>({
const activeTab = ref("login");
const isLoading = ref(false);
const loginPasswordRef = ref<FieldInstance>();
const rememberPassword = ref(false);
const loginForm = ref<LoginForm>({
username: "",
password: "",
});
const isLoading = ref(false);
const passwordRef = ref<FieldInstance>();
const rememberPassword = ref(false);
const registerForm = ref<RegisterForm>({
username: "",
password: "",
confirmPassword: "",
registerCode: "",
});
// 工具函数
const router = useRouter();
// 方法定义
const focusPassword = () => {
passwordRef.value?.focus();
const focusLoginPassword = () => {
loginPasswordRef.value?.focus();
};
// 表单验证
const validateConfirmPassword = (value: string) => {
return value === registerForm.value.password;
};
// 在组件加载时检查是否有保存的账号密码
@@ -110,22 +207,22 @@ onMounted(() => {
const savedUsername = localStorage.getItem(STORAGE_KEYS.USERNAME);
const savedPassword = localStorage.getItem(STORAGE_KEYS.PASSWORD);
if (savedUsername && savedPassword) {
formData.value.username = savedUsername;
formData.value.password = savedPassword;
loginForm.value.username = savedUsername;
loginForm.value.password = savedPassword;
rememberPassword.value = true;
}
});
const handleSubmit = async () => {
// 登录处理
const handleLogin = async () => {
try {
isLoading.value = true;
const res = await userApi.login(formData.value);
const res = await userApi.login(loginForm.value);
if (res.code === 0) {
// 处理记住密码
if (rememberPassword.value) {
localStorage.setItem(STORAGE_KEYS.USERNAME, formData.value.username);
localStorage.setItem(STORAGE_KEYS.PASSWORD, formData.value.password);
localStorage.setItem(STORAGE_KEYS.USERNAME, loginForm.value.username);
localStorage.setItem(STORAGE_KEYS.PASSWORD, loginForm.value.password);
} else {
localStorage.removeItem(STORAGE_KEYS.USERNAME);
localStorage.removeItem(STORAGE_KEYS.PASSWORD);
@@ -134,22 +231,65 @@ const handleSubmit = async () => {
localStorage.setItem(STORAGE_KEYS.TOKEN, res.data.token);
await router.push("/");
} else {
showNotify({
type: "danger",
message: res.message || "登录失败",
duration: 2000,
});
showNotify({ type: "danger", message: res.message || "登录失败" });
}
} catch (error) {
showNotify({
type: "danger",
message: "登录失败",
duration: 2000,
});
showNotify({ type: "danger", message: "登录失败" });
} finally {
isLoading.value = false;
}
};
// 注册处理
const handleRegister = async () => {
try {
isLoading.value = true;
const res = await userApi.register({
username: registerForm.value.username,
password: registerForm.value.password,
registerCode: registerForm.value.registerCode,
});
if (res.code === 0) {
showNotify({ type: "success", message: "注册成功" });
// 自动填充登录表单
loginForm.value.username = registerForm.value.username;
loginForm.value.password = registerForm.value.password;
activeTab.value = "login";
// 清空注册表单
registerForm.value = {
username: "",
password: "",
confirmPassword: "",
registerCode: "",
};
} else {
showNotify({ type: "danger", message: res.message || "注册失败" });
}
} catch (error) {
showNotify({ type: "danger", message: "注册失败" });
} finally {
isLoading.value = false;
}
};
// 定义验证规则
const usernameRules: FieldRule[] = [
{ required: true, message: "请填写用户名" },
{ pattern: /.{3,}/, message: "用户名至少3个字符" },
];
const passwordRules: FieldRule[] = [
{ required: true, message: "请填写密码" },
{ pattern: /.{6,}/, message: "密码至少6个字符" },
];
const confirmPasswordRules: FieldRule[] = [
{ required: true, message: "请确认密码" },
{ validator: validateConfirmPassword, message: "两次密码不一致" },
];
const registerCodeRules: FieldRule[] = [{ required: true, message: "请填写注册码" }];
</script>
<style lang="scss" scoped>
@@ -231,6 +371,32 @@ const handleSubmit = async () => {
padding: 12px 16px;
border-top: 0.5px solid #f5f5f5;
}
&__tabs {
:deep() {
.van-tabs__wrap {
padding: 0 12px;
}
.van-tabs__nav {
background: transparent;
}
.van-tab {
color: var(--theme-color);
font-size: 16px;
}
.van-tab--active {
color: var(--theme-theme);
font-weight: 500;
}
.van-tabs__line {
background-color: var(--theme-theme);
}
}
}
}
// Vant 组件样式优化