feat: 添加注册界面
This commit is contained in:
parent
fae07d9eae
commit
3f2c772eeb
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741694797806" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2622" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M192 192m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" p-id="2623"></path><path d="M192 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" p-id="2624"></path><path d="M192 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" p-id="2625"></path><path d="M864 160H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 480H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 800H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32z" p-id="2626"></path></svg>
|
Before Width: | Height: | Size: 870 B |
|
@ -1 +0,0 @@
|
|||
<svg t="1741522876251" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3628" width="200" height="200"><path d="M327.04 85.333333h369.92C841.472 85.333333 938.666667 186.794667 938.666667 337.749333v348.501334C938.666667 837.205333 841.472 938.666667 696.874667 938.666667h-229.546667a32 32 0 0 1 0-64h229.546667c107.989333 0 177.792-73.941333 177.792-188.416V337.749333c0-114.474667-69.802667-188.416-177.749334-188.416H327.04C219.093333 149.333333 149.333333 223.274667 149.333333 337.749333v348.501334c0 114.474667 69.76 188.416 177.706667 188.416a32 32 0 0 1 0 64C182.442667 938.666667 85.333333 837.205333 85.333333 686.250667V337.749333C85.333333 186.794667 182.442667 85.333333 327.04 85.333333z m-51.114667 381.098667a31.914667 31.914667 0 0 1 42.325334-16.042667 31.914667 31.914667 0 0 1 16.042666 42.282667A47.061333 47.061333 0 1 0 424.192 512c0-25.898667-21.077333-46.933333-47.018667-46.933333a32 32 0 0 1 0-64c50.048 0 91.904 33.408 105.728 78.933333h242.858667a32 32 0 0 1 32 32v79.018667a32 32 0 0 1-64 0V544h-56.704v47.018667a32 32 0 0 1-64 0V544h-90.154667a110.72 110.72 0 0 1-105.728 79.018667 111.104 111.104 0 0 1-101.248-156.544z" fill="#200E32" p-id="3629"></path></svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1 +0,0 @@
|
|||
<svg t="1741522263287" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2626" width="200" height="200"><path d="M511.913993 941.605241c-255.612968 0-385.311608-57.452713-385.311608-170.810012 0-80.846632 133.654964-133.998992 266.621871-151.88846L393.224257 602.049387c-79.986561-55.904586-118.86175-153.436587-118.86175-297.240383 0-139.33143 87.211154-222.586259 233.423148-222.586259l7.912649 0c146.211994 0 233.423148 83.254829 233.423148 222.586259 0 54.184445 0 214.67361-117.829666 297.412397l-0.344028 16.685369c132.966907 18.061482 266.105829 71.041828 266.105829 151.716445C897.225601 884.152528 767.526961 941.605241 511.913993 941.605241zM507.957668 141.567613c-79.470519 0-174.250294 28.382328-174.250294 163.241391 0 129.698639 34.230808 213.469511 104.584579 255.784982 8.944734 5.332437 14.277171 14.965228 14.277171 25.286074l0 59.344868c0 15.309256-11.524945 28.0383-26.662187 29.414413-144.319839 14.449185-239.959684 67.429531-239.959684 95.983874 0 92.199563 177.346548 111.637158 325.966739 111.637158 148.792206 0 325.966739-19.26558 325.966739-111.637158 0-28.726356-95.639845-81.534688-239.959684-95.983874-15.48127-1.548127-27.006215-14.621199-26.662187-30.102469l1.376113-59.344868c0.172014-10.148833 5.676466-19.437594 14.277171-24.770032 70.525785-42.487485 103.208466-123.678145 103.208466-255.784982 0-135.031077-94.779775-163.241391-174.250294-163.241391L507.957668 141.567613 507.957668 141.567613z" fill="#575B66" p-id="2627"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,145 +0,0 @@
|
|||
<template>
|
||||
<div class="card card-dash h-80 w-100 shadow-xl bg-base-100">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title place-self-center my-3 text-2xl">User Login</h1>
|
||||
<div class="flex flex-col w-full h-full">
|
||||
<label class="input w-full my-3">
|
||||
<img
|
||||
class="h-[1em] opacity-50"
|
||||
src="@/assets/user.svg"
|
||||
alt="User img"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="用户名"
|
||||
v-model="username"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</label>
|
||||
<label class="input w-full my-3">
|
||||
<img
|
||||
class="h-[1em] opacity-50"
|
||||
src="@/assets/pwd.svg"
|
||||
alt="User img"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
class="grow"
|
||||
placeholder="密码"
|
||||
v-model="password"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex justify-end mx-3">
|
||||
<RouterLink to="/">忘记密码?</RouterLink>
|
||||
</div>
|
||||
<div class="card-actions flex items-end my-3">
|
||||
<button class="btn flex-1" @click="handleRegister">注册</button>
|
||||
<button
|
||||
class="btn btn-primary flex-3"
|
||||
@click="handleLogin"
|
||||
:disabled="isLoading"
|
||||
>
|
||||
{{ isLoading ? "登录中..." : "登录" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 获取Alert store
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
// 响应式数据
|
||||
const username = ref("");
|
||||
const password = ref("");
|
||||
const isLoading = ref(false);
|
||||
|
||||
// 登录处理函数
|
||||
const handleLogin = async () => {
|
||||
// 验证输入
|
||||
if (!username.value.trim()) {
|
||||
alertStore?.show("请输入用户名", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password.value.trim()) {
|
||||
alertStore?.show("请输入密码", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调用AuthManager的登录函数
|
||||
await AuthManager.login(username.value.trim(), password.value.trim());
|
||||
|
||||
// 登录成功,显示成功消息并跳转
|
||||
alertStore?.show("登录成功", "success", 1000);
|
||||
|
||||
// 短暂延迟后跳转到project页面
|
||||
setTimeout(async () => {
|
||||
await router.push("/project");
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
console.error("Login error:", error);
|
||||
|
||||
// 处理不同类型的错误
|
||||
let errorMessage = "登录失败,请检查网络连接";
|
||||
|
||||
if (error.status === 400) {
|
||||
errorMessage = "用户名或密码错误";
|
||||
} else if (error.status === 401) {
|
||||
errorMessage = "用户名或密码错误";
|
||||
} else if (error.status === 500) {
|
||||
errorMessage = "服务器错误,请稍后重试";
|
||||
} else if (error.message) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
alertStore?.show(errorMessage, "error");
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 注册处理函数(可以根据需要实现)
|
||||
const handleRegister = () => {
|
||||
// 这里可以导航到注册页面或者打开注册模态框
|
||||
console.log("Navigate to register page");
|
||||
// router.push('/register')
|
||||
};
|
||||
|
||||
// 页面初始化时检查是否已有有效token
|
||||
const checkExistingToken = async () => {
|
||||
try {
|
||||
const isValid = await AuthManager.verifyToken();
|
||||
if (isValid) {
|
||||
// 如果token仍然有效,直接跳转到project页面
|
||||
await router.push("/project");
|
||||
}
|
||||
} catch (error) {
|
||||
// token无效或验证失败,继续显示登录页面
|
||||
console.log("Token verification failed, showing login page");
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时检查已存在的token
|
||||
onMounted(() => {
|
||||
checkExistingToken();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "@/assets/main.css";
|
||||
</style>
|
|
@ -1,22 +1,18 @@
|
|||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import LoginView from "../views/LoginView.vue";
|
||||
import LabView from "../views/LabView.vue";
|
||||
import AuthView from "../views/AuthView.vue";
|
||||
import ProjectView from "../views/Project/Index.vue";
|
||||
import TestView from "../views/TestView.vue";
|
||||
import UserView from "@/views/User/Index.vue";
|
||||
import AdminView from "../views/AdminView.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{ path: "/", name: "home", component: HomeView },
|
||||
{ path: "/login", name: "login", component: LoginView },
|
||||
{ path: "/lab/:id", name: "lab", component: LabView },
|
||||
{ path: "/login", name: "login", component: AuthView },
|
||||
{ path: "/project", name: "project", component: ProjectView },
|
||||
{ path: "/test", name: "test", component: TestView },
|
||||
{ path: "/user", name: "user", component: UserView },
|
||||
{ path: "/admin", name: "admin", component: AdminView },
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
<template>
|
||||
<div class="flex items-center justify-center min-h-screen bg-base-200">
|
||||
<div class="relative w-full max-w-md">
|
||||
<!-- Login Card -->
|
||||
<div v-if="!showSignUp" class="card card-dash h-80 w-100 shadow-xl bg-base-100">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title place-self-center my-3 text-2xl">用户登录</h1>
|
||||
<div class="flex flex-col w-full h-full">
|
||||
<label class="input w-full my-3">
|
||||
<User class="h-[1em] opacity-50" />
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="用户名"
|
||||
v-model="username"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</label>
|
||||
<label class="input w-full my-3">
|
||||
<Lock class="h-[1em] opacity-50" />
|
||||
<input
|
||||
type="password"
|
||||
class="grow"
|
||||
placeholder="密码"
|
||||
v-model="password"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex justify-end mx-3">
|
||||
<RouterLink to="/">忘记密码?</RouterLink>
|
||||
</div>
|
||||
<div class="card-actions flex items-end my-3">
|
||||
<button class="btn flex-1" @click="handleRegister">注册</button>
|
||||
<button
|
||||
class="btn btn-primary flex-3"
|
||||
@click="handleLogin"
|
||||
:disabled="isLoading"
|
||||
>
|
||||
{{ isLoading ? "登录中..." : "登录" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sign Up Card -->
|
||||
<div v-if="showSignUp" class="card card-dash h-96 w-100 shadow-xl bg-base-100">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title place-self-center my-3 text-2xl">用户注册</h1>
|
||||
<div class="flex flex-col w-full h-full">
|
||||
<label class="input w-full my-2">
|
||||
<User class="h-[1em] opacity-50" />
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="用户名"
|
||||
v-model="signUpData.username"
|
||||
@keyup.enter="handleSignUp"
|
||||
/>
|
||||
</label>
|
||||
<label class="input w-full my-2">
|
||||
<Mail class="h-[1em] opacity-50" />
|
||||
<input
|
||||
type="email"
|
||||
class="grow"
|
||||
placeholder="邮箱"
|
||||
v-model="signUpData.email"
|
||||
@keyup.enter="handleSignUp"
|
||||
/>
|
||||
</label>
|
||||
<label class="input w-full my-2">
|
||||
<Lock class="h-[1em] opacity-50" />
|
||||
<input
|
||||
type="password"
|
||||
class="grow"
|
||||
placeholder="密码"
|
||||
v-model="signUpData.password"
|
||||
@keyup.enter="handleSignUp"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="card-actions flex items-end my-3">
|
||||
<button class="btn flex-1" @click="backToLogin">返回登录</button>
|
||||
<button
|
||||
class="btn btn-primary flex-3"
|
||||
@click="handleSignUp"
|
||||
:disabled="isSignUpLoading"
|
||||
>
|
||||
{{ isSignUpLoading ? "注册中..." : "注册" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
import { User, Lock, Mail } from "lucide-vue-next";
|
||||
import { DataClient } from "@/APIClient";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 获取Alert store
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
// 创建API客户端实例
|
||||
const dataClient = new DataClient();
|
||||
|
||||
// 响应式数据
|
||||
const username = ref("");
|
||||
const password = ref("");
|
||||
const isLoading = ref(false);
|
||||
|
||||
// 注册相关数据
|
||||
const showSignUp = ref(false);
|
||||
const isSignUpLoading = ref(false);
|
||||
const signUpData = ref({
|
||||
username: "",
|
||||
email: "",
|
||||
password: ""
|
||||
});
|
||||
|
||||
// 登录处理函数
|
||||
const handleLogin = async () => {
|
||||
// 验证输入
|
||||
if (!username.value.trim()) {
|
||||
alertStore?.show("请输入用户名", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password.value.trim()) {
|
||||
alertStore?.show("请输入密码", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调用AuthManager的登录函数
|
||||
await AuthManager.login(username.value.trim(), password.value.trim());
|
||||
|
||||
// 登录成功,显示成功消息并跳转
|
||||
alertStore?.show("登录成功", "success", 1000);
|
||||
|
||||
// 短暂延迟后跳转到project页面
|
||||
setTimeout(async () => {
|
||||
await router.push("/project");
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
console.error("Login error:", error);
|
||||
|
||||
// 处理不同类型的错误
|
||||
let errorMessage = "登录失败,请检查网络连接";
|
||||
|
||||
if (error.status === 400) {
|
||||
errorMessage = "用户名或密码错误";
|
||||
} else if (error.status === 401) {
|
||||
errorMessage = "用户名或密码错误";
|
||||
} else if (error.status === 500) {
|
||||
errorMessage = "服务器错误,请稍后重试";
|
||||
} else if (error.message) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
alertStore?.show(errorMessage, "error");
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 注册处理函数
|
||||
const handleRegister = () => {
|
||||
showSignUp.value = true;
|
||||
// 清空注册表单
|
||||
signUpData.value = {
|
||||
username: "",
|
||||
email: "",
|
||||
password: ""
|
||||
};
|
||||
};
|
||||
|
||||
// 返回登录页面
|
||||
const backToLogin = () => {
|
||||
showSignUp.value = false;
|
||||
};
|
||||
|
||||
// 注册提交处理函数
|
||||
const handleSignUp = async () => {
|
||||
// 验证输入
|
||||
if (!signUpData.value.username.trim()) {
|
||||
alertStore?.show("请输入用户名", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!signUpData.value.email.trim()) {
|
||||
alertStore?.show("请输入邮箱", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// 简单的邮箱格式验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(signUpData.value.email.trim())) {
|
||||
alertStore?.show("请输入有效的邮箱地址", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!signUpData.value.password.trim()) {
|
||||
alertStore?.show("请输入密码", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// 密码长度验证
|
||||
if (signUpData.value.password.length < 6) {
|
||||
alertStore?.show("密码长度至少6位", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
isSignUpLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调用注册API
|
||||
const result = await dataClient.signUpUser(
|
||||
signUpData.value.username.trim(),
|
||||
signUpData.value.email.trim(),
|
||||
signUpData.value.password.trim()
|
||||
);
|
||||
|
||||
if (result) {
|
||||
// 注册成功
|
||||
alertStore?.show("注册成功!请登录", "success", 2000);
|
||||
|
||||
// 延迟后返回登录页面
|
||||
setTimeout(() => {
|
||||
backToLogin();
|
||||
}, 2000);
|
||||
} else {
|
||||
alertStore?.show("注册失败,请重试", "error");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Sign up error:", error);
|
||||
|
||||
let errorMessage = "注册失败,请检查网络连接";
|
||||
|
||||
if (error.status === 400) {
|
||||
// 检查是否有详细的错误信息
|
||||
if (error.result && error.result.detail) {
|
||||
errorMessage = error.result.detail;
|
||||
} else {
|
||||
errorMessage = "注册信息无效,请检查输入";
|
||||
}
|
||||
} else if (error.status === 500) {
|
||||
errorMessage = "服务器错误,请稍后重试";
|
||||
} else if (error.message) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
alertStore?.show(errorMessage, "error");
|
||||
} finally {
|
||||
isSignUpLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 页面初始化时检查是否已有有效token
|
||||
const checkExistingToken = async () => {
|
||||
try {
|
||||
const isValid = await AuthManager.verifyToken();
|
||||
if (isValid) {
|
||||
// 如果token仍然有效,直接跳转到project页面
|
||||
await router.push("/project");
|
||||
}
|
||||
} catch (error) {
|
||||
// token无效或验证失败,继续显示登录页面
|
||||
console.log("Token verification failed, showing login page");
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时检查已存在的token
|
||||
onMounted(() => {
|
||||
checkExistingToken();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,11 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss"></style>
|
|
@ -1,13 +0,0 @@
|
|||
<template>
|
||||
<div class="flex items-center justify-center min-h-screen bg-base-200">
|
||||
<div class="relative w-full max-w-md">
|
||||
<LoginCard />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LoginCard from "@/components/LoginCard.vue";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -249,5 +249,5 @@ async function refreshData() {
|
|||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
@import "../assets/main.css";
|
||||
@import "@/assets/main.css";
|
||||
</style>
|
|
@ -7,6 +7,9 @@
|
|||
<li id="2" @click="setActivePage">
|
||||
<a :class="{ 'menu-active': activePage === 2 }">Item 2</a>
|
||||
</li>
|
||||
<li id="" @click="setActivePage">
|
||||
<a :class="{ 'menu-active': activePage === 2 }">Item 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="divider divider-horizontal h-full"></div>
|
||||
<div class="card bg-base-200 w-300"></div>
|
||||
|
|
Loading…
Reference in New Issue