增加登录功能
This commit is contained in:
parent
cd00fadea3
commit
4e9720de5c
3
frontend/components.d.ts
vendored
3
frontend/components.d.ts
vendored
@ -15,11 +15,14 @@ declare module 'vue' {
|
||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
|
||||
14
frontend/src/api/login.ts
Normal file
14
frontend/src/api/login.ts
Normal file
@ -0,0 +1,14 @@
|
||||
//进行接口API的统一管理
|
||||
import { request } from "./axios";
|
||||
|
||||
export class login {
|
||||
/**
|
||||
* @description 获取article文章详情
|
||||
* @param {string} account - 账号
|
||||
* @param {string} password - 密码
|
||||
* @return {Promise} 返回请求结果
|
||||
*/
|
||||
static async goLogin(account: string, password: string) {
|
||||
return request("/index/user/login", { account,password }, "post");
|
||||
}
|
||||
}
|
||||
@ -52,6 +52,11 @@ const router = createRouter({
|
||||
name: "search",
|
||||
component: () => import("@/views/components/search.vue"),
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
component: () => import("@/views/login/index.vue"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import { ref, nextTick, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { Search, User, Setting, SwitchButton } from "@element-plus/icons-vue";
|
||||
import { ElMessage, ElDialog } from "element-plus";
|
||||
@ -9,9 +9,36 @@ const showSearchDialog = ref(false);
|
||||
const searchText = ref("");
|
||||
const searchType = ref("articles");
|
||||
const searchInput = ref();
|
||||
const username = ref("管理员"); // 这里可以从 store 获取
|
||||
const isLoggedIn = ref(false); // 登录状态,默认为false
|
||||
const username = ref(""); // 用户名,从本地存储获取
|
||||
const router = useRouter();
|
||||
|
||||
// 初始化时检查登录状态
|
||||
const initLoginStatus = () => {
|
||||
const token = localStorage.getItem('token');
|
||||
const userStr = localStorage.getItem('user');
|
||||
const isLoggedInFlag = localStorage.getItem('isLoggedIn');
|
||||
|
||||
if (token && userStr && isLoggedInFlag === 'true') {
|
||||
try {
|
||||
const userData = JSON.parse(userStr);
|
||||
username.value = userData.user_name || userData.user_account || '用户';
|
||||
isLoggedIn.value = true;
|
||||
} catch (error) {
|
||||
console.error('解析用户信息失败:', error);
|
||||
// 清除无效数据
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
localStorage.removeItem('isLoggedIn');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 在组件挂载时初始化登录状态
|
||||
onMounted(() => {
|
||||
initLoginStatus();
|
||||
});
|
||||
|
||||
// 搜索类型选项
|
||||
const searchTypeOptions = [
|
||||
{ value: "articles", label: "文章" },
|
||||
@ -78,11 +105,33 @@ const handleCommand = (command: string) => {
|
||||
break;
|
||||
case "logout":
|
||||
console.log("退出登录");
|
||||
// TODO: 实现退出登录逻辑
|
||||
ElMessage.success("退出登录成功");
|
||||
// 执行退出登录逻辑
|
||||
logout();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 退出登录函数
|
||||
const logout = () => {
|
||||
// 清除登录状态
|
||||
isLoggedIn.value = false;
|
||||
|
||||
// 清除本地存储的所有登录相关信息
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
localStorage.removeItem('isLoggedIn');
|
||||
|
||||
// 显示退出成功消息
|
||||
ElMessage.success("退出登录成功");
|
||||
|
||||
// 留在当前页面,显示未登录状态
|
||||
};
|
||||
|
||||
// 显示登录界面
|
||||
const showLogin = () => {
|
||||
// 跳转到登录页面
|
||||
router.push('/login');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -119,8 +168,8 @@ const handleCommand = (command: string) => {
|
||||
<el-icon><Search /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div class="user-info">
|
||||
<!-- 用户信息或登录按钮 -->
|
||||
<div v-if="isLoggedIn" class="user-info">
|
||||
<span class="username">{{ username }}</span>
|
||||
<el-dropdown @command="handleCommand" class="user-dropdown">
|
||||
<el-avatar :size="32" class="user-avatar">
|
||||
@ -144,6 +193,11 @@ const handleCommand = (command: string) => {
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div v-else class="login-section">
|
||||
<el-button type="primary" size="small" @click="showLogin">
|
||||
登录
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -303,6 +357,16 @@ const handleCommand = (command: string) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-section {
|
||||
flex-shrink: 0;
|
||||
|
||||
.el-button {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,6 +398,14 @@ const handleCommand = (command: string) => {
|
||||
.username {
|
||||
display: none; // 小屏幕隐藏用户名
|
||||
}
|
||||
|
||||
.login-section {
|
||||
.el-button {
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
299
frontend/src/views/login/index.vue
Normal file
299
frontend/src/views/login/index.vue
Normal file
@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<div class="login-bg">
|
||||
<div class="login-container">
|
||||
<div class="login-left">
|
||||
<img
|
||||
src="@/assets/imgs/logo-light.png"
|
||||
alt="Yunzer Logo"
|
||||
class="login-logo"
|
||||
/>
|
||||
<div class="login-welcome">
|
||||
<h2>欢迎来到云享社区</h2>
|
||||
<p>集学习、成长与分享于一体的平台</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form-area">
|
||||
<h3 class="login-title">用户登录</h3>
|
||||
<el-form
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
ref="loginForm"
|
||||
class="login-form"
|
||||
@submit.native.prevent="onLogin"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
size="large"
|
||||
placeholder="账号/邮箱"
|
||||
prefix-icon="el-icon-user"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
size="large"
|
||||
placeholder="登录密码"
|
||||
prefix-icon="el-icon-lock"
|
||||
show-password
|
||||
clearable
|
||||
@keyup.enter="onLogin"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="login-actions">
|
||||
<el-checkbox v-model="form.remember">记住我</el-checkbox>
|
||||
<a class="link" @click.prevent="toForgot">忘记密码?</a>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="login-btn"
|
||||
size="large"
|
||||
:loading="loading"
|
||||
@click="onLogin"
|
||||
block
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
没有账号?
|
||||
<a class="link" @click.prevent="toRegister">立即注册</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-copyright">© 2025 Yunzer | 苏ICP备2023006641号</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { login } from "@/api/login";
|
||||
|
||||
// 登录表单状态
|
||||
const router = useRouter();
|
||||
const form = ref({
|
||||
username: "",
|
||||
password: "",
|
||||
remember: true,
|
||||
});
|
||||
const loading = ref(false);
|
||||
const loginForm = ref();
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: "请输入账号/邮箱", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入登录密码", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const onLogin = () => {
|
||||
loginForm.value.validate(async (valid: boolean) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await login.goLogin(form.value.username, form.value.password);
|
||||
|
||||
if (response.data.code === 0) {
|
||||
// 登录成功
|
||||
ElMessage.success(response.data.msg || "登录成功,欢迎回来!");
|
||||
|
||||
// 保存用户信息到本地存储
|
||||
if (response.data.data) {
|
||||
// 保存用户完整信息
|
||||
localStorage.setItem('user', JSON.stringify(response.data.data));
|
||||
|
||||
// 如果有user_id,可以作为token使用,或者生成一个会话标识
|
||||
if (response.data.data.user_id) {
|
||||
localStorage.setItem('token', response.data.data.user_id.toString());
|
||||
} else {
|
||||
// 如果没有user_id,使用时间戳作为临时token
|
||||
localStorage.setItem('token', Date.now().toString());
|
||||
}
|
||||
|
||||
// 保存登录状态
|
||||
localStorage.setItem('isLoggedIn', 'true');
|
||||
}
|
||||
|
||||
router.push("/");
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || "登录失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("登录失败:", error);
|
||||
ElMessage.error("登录失败,请检查网络连接");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function toForgot() {
|
||||
ElMessage.info("请联系管理员重置密码");
|
||||
}
|
||||
|
||||
function toRegister() {
|
||||
router.push("/register");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-bg {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(120deg, #5bbefa 0%, #786de0 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
.login-container {
|
||||
background: #fff;
|
||||
min-width: 380px;
|
||||
max-width: 920px;
|
||||
width: 90vw;
|
||||
min-height: 450px;
|
||||
display: flex;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 12px 36px 2px rgba(103, 117, 214, 0.13),
|
||||
0 2px 4px rgba(64, 158, 255, 0.08);
|
||||
margin: 0 auto;
|
||||
}
|
||||
.login-left {
|
||||
flex: 1.05;
|
||||
background: linear-gradient(136deg, #67cee8 0%, #5347c9 100%);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-logo {
|
||||
width: 90px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.login-welcome h2 {
|
||||
font-weight: 700;
|
||||
font-size: 26px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.login-welcome p {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
opacity: 0.86;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.login-form-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 54px 38px 36px 38px;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
}
|
||||
.login-title {
|
||||
font-weight: 600;
|
||||
font-size: 23px;
|
||||
margin-bottom: 32px;
|
||||
color: #303133;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
}
|
||||
.login-form :deep(.el-input__wrapper) {
|
||||
background: #f6f9ff;
|
||||
border: none;
|
||||
border-radius: 9px;
|
||||
}
|
||||
.login-form :deep(.el-input__inner) {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
.login-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
background-image: linear-gradient(88deg, #67cee8 0%, #5347c9 100%);
|
||||
border: none;
|
||||
box-shadow: 0 2px 10px rgba(76, 130, 255, 0.07);
|
||||
transition: background 0.19s;
|
||||
}
|
||||
.login-btn:hover {
|
||||
background-image: linear-gradient(88deg, #5bbefa 0%, #786de0 100%);
|
||||
}
|
||||
.link {
|
||||
color: #7364ec;
|
||||
cursor: pointer;
|
||||
margin-left: 6px;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
transition: color 0.19s;
|
||||
}
|
||||
.link:hover {
|
||||
color: #6daff7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.login-footer {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #aaa;
|
||||
}
|
||||
.login-footer .link {
|
||||
font-weight: 600;
|
||||
}
|
||||
.login-copyright {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
color: #e6f6ff;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.5px;
|
||||
opacity: 0.88;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 750px) {
|
||||
.login-container {
|
||||
flex-direction: column;
|
||||
min-width: 280px;
|
||||
max-width: 99vw;
|
||||
width: 99vw;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.login-left {
|
||||
min-height: 135px;
|
||||
padding: 28px 0;
|
||||
border-radius: 12px 12px 0 0;
|
||||
flex: none;
|
||||
}
|
||||
.login-form-area {
|
||||
padding: 32px 12vw 32px 12vw;
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.login-form-area {
|
||||
padding: 24px 8vw 20px 8vw;
|
||||
}
|
||||
.login-container {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user