From ba1311ae59a712ebba03625e3f1ad49513153a0a Mon Sep 17 00:00:00 2001 From: xc Date: Fri, 14 Nov 2025 19:31:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nprogress.less => nprogress.css} | 12 +- vue/afvue/src/main.js | 2 + vue/afvue/src/permission.js | 32 ++++ vue/afvue/src/router/index.js | 2 +- vue/afvue/src/stores/auth.js | 171 ++++++++++++++++++ vue/afvue/src/stores/user.js | 31 ++++ vue/afvue/src/views/Login.vue | 10 + 7 files changed, 253 insertions(+), 7 deletions(-) rename vue/afvue/src/components/{NProgress/nprogress.less => nprogress.css} (82%) create mode 100644 vue/afvue/src/permission.js create mode 100644 vue/afvue/src/stores/auth.js create mode 100644 vue/afvue/src/stores/user.js diff --git a/vue/afvue/src/components/NProgress/nprogress.less b/vue/afvue/src/components/nprogress.css similarity index 82% rename from vue/afvue/src/components/NProgress/nprogress.less rename to vue/afvue/src/components/nprogress.css index 7826c0e..5138ab5 100644 --- a/vue/afvue/src/components/NProgress/nprogress.less +++ b/vue/afvue/src/components/nprogress.css @@ -1,4 +1,3 @@ -@import url('../index.less'); /* Make clicks pass-through */ #nprogress { @@ -6,8 +5,8 @@ } #nprogress .bar { - background: @primary-color; - + /* background: @primary-color; */ + background-color: rgb(224, 89, 82); position: fixed; z-index: 1031; top: 0; @@ -24,7 +23,8 @@ right: 0px; width: 100px; height: 100%; - box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; + /* box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; */ + box-shadow: 0 0 10px rgb(224, 89, 82), 0 0 5px rgb(220, 215, 215); opacity: 1.0; -webkit-transform: rotate(3deg) translate(0px, -4px); @@ -47,8 +47,8 @@ box-sizing: border-box; border: solid 2px transparent; - border-top-color: @primary-color; - border-left-color: @primary-color; + border-top-color: rgb(224, 89, 82); + border-left-color: rgb(224, 89, 82); border-radius: 50%; -webkit-animation: nprogress-spinner 400ms linear infinite; diff --git a/vue/afvue/src/main.js b/vue/afvue/src/main.js index 65bf147..04e96a1 100644 --- a/vue/afvue/src/main.js +++ b/vue/afvue/src/main.js @@ -3,6 +3,8 @@ import { createPinia, PiniaVuePlugin } from 'pinia' import { VueAxios } from './utils/request' import App from './App.vue' import router from './router' +// 导入权限控制模块 +import './permission' import './assets/main.css' diff --git a/vue/afvue/src/permission.js b/vue/afvue/src/permission.js new file mode 100644 index 0000000..6841b42 --- /dev/null +++ b/vue/afvue/src/permission.js @@ -0,0 +1,32 @@ +import router from './router' +import { userStStore } from './stores/user' +import NProgress from 'nprogress' // progress bar +import '@/components/nprogress.css' // progress bar style + +NProgress.configure({ showSpinner: false }) // NProgress Configuration + +// 登录页面 +const loginRoutePath = '/login' +// 白名单 +const whiteList = [loginRoutePath, '/404', '/401'] + + + + +// 路由守卫 +// router.beforeEach((to, from, next) => { +// NProgress.start() // start progress bar + +// // 获取用户store实例 +// const userStore = userStStore() +// console.log(userStore.token,"user token") + +// // 检查token +// console.log("check token") + +// }) + +// 路由守卫 +router.afterEach((to, from) => { + NProgress.done() // finish progress bar +}) \ No newline at end of file diff --git a/vue/afvue/src/router/index.js b/vue/afvue/src/router/index.js index 2bd47e9..b7bca50 100644 --- a/vue/afvue/src/router/index.js +++ b/vue/afvue/src/router/index.js @@ -68,4 +68,4 @@ const router = new VueRouter({ // } // }) -export default router +export default router \ No newline at end of file diff --git a/vue/afvue/src/stores/auth.js b/vue/afvue/src/stores/auth.js new file mode 100644 index 0000000..3ebf0f8 --- /dev/null +++ b/vue/afvue/src/stores/auth.js @@ -0,0 +1,171 @@ +import { defineStore } from 'pinia' +import { api } from '@/api/axios' // 你的 API 配置 + +export const useAuthStore = defineStore('auth', { + state: () => ({ + // 用户信息 + user: null, + // 令牌 + token: localStorage.getItem('token') || null, + // 登录状态 + isLoggedIn: !!localStorage.getItem('token'), + // 加载状态 + isLoading: false, + // 错误信息 + error: null + }), + + getters: { + // 用户基本信息 + userInfo: (state) => state.user, + // 用户名 + userName: (state) => state.user?.username || state.user?.name || '用户', + // 用户角色 + userRole: (state) => state.user?.role || '', + // 是否有特定权限 + hasPermission: (state) => (permission) => { + return state.user?.permissions?.includes(permission) || false + }, + // 是否是管理员 + isAdmin: (state) => state.user?.role === 'admin' + }, + + actions: { + /** + * 用户登录 + */ + async login(credentials) { + this.isLoading = true + this.error = null + + try { + const response = await api.post('/auth/login', credentials) + const { user, token } = response.data + + // 保存用户信息和令牌 + this.user = user + this.token = token + this.isLoggedIn = true + + // 保存到 localStorage + localStorage.setItem('token', token) + localStorage.setItem('user', JSON.stringify(user)) + + // 设置 axios 默认授权头 + api.defaults.headers.common['Authorization'] = `Bearer ${token}` + + this.isLoading = false + return { success: true, user } + } catch (error) { + this.isLoading = false + this.error = error.response?.data?.message || '登录失败' + return { success: false, error: this.error } + } + }, + + /** + * 用户注册 + */ + async register(userData) { + this.isLoading = true + this.error = null + + try { + const response = await api.post('/auth/register', userData) + const { user, token } = response.data + + // 自动登录 + this.user = user + this.token = token + this.isLoggedIn = true + + localStorage.setItem('token', token) + localStorage.setItem('user', JSON.stringify(user)) + api.defaults.headers.common['Authorization'] = `Bearer ${token}` + + this.isLoading = false + return { success: true, user } + } catch (error) { + this.isLoading = false + this.error = error.response?.data?.message || '注册失败' + return { success: false, error: this.error } + } + }, + + /** + * 用户退出登录 + */ + logout() { + this.user = null + this.token = null + this.isLoggedIn = false + this.error = null + + // 清除 localStorage + localStorage.removeItem('token') + localStorage.removeItem('user') + + // 移除 axios 授权头 + delete api.defaults.headers.common['Authorization'] + + // 可以在这里跳转到登录页 + if (this.router) { + this.router.push('/login') + } + }, + + /** + * 检查登录状态(页面刷新时调用) + */ + async checkAuth() { + const token = localStorage.getItem('token') + const userData = localStorage.getItem('user') + + if (token && userData) { + try { + // 验证 token 是否有效 + const response = await api.get('/auth/me') + this.user = response.data.user + this.token = token + this.isLoggedIn = true + api.defaults.headers.common['Authorization'] = `Bearer ${token}` + } catch (error) { + // token 无效,清除状态 + this.logout() + } + } + }, + + /** + * 更新用户信息 + */ + async updateUser(userData) { + try { + const response = await api.put('/auth/profile', userData) + this.user = { ...this.user, ...response.data.user } + + // 更新 localStorage + localStorage.setItem('user', JSON.stringify(this.user)) + + return { success: true, user: this.user } + } catch (error) { + this.error = error.response?.data?.message || '更新失败' + return { success: false, error: this.error } + } + }, + + /** + * 清除错误信息 + */ + clearError() { + this.error = null + }, + + /** + * 设置路由实例(用于退出登录后跳转) + */ + setRouter(router) { + this.router = router + } + } +}) \ No newline at end of file diff --git a/vue/afvue/src/stores/user.js b/vue/afvue/src/stores/user.js new file mode 100644 index 0000000..d0960f0 --- /dev/null +++ b/vue/afvue/src/stores/user.js @@ -0,0 +1,31 @@ +import { defineStore } from 'pinia'; + +export const userStStore = defineStore('user', { + state: () => { + return { + token: null, // 存储用户token + user: null, // 存储用户对象 + isLoggedIn: false // 用户登录状态 + }; + }, + getters: { + getUser: (state) => state.user, + getIsLoggedIn: (state) => state.isLoggedIn, + token: (state) => state.token + }, + actions: { + setUser(user) { + this.user = user; + this.isLoggedIn = true; + }, + logout() { + this.token = null + this.user = null; + this.isLoggedIn = false; + }, + setToken(token) { + // this.token = token; + console.log("set token") + } + }, +}); diff --git a/vue/afvue/src/views/Login.vue b/vue/afvue/src/views/Login.vue index 9a9a16f..67f9a35 100644 --- a/vue/afvue/src/views/Login.vue +++ b/vue/afvue/src/views/Login.vue @@ -22,6 +22,7 @@ import { login } from '@/api/scinfo' import storage from 'store' import router from '@/router' +import { useUserStore } from '@/stores/user' export default { name: 'Login', @@ -53,6 +54,15 @@ export default { if (res && res.data && res.data.token) { // 保存token到localStorage storage.set('token', res.data.token) + + // 使用Pinia store管理登录状态 + const userStore = useUserStore() + userStore.setUser({ + username: this.username, + token: res.data.token, + ...(res.data.userInfo || {}) + }) + // 跳转到首页 router.push('/') } else {