437 lines
12 KiB
Vue
Raw Normal View History

<template>
2024-04-09 17:57:51 +08:00
<el-container>
<el-header height="50px">
<div class="header-title">社团展示系统</div>
2024-06-05 18:02:15 +08:00
<nav class="header-content">
<template v-if="userStore.isInitialized">
<router-link v-slot="{ navigate }" custom to="/2048">
<div class="nav-img-items game-2048" @click="navigate">
<img alt="2048" draggable="false" src="@/assets/2048.png" />
</div>
</router-link>
<template v-if="userStore.userInfo !== null">
<div class="username">
{{ userStore.userInfo.name }}
</div>
<el-dropdown ref="dropdownRef">
<el-avatar :icon="userStore.userInfo.avatar || Avatar" :size="40"></el-avatar>
<template #dropdown>
<el-dropdown-menu>
<router-link v-slot="{ navigate }" custom to="/user">
<el-dropdown-item :icon="UserFilled" @click="navigate()">
个人主页
</el-dropdown-item>
</router-link>
<el-dropdown-item :icon="CloseBold" @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<el-avatar
v-else
:size="40"
style="color: black; user-select: none; cursor: pointer"
@click="showLoginRegisterDialog = true"
>
登录
</el-avatar>
</template>
<loading-icon v-else />
2024-06-05 18:02:15 +08:00
</nav>
2024-04-09 17:57:51 +08:00
</el-header>
<router-view />
2024-04-09 17:57:51 +08:00
</el-container>
2024-04-14 14:40:34 +08:00
<el-dialog
v-model="showLoginRegisterDialog"
:align-center="true"
class="login-dialog"
width="400"
2024-04-14 14:40:34 +08:00
>
2024-04-10 17:51:13 +08:00
<el-tabs v-model="loginRegisterDialogActiveName">
<el-tab-pane label="登录" name="login">
<el-form ref="loginFormRef" :model="loginFormData" :rules="loginFormRules" @submit.prevent>
2024-04-10 17:51:13 +08:00
<el-form-item prop="username">
2024-04-14 14:40:34 +08:00
<el-input
v-model="loginFormData.username"
:disabled="logining"
placeholder="请输入用户名"
2024-04-14 14:40:34 +08:00
>
2024-04-10 17:51:13 +08:00
<template #prepend>
<el-icon>
<icon-ep-user-filled />
</el-icon>
2024-04-10 17:51:13 +08:00
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
2024-04-14 14:40:34 +08:00
<el-input
v-model="loginFormData.password"
:disabled="logining"
placeholder="请输入密码"
type="password"
2024-04-14 14:40:34 +08:00
>
2024-04-10 17:51:13 +08:00
<template #prepend>
<el-icon>
<icon-cs-lock />
</el-icon>
2024-04-10 17:51:13 +08:00
</template>
</el-input>
</el-form-item>
2024-04-19 20:52:42 +08:00
<el-form-item prop="verifyCode">
<verify-input v-model="loginFormData" :disabled="logining" />
</el-form-item>
2024-04-18 18:02:43 +08:00
<el-form-item style="margin-bottom: 0">
<el-button
:loading="logining"
native-type="submit"
2024-04-18 18:02:43 +08:00
style="width: 100%"
type="primary"
2024-04-19 20:52:42 +08:00
@click="submitLoginForm"
2024-04-18 18:02:43 +08:00
>
登录
</el-button>
</el-form-item>
2024-04-10 17:51:13 +08:00
</el-form>
</el-tab-pane>
<el-tab-pane label="注册" name="register">
<el-form
ref="registerFormRef"
:model="registerFormData"
:rules="registerFormRules"
@submit.prevent
>
2024-04-10 17:51:13 +08:00
<el-form-item prop="username">
2024-04-14 14:40:34 +08:00
<el-input
v-model="registerFormData.username"
:disabled="registering"
placeholder="请输入注册用户名"
2024-04-14 14:40:34 +08:00
>
2024-04-10 17:51:13 +08:00
<template #prepend>
<el-icon>
<icon-cs-user />
</el-icon>
2024-04-10 17:51:13 +08:00
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
2024-04-14 14:40:34 +08:00
<el-input
v-model="registerFormData.password"
:disabled="registering"
placeholder="请输入密码"
type="password"
2024-04-14 14:40:34 +08:00
>
2024-04-10 17:51:13 +08:00
<template #prepend>
<el-icon>
<icon-cs-lock />
</el-icon>
2024-04-10 17:51:13 +08:00
</template>
</el-input>
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input
2024-04-14 14:40:34 +08:00
v-model="registerFormData.confirmPassword"
:disabled="registering"
placeholder="请确认密码"
type="password"
2024-04-10 17:51:13 +08:00
>
<template #prepend>
<el-icon>
<icon-cs-lock />
</el-icon>
2024-04-10 17:51:13 +08:00
</template>
</el-input>
</el-form-item>
2024-04-19 20:52:42 +08:00
<el-form-item prop="verifyCode">
<verify-input v-model="registerFormData" :disabled="registering" />
</el-form-item>
2024-04-10 17:51:13 +08:00
<el-form-item style="margin-bottom: 0">
2024-04-14 14:40:34 +08:00
<el-button
:loading="registering"
native-type="submit"
2024-04-14 14:40:34 +08:00
style="width: 100%"
type="primary"
2024-04-14 14:40:34 +08:00
@click="submitRegisterForm"
>
注册
</el-button>
2024-04-10 17:51:13 +08:00
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<style lang="scss" scoped>
2024-04-09 17:57:51 +08:00
.el-header {
--el-header-padding: var(--page-content-padding);
position: relative;
2024-04-09 17:57:51 +08:00
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--el-border-color);
box-shadow: 0 1px 5px rgba(black, 0.1);
background-color: white;
z-index: 100;
2024-04-09 17:57:51 +08:00
}
.el-container {
min-height: 100vh;
}
.el-main {
--el-main-padding: var(--page-content-padding);
height: 100%;
}
.header-content {
2024-04-14 14:40:34 +08:00
display: flex;
align-items: center;
}
2024-04-18 17:57:35 +08:00
2024-06-05 18:02:15 +08:00
.header-content > * {
margin-right: 10px;
}
.header-content > *:last-child {
margin-right: 0;
}
.nav-img-items {
display: flex;
justify-content: center;
align-items: center;
}
.nav-img-items img {
width: 40px;
height: 40px;
}
.game-2048 {
margin-right: 20px;
}
.header-title {
2024-04-09 17:57:51 +08:00
font-size: 1.2em;
}
2024-04-14 14:40:34 +08:00
.username {
margin-right: 10px;
}
2024-04-09 17:57:51 +08:00
</style>
<script lang="ts" setup>
2024-04-14 14:40:34 +08:00
import axiosInstance from '@/api';
2024-04-18 18:02:43 +08:00
import { type VerifyImagePath } from '@/components/VerifyInput.vue';
import { loginResponseSchema, registerResponseSchema } from '@/schemas';
2024-04-14 14:40:34 +08:00
import { useUserStore } from '@/stores';
2024-04-18 17:57:35 +08:00
import { errorMessage } from '@/utils';
import { Avatar, CloseBold, UserFilled } from '@element-plus/icons-vue';
2024-04-14 14:40:34 +08:00
import { AxiosError } from 'axios';
2024-04-18 17:57:35 +08:00
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
2024-04-19 20:52:42 +08:00
import { partial } from 'lodash-es';
import { reactive, ref, watch } from 'vue';
2024-04-19 20:52:42 +08:00
import { RouterView, useRouter } from 'vue-router';
const router = useRouter();
2024-04-14 14:40:34 +08:00
const userStore = useUserStore();
watch(
() => userStore.isInitialized,
(value) => {
if (value && userStore.token === null) {
showLoginRegisterDialog.value = true;
}
}
);
2024-04-14 14:40:34 +08:00
const showLoginRegisterDialog = ref(false);
watch(showLoginRegisterDialog, (value) => {
if (value) {
logining.value = false;
2024-04-19 20:52:42 +08:00
loginFormData.verifyImage = 'none';
2024-04-14 14:40:34 +08:00
loginFormRef.value?.resetFields();
registering.value = false;
2024-04-19 20:52:42 +08:00
registerFormData.verifyImage = 'none';
2024-04-14 14:40:34 +08:00
registerFormRef.value?.resetFields();
}
});
2024-04-10 17:51:13 +08:00
const loginRegisterDialogActiveName = ref('login');
const loginFormRef = ref<FormInstance>();
const loginFormData = reactive({
2024-04-14 14:40:34 +08:00
username: 'wubaopu2',
2024-04-18 17:57:35 +08:00
password: '123456',
verifyImage: 'none' as VerifyImagePath,
2024-04-19 20:52:42 +08:00
verifyCode: ''
2024-04-10 17:51:13 +08:00
});
const loginFormRules = reactive<FormRules<typeof loginFormData>>({
2024-04-14 14:40:34 +08:00
username: [
2024-04-19 20:52:42 +08:00
{ required: true, message: '请输入用户名' },
{ min: 6, message: '用户名长度不能小于3位' }
2024-04-14 14:40:34 +08:00
],
password: [
2024-04-19 20:52:42 +08:00
{ required: true, message: '请输入密码' },
{ min: 6, message: '密码长度不能小于6位' }
],
verifyCode: [
{ required: true, message: '请输入验证码' },
{ pattern: /[0-9A-Za-z]{4}/, message: '验证码不符合格式' }
2024-04-14 14:40:34 +08:00
]
2024-04-10 17:51:13 +08:00
});
2024-04-14 14:40:34 +08:00
const logining = ref(false);
2024-04-10 17:51:13 +08:00
const registerFormRef = ref<FormInstance>();
const registerFormData = reactive({
username: '',
password: '',
2024-04-18 17:57:35 +08:00
confirmPassword: '',
verifyImage: 'none' as VerifyImagePath,
2024-04-19 20:52:42 +08:00
verifyCode: ''
2024-04-10 17:51:13 +08:00
});
const registerFormRules = reactive<FormRules<typeof registerFormData>>({
2024-04-14 14:40:34 +08:00
...loginFormRules,
2024-04-10 17:58:10 +08:00
confirmPassword: [
{
validator(_, value, callback) {
2024-04-14 14:40:34 +08:00
if (value === '') {
callback('请输入密码');
} else if (value !== registerFormData.password) {
callback('密码不一致');
} else if (value.length < 6) {
callback('密码长度不能小于6位');
2024-04-10 17:58:10 +08:00
} else {
callback();
}
}
}
]
2024-04-10 17:51:13 +08:00
});
2024-04-14 14:40:34 +08:00
const registering = ref(false);
const loginErrorMessage = partial(errorMessage, '登录');
2024-04-14 14:40:34 +08:00
interface LoginParams {
username: string;
password: string;
2024-04-18 18:02:43 +08:00
key: string;
code: string;
2024-04-14 14:40:34 +08:00
}
2024-04-18 17:57:35 +08:00
2024-04-18 18:02:43 +08:00
async function login(params: LoginParams) {
2024-04-14 14:40:34 +08:00
try {
const loginResp = loginResponseSchema.parse(
(await axiosInstance.post('/api/user/login', params)).data
);
if (loginResp.type === 'error') {
ElMessage.error(loginErrorMessage(loginResp.code, loginResp.msg));
return false;
}
return true;
} catch (e) {
if (e instanceof AxiosError) {
ElMessage.error(loginErrorMessage(e.code, e.message));
2024-04-18 17:57:35 +08:00
} else {
ElMessage.error('error');
2024-04-14 14:40:34 +08:00
}
2024-04-19 20:52:42 +08:00
console.log(e);
2024-04-14 14:40:34 +08:00
return false;
}
}
2024-04-14 14:40:34 +08:00
async function submitLoginForm() {
let succeed = false;
logining.value = true;
try {
try {
await loginFormRef.value?.validate();
} catch (e) {
return;
}
2024-04-19 20:52:42 +08:00
if (typeof loginFormData.verifyImage === 'string') {
ElMessage.error('请获取验证码');
return;
}
const {
username,
password,
verifyImage: { key },
verifyCode: code
} = loginFormData;
console.log(loginFormData);
2024-04-19 20:52:42 +08:00
succeed = await login({
username,
password,
key,
code
});
2024-04-14 14:40:34 +08:00
} finally {
if (succeed) {
window.location.reload();
} else {
loginFormData.verifyImage = 'none';
loginFormRef.value?.resetFields('verifyCode');
2024-04-14 14:40:34 +08:00
logining.value = false;
}
}
}
2024-04-14 14:40:34 +08:00
const registerErrorMessage = partial(errorMessage, '注册');
2024-04-19 20:52:42 +08:00
interface RegisterParams extends LoginParams {
auth: number;
}
2024-04-19 20:52:42 +08:00
async function register(params: RegisterParams) {
2024-04-14 14:40:34 +08:00
try {
const registerResp = registerResponseSchema.parse(
2024-04-19 20:52:42 +08:00
(await axiosInstance.put('/api/user/create', params)).data
2024-04-14 14:40:34 +08:00
);
if (registerResp.type === 'error') {
ElMessage.error({
message: registerErrorMessage(registerResp.code, registerResp.msg)
});
return false;
}
return true;
} catch (e) {
if (e instanceof AxiosError) {
ElMessage.error(registerErrorMessage(e.code, e.message));
2024-04-18 17:57:35 +08:00
} else {
ElMessage.error('error');
2024-04-14 14:40:34 +08:00
}
2024-04-19 20:52:42 +08:00
console.log(e);
2024-04-14 14:40:34 +08:00
return false;
}
}
2024-04-14 14:40:34 +08:00
async function submitRegisterForm() {
let succeed = false;
registering.value = true;
2024-04-10 17:51:13 +08:00
try {
2024-04-14 14:40:34 +08:00
try {
await registerFormRef.value?.validate();
} catch {
return;
}
2024-04-19 20:52:42 +08:00
if (typeof registerFormData.verifyImage === 'string') {
ElMessage.error('请输入验证码');
return;
}
const {
username,
password,
verifyImage: { key },
verifyCode: code
} = registerFormData;
succeed = await register({ username, password, key, code, auth: 1 });
2024-04-14 14:40:34 +08:00
} finally {
if (succeed) {
window.location.reload();
} else {
registerFormData.verifyImage = 'none';
registerFormRef.value?.resetFields('verifyCode');
2024-04-14 14:40:34 +08:00
registering.value = false;
}
2024-04-10 17:51:13 +08:00
}
}
2024-04-14 14:40:34 +08:00
async function logout() {
userStore.$reset();
window.location.reload();
}
</script>