vue2server7 7.0.50 → 7.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/ms-vscode-remote.remote-ssh-0.119.2025033120.vsix +0 -0
- package/test/13420256837985870.gif +0 -0
- package/test/13420256921603132.gif +0 -0
- package/test/13420256985163546.png +0 -0
- package/test/vue3_permission_directive.md +0 -206
- package/test/vue3_permission_directive_advanced.md +0 -248
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# Vue3 按钮权限控制(自定义指令 + Pinia 版)
|
|
2
|
-
|
|
3
|
-
## 📌 目标
|
|
4
|
-
|
|
5
|
-
实现一套企业级按钮权限控制方案:
|
|
6
|
-
|
|
7
|
-
- ✔ 从 Pinia 获取权限
|
|
8
|
-
- ✔ 支持单权限 / 多权限
|
|
9
|
-
- ✔ 支持 AND / OR 逻辑
|
|
10
|
-
- ✔ 支持动态更新权限
|
|
11
|
-
- ✔ 全局指令 v-permission
|
|
12
|
-
- ✔ 自动响应权限变化
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# 🧠 一、权限 Store(Pinia)
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
// stores/auth.ts
|
|
20
|
-
import { defineStore } from 'pinia'
|
|
21
|
-
|
|
22
|
-
export const useAuthStore = defineStore('auth', {
|
|
23
|
-
state: () => ({
|
|
24
|
-
permissions: [] as string[],
|
|
25
|
-
}),
|
|
26
|
-
|
|
27
|
-
actions: {
|
|
28
|
-
setPermissions(perms: string[]) {
|
|
29
|
-
this.permissions = perms
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
clearPermissions() {
|
|
33
|
-
this.permissions = []
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
# 🧩 二、权限判断工具函数
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
// utils/permission.ts
|
|
45
|
-
|
|
46
|
-
export type PermissionValue =
|
|
47
|
-
| string
|
|
48
|
-
| string[]
|
|
49
|
-
| { value: string[]; mode?: 'and' | 'or' }
|
|
50
|
-
|
|
51
|
-
export function hasPermission(
|
|
52
|
-
userPermissions: string[],
|
|
53
|
-
required?: PermissionValue
|
|
54
|
-
): boolean {
|
|
55
|
-
if (!required) return true
|
|
56
|
-
|
|
57
|
-
if (typeof required === 'string') {
|
|
58
|
-
return userPermissions.includes(required)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (Array.isArray(required)) {
|
|
62
|
-
return required.some(p => userPermissions.includes(p))
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const { value, mode = 'or' } = required
|
|
66
|
-
|
|
67
|
-
if (mode === 'and') {
|
|
68
|
-
return value.every(p => userPermissions.includes(p))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return value.some(p => userPermissions.includes(p))
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
# ⚙️ 三、自定义指令 v-permission
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
// directives/permission.ts
|
|
81
|
-
import type { Directive } from 'vue'
|
|
82
|
-
import { useAuthStore } from '@/stores/auth'
|
|
83
|
-
import { hasPermission } from '@/utils/permission'
|
|
84
|
-
|
|
85
|
-
export const permission: Directive = {
|
|
86
|
-
mounted(el, binding) {
|
|
87
|
-
const authStore = useAuthStore()
|
|
88
|
-
|
|
89
|
-
const check = () => {
|
|
90
|
-
const ok = hasPermission(authStore.permissions, binding.value)
|
|
91
|
-
|
|
92
|
-
if (!ok) {
|
|
93
|
-
el.remove()
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
check()
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
updated(el, binding) {
|
|
101
|
-
const authStore = useAuthStore()
|
|
102
|
-
|
|
103
|
-
const ok = hasPermission(authStore.permissions, binding.value)
|
|
104
|
-
|
|
105
|
-
if (!ok) {
|
|
106
|
-
el.remove()
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
# 🚀 四、全局注册指令
|
|
115
|
-
|
|
116
|
-
```ts
|
|
117
|
-
// main.ts
|
|
118
|
-
import { createApp } from 'vue'
|
|
119
|
-
import App from './App.vue'
|
|
120
|
-
import { createPinia } from 'pinia'
|
|
121
|
-
import { permission } from '@/directives/permission'
|
|
122
|
-
|
|
123
|
-
const app = createApp(App)
|
|
124
|
-
|
|
125
|
-
app.use(createPinia())
|
|
126
|
-
|
|
127
|
-
app.directive('permission', permission)
|
|
128
|
-
|
|
129
|
-
app.mount('#app')
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
# 🧪 五、使用方式
|
|
135
|
-
|
|
136
|
-
## ✔ 单权限
|
|
137
|
-
|
|
138
|
-
```vue
|
|
139
|
-
<button v-permission="'user:add'">
|
|
140
|
-
新增用户
|
|
141
|
-
</button>
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## ✔ 多权限(OR)
|
|
147
|
-
|
|
148
|
-
```vue
|
|
149
|
-
<button v-permission="['user:add', 'user:edit']">
|
|
150
|
-
新增 / 编辑
|
|
151
|
-
</button>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## ✔ AND 权限
|
|
157
|
-
|
|
158
|
-
```vue
|
|
159
|
-
<button
|
|
160
|
-
v-permission="{
|
|
161
|
-
value: ['user:add', 'user:delete'],
|
|
162
|
-
mode: 'and'
|
|
163
|
-
}"
|
|
164
|
-
>
|
|
165
|
-
必须同时拥有权限
|
|
166
|
-
</button>
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
# ⚠️ 六、常见问题
|
|
172
|
-
|
|
173
|
-
## ❌ Pinia 未初始化
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
let authStore: any
|
|
177
|
-
|
|
178
|
-
function getStore() {
|
|
179
|
-
if (!authStore) {
|
|
180
|
-
authStore = useAuthStore()
|
|
181
|
-
}
|
|
182
|
-
return authStore
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## ❌ 权限不更新
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
import { watch } from 'vue'
|
|
192
|
-
|
|
193
|
-
watch(
|
|
194
|
-
() => authStore.permissions,
|
|
195
|
-
() => {
|
|
196
|
-
// recheck
|
|
197
|
-
},
|
|
198
|
-
{ deep: true }
|
|
199
|
-
)
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
# 🎯 总结
|
|
205
|
-
|
|
206
|
-
适用于中后台系统的权限控制方案,支持扩展 RBAC / 路由权限 / 菜单权限。
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
# Vue3 按钮权限管理(进阶版:自定义指令)
|
|
2
|
-
|
|
3
|
-
## 📌 目标
|
|
4
|
-
实现一套通用的按钮权限控制方案,支持:
|
|
5
|
-
|
|
6
|
-
- ✅ 单权限 / 多权限
|
|
7
|
-
- ✅ AND / OR 权限判断
|
|
8
|
-
- ✅ 隐藏 或 禁用 控制
|
|
9
|
-
- ✅ 动态更新权限
|
|
10
|
-
- ✅ 可扩展(Pinia / 后端权限)
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 🧩 一、指令实现
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
// src/directives/permission.ts
|
|
18
|
-
import type { Directive } from 'vue'
|
|
19
|
-
|
|
20
|
-
function getPermissions(): string[] {
|
|
21
|
-
return JSON.parse(localStorage.getItem('permissions') || '[]')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function checkPermission(value: any, permissions: string[]) {
|
|
25
|
-
if (typeof value === 'string') {
|
|
26
|
-
return permissions.includes(value)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (Array.isArray(value)) {
|
|
30
|
-
return value.some(p => permissions.includes(p))
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (typeof value === 'object') {
|
|
34
|
-
const { code, mode = 'or' } = value
|
|
35
|
-
|
|
36
|
-
if (Array.isArray(code)) {
|
|
37
|
-
return mode === 'and'
|
|
38
|
-
? code.every(p => permissions.includes(p))
|
|
39
|
-
: code.some(p => permissions.includes(p))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return permissions.includes(code)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function handleEl(el: HTMLElement, hasPermission: boolean, effect = 'remove') {
|
|
49
|
-
if (hasPermission) return
|
|
50
|
-
|
|
51
|
-
if (effect === 'disable') {
|
|
52
|
-
el.setAttribute('disabled', 'true')
|
|
53
|
-
el.classList.add('is-disabled')
|
|
54
|
-
el.style.pointerEvents = 'none'
|
|
55
|
-
} else {
|
|
56
|
-
el.parentNode?.removeChild(el)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const permissionDirective: Directive = {
|
|
61
|
-
mounted(el, binding) {
|
|
62
|
-
const permissions = getPermissions()
|
|
63
|
-
const { value, arg } = binding
|
|
64
|
-
|
|
65
|
-
const hasPermission = checkPermission(value, permissions)
|
|
66
|
-
|
|
67
|
-
handleEl(el, hasPermission, arg)
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
updated(el, binding) {
|
|
71
|
-
const permissions = getPermissions()
|
|
72
|
-
const { value, arg } = binding
|
|
73
|
-
|
|
74
|
-
const hasPermission = checkPermission(value, permissions)
|
|
75
|
-
|
|
76
|
-
handleEl(el, hasPermission, arg)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## 🚀 二、全局注册
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
// main.ts
|
|
87
|
-
import { createApp } from 'vue'
|
|
88
|
-
import App from './App.vue'
|
|
89
|
-
import { permissionDirective } from '@/directives/permission'
|
|
90
|
-
|
|
91
|
-
const app = createApp(App)
|
|
92
|
-
|
|
93
|
-
app.directive('permission', permissionDirective)
|
|
94
|
-
|
|
95
|
-
app.mount('#app')
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## 🎯 三、使用方式
|
|
101
|
-
|
|
102
|
-
### 1️⃣ 单权限
|
|
103
|
-
```vue
|
|
104
|
-
<el-button v-permission="'user:add'">新增</el-button>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
### 2️⃣ 多权限(满足一个)
|
|
110
|
-
```vue
|
|
111
|
-
<el-button v-permission="['user:add','user:create']">
|
|
112
|
-
新增
|
|
113
|
-
</el-button>
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
### 3️⃣ 多权限(必须全部满足)
|
|
119
|
-
```vue
|
|
120
|
-
<el-button v-permission="{ code: ['user:add','user:vip'], mode: 'and' }">
|
|
121
|
-
高级新增
|
|
122
|
-
</el-button>
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
### 4️⃣ 无权限 → 禁用按钮
|
|
128
|
-
```vue
|
|
129
|
-
<el-button v-permission:disable="'user:add'">
|
|
130
|
-
新增
|
|
131
|
-
</el-button>
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## 🧠 四、推荐优化(生产环境必做)
|
|
137
|
-
|
|
138
|
-
### ✔ 使用 Pinia 管理权限
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
// stores/user.ts
|
|
142
|
-
import { defineStore } from 'pinia'
|
|
143
|
-
|
|
144
|
-
export const useUserStore = defineStore('user', {
|
|
145
|
-
state: () => ({
|
|
146
|
-
permissions: [] as string[]
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### ✔ 修改指令权限来源
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
import { useUserStore } from '@/stores/user'
|
|
155
|
-
|
|
156
|
-
const permissions = useUserStore().permissions
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
## 📦 五、权限格式规范
|
|
162
|
-
|
|
163
|
-
建议统一格式:
|
|
164
|
-
|
|
165
|
-
```
|
|
166
|
-
user:add
|
|
167
|
-
user:delete
|
|
168
|
-
order:list
|
|
169
|
-
order:create
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## 🔐 六、后端权限校验(必须)
|
|
175
|
-
|
|
176
|
-
前端权限只是 UI 控制,后端必须校验:
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
if (!user.permissions.includes('user:add')) {
|
|
180
|
-
throw new Error('Forbidden')
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## ⚠️ 七、常见问题
|
|
187
|
-
|
|
188
|
-
### ❌ 权限变化 UI 不更新
|
|
189
|
-
✔ 已通过 `updated` 生命周期解决
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
### ❌ Element Plus 禁用无效
|
|
194
|
-
✔ 必须加:
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
el.style.pointerEvents = 'none'
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
### ❌ 使用 v-if 控制权限
|
|
203
|
-
不推荐,会导致:
|
|
204
|
-
- 模板混乱
|
|
205
|
-
- 逻辑分散
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## 🏁 总结
|
|
210
|
-
|
|
211
|
-
推荐最终方案:
|
|
212
|
-
|
|
213
|
-
👉 自定义指令(控制 UI)
|
|
214
|
-
👉 Pinia(存权限)
|
|
215
|
-
👉 后端校验(保证安全)
|
|
216
|
-
|
|
217
|
-
这样你的权限系统:
|
|
218
|
-
- 清晰
|
|
219
|
-
- 可维护
|
|
220
|
-
- 可扩展
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
type Button = {
|
|
224
|
-
appName: string
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
type Menu = {
|
|
228
|
-
children?: Menu[]
|
|
229
|
-
buttonChildren?: Button[]
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function getButtonPermissions(menuTree: Menu[]): string[] {
|
|
233
|
-
const set = new Set<string>()
|
|
234
|
-
|
|
235
|
-
const dfs = (nodes: Menu[]) => {
|
|
236
|
-
nodes.forEach(node => {
|
|
237
|
-
node.buttonChildren?.forEach(btn => {
|
|
238
|
-
if (btn.appName) set.add(btn.appName)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
if (node.children) dfs(node.children)
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
dfs(menuTree)
|
|
246
|
-
|
|
247
|
-
return [...set]
|
|
248
|
-
}
|