diff --git a/rc_autoplc_front/src/directives/permission.ts b/rc_autoplc_front/src/directives/permission.ts new file mode 100644 index 0000000..09fefa4 --- /dev/null +++ b/rc_autoplc_front/src/directives/permission.ts @@ -0,0 +1,26 @@ +import type { App, DirectiveBinding } from 'vue' +import { useAuthStore } from '@/stores/auth' + +function hasPermission(value: unknown): boolean { + const authStore = useAuthStore() + const codes = Array.isArray(value) ? value : [value] + const normalized = codes.filter((code): code is string => typeof code === 'string' && code.length > 0) + + if (!normalized.length) return true + return authStore.hasAnyPermission(normalized) +} + +export function setupPermissionDirective(app: App) { + app.directive('permission', { + mounted(el: HTMLElement, binding: DirectiveBinding) { + if (!hasPermission(binding.value)) { + el.parentNode?.removeChild(el) + } + }, + updated(el: HTMLElement, binding: DirectiveBinding) { + if (!hasPermission(binding.value)) { + el.parentNode?.removeChild(el) + } + }, + }) +} diff --git a/rc_autoplc_front/src/main.ts b/rc_autoplc_front/src/main.ts index ce0adbf..bb25fdf 100644 --- a/rc_autoplc_front/src/main.ts +++ b/rc_autoplc_front/src/main.ts @@ -7,10 +7,10 @@ import router from './router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import zhCn from 'element-plus/es/locale/lang/zh-cn' +import { setupPermissionDirective } from '@/directives/permission' const app = createApp(App) -// 创建 Pinia 实例并配置持久化插件 const pinia = createPinia() pinia.use(piniaPluginPersistedstate) @@ -19,5 +19,6 @@ app.use(ElementPlus, { }) app.use(pinia) app.use(router) +setupPermissionDirective(app) app.mount('#app') diff --git a/rc_autoplc_front/src/router/index.ts b/rc_autoplc_front/src/router/index.ts index 2ebc19d..ad2ed4d 100644 --- a/rc_autoplc_front/src/router/index.ts +++ b/rc_autoplc_front/src/router/index.ts @@ -2,6 +2,14 @@ import { createRouter, createWebHashHistory } from 'vue-router' import { useAuthStore } from '@/stores/auth' import { ElMessage } from 'element-plus' +declare module 'vue-router' { + interface RouteMeta { + requiresAuth?: boolean + permission?: string | string[] + hidden?: boolean + } +} + const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL), routes: [ @@ -29,81 +37,77 @@ const router = createRouter({ path: '/user', name: 'user', component: () => import('../views/user/index.vue'), + meta: { permission: 'sys:user' }, }, { path: '/role', name: 'role', component: () => import('../views/role/index.vue'), + meta: { permission: 'sys:role' }, }, { path: '/department', name: 'department', component: () => import('../views/department/index.vue'), + meta: { permission: 'sys:department' }, }, { path: '/permission', name: 'permission', component: () => import('../views/permission/index.vue'), + meta: { permission: 'sys:permission' }, }, - { - path: '/position', - name: 'position', - component: () => import('../views/position/index.vue'), - }, + { path: '/manage-log', name: 'manage-log', component: () => import('../views/manage-log/index.vue'), + meta: { permission: 'sys:managelog' }, }, - { - path: '/user-role', - name: 'user-role', - component: () => import('../views/user-role/index.vue'), - }, + { path: '/island-info', name: 'island-info', component: () => import('../views/islandInfo/index.vue'), + meta: { permission: 'data:island' }, }, { path: '/devparam', name: 'devparam', component: () => import('../views/devparam/index.vue'), + meta: { permission: 'data:devparam' }, }, { path: '/devinfo', name: 'devinfo', component: () => import('../views/devinfo/index.vue'), + meta: { permission: 'data:dev' }, }, { path: '/plc-devinfo', name: 'plc-devinfo', component: () => import('../views/devinfo/plc.vue'), + meta: { permission: 'data:plc' }, }, { path: '/goods-info', name: 'goods-info', component: () => import('../views/goods/index.vue'), + meta: { permission: 'tb:goodrecord' }, }, - { - path: '/flow-info', - name: 'flow-info', - component: () => import('../views/flowinfo/index.vue'), - }, + { path: '/step-info', name: 'step-info', component: () => import('../views/stepinfo/index.vue'), + meta: { permission: 'data:sop' }, }, - { - path: '/plc-device-control', - name: 'plc-device-control', - component: () => import('../views/plcdevicecontrol/index.vue'), - }, + { path: '/sample-injection', name: 'sample-injection', component: () => import('../views/sampleinjection/index.vue'), + meta: { permission: 'tb:goodcontrol' }, }, ], }, @@ -115,33 +119,40 @@ router.beforeEach((to, _from, next) => { const authStore = useAuthStore() const token = authStore.getToken() - // 如果访问登录页 if (to.path === '/login') { - // 已登录状态下访问登录页,自动跳转到首页 if (token) { next('/home') ElMessage.success('已登录,自动跳转') } else { - // 未登录,正常进入登录页 next() } return } - // 访问需要认证的页面,但未登录(无token) if (to.meta.requiresAuth && !token) { ElMessage.warning('请先登录') next('/login') return } - // 如果访问根路径,重定向到首页 + const permission = to.meta.permission + if (permission) { + const allowed = Array.isArray(permission) + ? authStore.hasAnyPermission(permission) + : authStore.hasPermission(permission) + + if (!allowed) { + ElMessage.warning('暂无访问权限') + next('/home') + return + } + } + if (to.path === '/') { next('/home') return } - // 已登录且访问合法页面,正常放行 next() }) diff --git a/rc_autoplc_front/src/stores/auth.ts b/rc_autoplc_front/src/stores/auth.ts index c2d911f..e85e45e 100644 --- a/rc_autoplc_front/src/stores/auth.ts +++ b/rc_autoplc_front/src/stores/auth.ts @@ -1,8 +1,21 @@ -import { ref } from 'vue' +import { computed, ref } from 'vue' import { defineStore } from 'pinia' +export interface AuthUserInfo { + username?: string + token?: string + roles?: string[] + permissions?: string[] + [key: string]: any +} + +export type PermissionCode = string + export const useAuthStore = defineStore('auth', () => { const token = ref('') + const userInfo = ref(null) + + const permissions = computed(() => userInfo.value?.permissions || []) function setToken(newToken: string) { token.value = newToken @@ -12,15 +25,61 @@ export const useAuthStore = defineStore('auth', () => { return token.value } - function removeToken() { + function setUserInfo(info: AuthUserInfo | null) { + userInfo.value = info + if (info?.token) { + token.value = info.token + } + } + + function getUserInfo(): AuthUserInfo | null { + return userInfo.value + } + + function setPermissions(nextPermissions: PermissionCode[]) { + if (!userInfo.value) { + userInfo.value = { permissions: nextPermissions } + return + } + userInfo.value.permissions = nextPermissions + } + + function getPermissions(): PermissionCode[] { + return permissions.value + } + + function hasPermission(code: PermissionCode): boolean { + return getPermissions().includes(code) + } + + function hasAnyPermission(codes: PermissionCode[]): boolean { + if (!codes.length) return true + return codes.some(code => hasPermission(code)) + } + + function hasAllPermissions(codes: PermissionCode[]): boolean { + return codes.every(code => hasPermission(code)) + } + + function clearAuth() { token.value = '' + userInfo.value = null } return { token, + userInfo, + permissions, setToken, getToken, - removeToken, + setUserInfo, + getUserInfo, + setPermissions, + getPermissions, + hasPermission, + hasAnyPermission, + hasAllPermissions, + clearAuth, } }, { persist: true, diff --git a/rc_autoplc_front/src/utils/request.ts b/rc_autoplc_front/src/utils/request.ts index d1c3c14..f8a18d3 100644 --- a/rc_autoplc_front/src/utils/request.ts +++ b/rc_autoplc_front/src/utils/request.ts @@ -26,7 +26,7 @@ const TokenManager = { removeToken() { const authStore = useAuthStore() - authStore.removeToken() + authStore.clearAuth() // 删除默认请求头 delete request.defaults.headers.common['Authorization'] }, @@ -82,28 +82,27 @@ request.interceptors.request.use( // 响应拦截器 request.interceptors.response.use( (response: AxiosResponse) => { - const { data } = response + const { data } = response as AxiosResponse + const responseToken = data?.token || data?.data?.token + const responseCode = data?.code ?? data?.data?.code - // 如果返回 code 为 0 且有新 token,更新 token - if (data.code === 0 && data.token) { - TokenManager.setToken(data.token) + if (responseCode === 0 && responseToken) { + TokenManager.setToken(responseToken) } console.log('data*--', data) - // 处理业务错误码 - if (data.code === 302) { + if (responseCode === 302) { // 处理登录过期(防重复提示) handleLoginExpired() return Promise.reject(data) } - // 其他错误码 - if (data.code !== 0 && data.code !== undefined) { - // 检查是否是状态刷新相关的请求,如果是则静默处理某些错误 + const hasBusinessCode = responseCode !== undefined && responseCode !== null + if (hasBusinessCode && responseCode !== 0) { const url = response.config?.url || '' const isStatusRefreshRequest = url.includes('/plc/plcStatus') || url.includes('/plc/getModel') - const errorMsg = data.message || data.msg || '' + const errorMsg = data?.message || data?.msg || data?.data?.message || data?.data?.msg || '' // 如果是状态刷新请求,且错误消息包含特定关键词,则静默处理 if (isStatusRefreshRequest && ( @@ -125,17 +124,17 @@ request.interceptors.response.use( return data }, (error) => { - // 处理401未授权错误 - if (error.response?.status === 401) { + if (axios.isAxiosError(error) && error.response?.status === 401) { // 处理登录过期(防重复提示) handleLoginExpired() return Promise.reject(error) } - // 检查是否是状态刷新相关的请求 - const url = error.config?.url || '' + const url = axios.isAxiosError(error) ? (error.config?.url || '') : '' const isStatusRefreshRequest = url.includes('/plc/plcStatus') || url.includes('/plc/getModel') - const errorMsg = error.response?.data?.message || error.response?.data?.msg || '请求失败' + const errorMsg = axios.isAxiosError(error) + ? (error.response?.data?.message || error.response?.data?.msg || error.response?.data?.data?.message || error.response?.data?.data?.msg || '请求失败') + : '请求失败' // 如果是状态刷新请求,且错误消息包含特定关键词,则静默处理 if (isStatusRefreshRequest && ( diff --git a/rc_autoplc_front/src/views/Layout.vue b/rc_autoplc_front/src/views/Layout.vue index 5ea5e63..0f7ef22 100644 --- a/rc_autoplc_front/src/views/Layout.vue +++ b/rc_autoplc_front/src/views/Layout.vue @@ -35,68 +35,76 @@ 首页 - + + - + 用户管理 - + 角色管理 - + 权限管理 - + 部门管理 - + 操作日志管理 - + + - + PLC设备管理 - + 动作参数管理 - + 动作管理 - + 功能岛管理 - + SOP管理 + + + 样品记录 + - + + - + 进样控制 - + + 样品记录 @@ -113,7 +121,7 @@ - - - diff --git a/rc_autoplc_front/src/views/user/index.vue b/rc_autoplc_front/src/views/user/index.vue index 34490e2..fb61142 100644 --- a/rc_autoplc_front/src/views/user/index.vue +++ b/rc_autoplc_front/src/views/user/index.vue @@ -17,12 +17,12 @@ /> - 查询 + 查询 重置
- 新增用户 + 新增用户
@@ -92,9 +92,9 @@ />