feat(m11): add v-permission directive and button-level permission codes

This commit is contained in:
2026-05-25 01:41:50 +08:00
parent 1f599e5646
commit 3bb19537fe
6 changed files with 69 additions and 10 deletions
@@ -58,12 +58,39 @@ public class AuthController {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials");
}
List<String> 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<String, Object> 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")
@@ -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)
}
}
}
+2
View File
@@ -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");
@@ -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;
},
@@ -12,7 +12,7 @@
@keyup.enter="load"
/>
<el-button type="primary" :loading="loading" @click="load">查询</el-button>
<el-button type="success" @click="openCreate">新建客户</el-button>
<el-button type="success" v-permission="'customer:rw'" @click="openCreate">新建客户</el-button>
</div>
</div>
</template>
@@ -23,8 +23,8 @@
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="goDetail(row.id)">详情</el-button>
<el-button type="primary" link @click="openEdit(row)">编辑</el-button>
<el-button type="danger" link @click="onDelete(row)">删除</el-button>
<el-button type="primary" link v-permission="'customer:rw'" @click="openEdit(row)">编辑</el-button>
<el-button type="danger" link v-permission="'customer:delete'" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -21,8 +21,8 @@
@keyup.enter="load"
/>
<el-button type="primary" :loading="loading" @click="load">查询</el-button>
<el-button type="success" @click="goNew">新建许可 SN</el-button>
<el-button @click="batchDialogVisible = true">批量导入</el-button>
<el-button type="success" v-permission="'license:sn:rw'" @click="goNew">新建许可 SN</el-button>
<el-button v-permission="'license:sn:rw'" @click="batchDialogVisible = true">批量导入</el-button>
</div>
</div>
</template>