From 3bb19537fe3e100fd6667a3c9ba52773f02aeb8d Mon Sep 17 00:00:00 2001 From: huangping Date: Mon, 25 May 2026 01:41:50 +0800 Subject: [PATCH] feat(m11): add v-permission directive and button-level permission codes --- .../platform/api/auth/AuthController.java | 37 ++++++++++++++++--- .../src/directives/permission.js | 27 ++++++++++++++ web/delivery-platform-ui/src/main.js | 2 + web/delivery-platform-ui/src/stores/auth.js | 3 ++ .../src/views/CustomersView.vue | 6 +-- .../src/views/LicenseSnListView.vue | 4 +- 6 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 web/delivery-platform-ui/src/directives/permission.js diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java index 1dcec17..c069e61 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java @@ -58,12 +58,39 @@ public class AuthController { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials"); } + List permissions = new java.util.ArrayList<>(); + switch(role) { + case "SYS_ADMIN": + permissions.add("*:*"); + break; + case "SALES": + permissions.add("customer:*"); + permissions.add("project:*"); + permissions.add("contract:*"); + permissions.add("delivery:read"); + break; + case "DELIVERY": + permissions.add("delivery:*"); + permissions.add("device:*"); + break; + case "LICENSE_OPS": + permissions.add("license:*"); + permissions.add("callback:*"); + permissions.add("todo:*"); + permissions.add("device:read"); + permissions.add("integration:read"); + permissions.add("report:callback"); + break; + } + String token = jwtService.createToken(user, displayName, List.of(role)); - return Map.of( - "token", token, - "tokenType", "Bearer", - "roles", List.of(role), - "displayName", displayName); + java.util.Map result = new java.util.LinkedHashMap<>(); + result.put("token", token); + result.put("tokenType", "Bearer"); + result.put("roles", List.of(role)); + result.put("displayName", displayName); + result.put("permissions", permissions); + return result; } @PostMapping("/change-password") diff --git a/web/delivery-platform-ui/src/directives/permission.js b/web/delivery-platform-ui/src/directives/permission.js new file mode 100644 index 0000000..d28efde --- /dev/null +++ b/web/delivery-platform-ui/src/directives/permission.js @@ -0,0 +1,27 @@ +import { useAuthStore } from '../stores/auth' + +export default { + mounted(el, binding) { + const auth = useAuthStore() + const requiredPermission = binding.value + + if (!requiredPermission) return + + const userPermissions = auth.permissions || [] + + const hasPermission = userPermissions.some(p => { + if (typeof p === 'string') { + if (requiredPermission.endsWith(':*')) { + const prefix = requiredPermission.slice(0, -2) + return p.startsWith(prefix) + } + return p === requiredPermission || p === '*:*' + } + return false + }) + + if (!hasPermission) { + el.parentNode?.removeChild(el) + } + } +} diff --git a/web/delivery-platform-ui/src/main.js b/web/delivery-platform-ui/src/main.js index b75e270..d4957fa 100644 --- a/web/delivery-platform-ui/src/main.js +++ b/web/delivery-platform-ui/src/main.js @@ -7,6 +7,7 @@ import axios from "axios"; import App from "./App.vue"; import router from "./router"; import { useAuthStore } from "./stores/auth"; +import permission from "./directives/permission"; // 开发环境始终使用相对路径,以便 Vite 将 /api 代理到后端;误设 VITE_API_BASE 时否则会直连并常出现跨域或连错环境。 const apiBaseRaw = @@ -38,4 +39,5 @@ axios.interceptors.response.use( }, ); +app.directive("permission", permission); app.use(router).use(ElementPlus).mount("#app"); diff --git a/web/delivery-platform-ui/src/stores/auth.js b/web/delivery-platform-ui/src/stores/auth.js index 03a7f70..35d5c3f 100644 --- a/web/delivery-platform-ui/src/stores/auth.js +++ b/web/delivery-platform-ui/src/stores/auth.js @@ -8,6 +8,7 @@ export const useAuthStore = defineStore("auth", { token: localStorage.getItem(TOKEN_KEY) || "", displayName: "", roles: [], + permissions: [], }), getters: { hasAnyRole: (state) => { @@ -24,6 +25,7 @@ export const useAuthStore = defineStore("auth", { this.token = data.token; this.displayName = data.displayName || username; this.roles = data.roles || []; + this.permissions = data.permissions || []; localStorage.setItem(TOKEN_KEY, this.token); axios.defaults.headers.common.Authorization = `Bearer ${this.token}`; }, @@ -31,6 +33,7 @@ export const useAuthStore = defineStore("auth", { this.token = ""; this.displayName = ""; this.roles = []; + this.permissions = []; localStorage.removeItem(TOKEN_KEY); delete axios.defaults.headers.common.Authorization; }, diff --git a/web/delivery-platform-ui/src/views/CustomersView.vue b/web/delivery-platform-ui/src/views/CustomersView.vue index 5e221a9..0d62193 100644 --- a/web/delivery-platform-ui/src/views/CustomersView.vue +++ b/web/delivery-platform-ui/src/views/CustomersView.vue @@ -12,7 +12,7 @@ @keyup.enter="load" /> 查询 - 新建客户 + 新建客户 @@ -23,8 +23,8 @@ diff --git a/web/delivery-platform-ui/src/views/LicenseSnListView.vue b/web/delivery-platform-ui/src/views/LicenseSnListView.vue index 63b98a2..8bc2aba 100644 --- a/web/delivery-platform-ui/src/views/LicenseSnListView.vue +++ b/web/delivery-platform-ui/src/views/LicenseSnListView.vue @@ -21,8 +21,8 @@ @keyup.enter="load" /> 查询 - 新建许可 SN - 批量导入 + 新建许可 SN + 批量导入