实现登录
This commit is contained in:
@@ -1749,7 +1749,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 6,
|
|
||||||
"example": "password123"
|
"example": "password123"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
|
|||||||
19
src/App.vue
19
src/App.vue
@@ -1,14 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<MainLayout />
|
<div id="app">
|
||||||
|
<template v-if="isLoginPage">
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<MainLayout />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import MainLayout from './layouts/MainLayout.vue';
|
import MainLayout from './layouts/MainLayout.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
MainLayout
|
MainLayout
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const route = useRoute();
|
||||||
|
const isLoginPage = computed(() => route.path === '/login');
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoginPage
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
117
src/components/LoginForm.vue
Normal file
117
src/components/LoginForm.vue
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login-container">
|
||||||
|
<el-card class="login-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>系统登录</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
ref="loginFormRef"
|
||||||
|
:model="loginForm"
|
||||||
|
:rules="loginRules"
|
||||||
|
label-width="80px"
|
||||||
|
class="login-form"
|
||||||
|
@keyup.enter="handleLogin"
|
||||||
|
>
|
||||||
|
<el-form-item label="用户名" prop="identifier">
|
||||||
|
<el-input v-model="loginForm.identifier" placeholder="请输入用户名/邮箱/手机号"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码" prop="password">
|
||||||
|
<el-input type="password" v-model="loginForm.password" placeholder="请输入密码" show-password></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleLogin" :loading="loading" style="width: 100%;">登录</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import apiClient from '../api/index.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoginForm',
|
||||||
|
setup() {
|
||||||
|
const router = useRouter();
|
||||||
|
const loginFormRef = ref(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const loginForm = reactive({
|
||||||
|
identifier: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginRules = reactive({
|
||||||
|
identifier: [
|
||||||
|
{ required: true, message: '请输入用户名/邮箱/手机号', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
if (!loginFormRef.value) return;
|
||||||
|
loginFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const response = await apiClient.users.login(loginForm);
|
||||||
|
if (response.code === 2000 && response.data && response.data.token) {
|
||||||
|
localStorage.setItem('jwt_token', response.data.token);
|
||||||
|
localStorage.setItem('username', response.data.username); // 存储用户名
|
||||||
|
ElMessage.success('登录成功!');
|
||||||
|
router.push('/'); // 登录成功后跳转到首页
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.message || '登录失败,请检查用户名或密码!');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录请求失败:', error);
|
||||||
|
ElMessage.error('登录请求失败,请稍后再试!');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loginFormRef,
|
||||||
|
loginForm,
|
||||||
|
loginRules,
|
||||||
|
loading,
|
||||||
|
handleLogin,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
width: 400px;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
管理员 <el-icon><ArrowDown /></el-icon>
|
{{ username }} <el-icon><ArrowDown /></el-icon>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item>个人信息</el-dropdown-item>
|
<el-dropdown-item>个人信息</el-dropdown-item>
|
||||||
<el-dropdown-item>退出登录</el-dropdown-item>
|
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
@@ -72,8 +72,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { House, Monitor, Calendar, ArrowDown, Menu, Fold, Expand } from '@element-plus/icons-vue';
|
import { House, Monitor, Calendar, ArrowDown, Menu, Fold, Expand } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -89,19 +89,31 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const isCollapse = ref(false);
|
const isCollapse = ref(false);
|
||||||
|
const username = ref(localStorage.getItem('username') || '管理员');
|
||||||
|
|
||||||
|
// 监听localStorage变化,实时更新用户名
|
||||||
|
const handleStorageChange = () => {
|
||||||
|
username.value = localStorage.getItem('username') || '管理员';
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
});
|
||||||
|
|
||||||
// 切换侧边栏折叠状态
|
|
||||||
const toggleCollapse = () => {
|
const toggleCollapse = () => {
|
||||||
isCollapse.value = !isCollapse.value;
|
isCollapse.value = !isCollapse.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 当前激活的菜单项
|
|
||||||
const activeMenu = computed(() => {
|
const activeMenu = computed(() => {
|
||||||
return route.path;
|
return route.path;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当前页面标题
|
|
||||||
const currentPageTitle = computed(() => {
|
const currentPageTitle = computed(() => {
|
||||||
const routeMap = {
|
const routeMap = {
|
||||||
'/': '系统首页',
|
'/': '系统首页',
|
||||||
@@ -111,11 +123,20 @@ export default {
|
|||||||
return routeMap[route.path] || '猪场管理系统';
|
return routeMap[route.path] || '猪场管理系统';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
localStorage.removeItem('jwt_token');
|
||||||
|
localStorage.removeItem('username'); // 清除用户名
|
||||||
|
username.value = '管理员'; // 重置显示
|
||||||
|
router.push('/login');
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isCollapse,
|
isCollapse,
|
||||||
activeMenu,
|
activeMenu,
|
||||||
currentPageTitle,
|
currentPageTitle,
|
||||||
toggleCollapse
|
toggleCollapse,
|
||||||
|
logout,
|
||||||
|
username
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
29
src/main.js
29
src/main.js
@@ -7,15 +7,17 @@ import App from './App.vue';
|
|||||||
import Home from './components/Home.vue';
|
import Home from './components/Home.vue';
|
||||||
import DeviceList from './components/DeviceList.vue';
|
import DeviceList from './components/DeviceList.vue';
|
||||||
import PlanList from './components/PlanList.vue';
|
import PlanList from './components/PlanList.vue';
|
||||||
|
import LoginForm from './components/LoginForm.vue'; // 导入登录组件
|
||||||
|
|
||||||
// 导入全局样式
|
// 导入全局样式
|
||||||
import './assets/styles/main.css';
|
import './assets/styles/main.css';
|
||||||
|
|
||||||
// 配置路由
|
// 配置路由
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/', component: Home },
|
{ path: '/', component: Home, meta: { requiresAuth: true } },
|
||||||
{ path: '/devices', component: DeviceList },
|
{ path: '/devices', component: DeviceList, meta: { requiresAuth: true } },
|
||||||
{ path: '/plans', component: PlanList }
|
{ path: '/plans', component: PlanList, meta: { requiresAuth: true } },
|
||||||
|
{ path: '/login', component: LoginForm }
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
@@ -23,6 +25,21 @@ const router = createRouter({
|
|||||||
routes
|
routes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 全局路由守卫
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const loggedIn = localStorage.getItem('jwt_token');
|
||||||
|
|
||||||
|
if (to.matched.some(record => record.meta.requiresAuth) && !loggedIn) {
|
||||||
|
// 如果路由需要认证但用户未登录,则重定向到登录页
|
||||||
|
next('/login');
|
||||||
|
} else if (to.path === '/login' && loggedIn) {
|
||||||
|
// 如果用户已登录但试图访问登录页,则重定向到首页
|
||||||
|
next('/');
|
||||||
|
} else {
|
||||||
|
next(); // 正常放行
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 创建Vue应用实例
|
// 创建Vue应用实例
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
@@ -32,9 +49,5 @@ app.use(ElementPlus);
|
|||||||
// 使用路由
|
// 使用路由
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
// 初始化服务(示例)
|
|
||||||
// app.config.globalProperties.$api = ApiService;
|
|
||||||
// app.config.globalProperties.$utils = Utils;
|
|
||||||
|
|
||||||
// 挂载应用
|
// 挂载应用
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ const http = axios.create({
|
|||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
http.interceptors.request.use(
|
http.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
// 可以在这里添加认证token等
|
// 在这里添加认证token
|
||||||
// const token = localStorage.getItem('access_token');
|
const token = localStorage.getItem('jwt_token');
|
||||||
// if (token) {
|
if (token) {
|
||||||
// config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
// }
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
@@ -49,6 +49,14 @@ http.interceptors.response.use(
|
|||||||
if (error.response) {
|
if (error.response) {
|
||||||
// 服务器返回错误状态码
|
// 服务器返回错误状态码
|
||||||
console.error('API Error:', error.response.status, error.response.data);
|
console.error('API Error:', error.response.status, error.response.data);
|
||||||
|
// 如果是401未授权,可以考虑重定向到登录页
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
// 清除token并重定向到登录页
|
||||||
|
localStorage.removeItem('jwt_token');
|
||||||
|
// 这里需要访问router,但http.js是纯工具文件,不应直接依赖Vue Router实例
|
||||||
|
// 可以在main.js的全局错误处理或组件中处理401错误
|
||||||
|
// 例如:window.location.href = '/login';
|
||||||
|
}
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// 请求发出但没有收到响应
|
// 请求发出但没有收到响应
|
||||||
console.error('Network Error:', error.message);
|
console.error('Network Error:', error.message);
|
||||||
|
|||||||
Reference in New Issue
Block a user