Skip to content

Commit 7fc2f00

Browse files
committed
* 'main' of https://github.com/baohaojie123/vue-vben-admin: types: 扩展user-dropdown组件的menus类型,支持iconify (vbenjs#6283) fix: resolve onClosed method failure in connectedComponent of useVbenModal (vbenjs#6309) fix: 修复使用useVbenVxeGrid配置hasEmptyText、hasEmptyRender不生效的问题 (vbenjs#6310) fix: When defaultHomePage is inconsistent with user.homePath, the pa… (vbenjs#6299) docs(settings): 完善'生产环境动态配置'步骤 (vbenjs#6297) fix: alert width fixed in small screen (vbenjs#6312) style: fix lint error (vbenjs#6298) feat: support for hybrid permission access control mode (vbenjs#6294) fix: fix table-title slot not work (vbenjs#6295) # Conflicts: # docs/src/en/guide/in-depth/access.md
2 parents 8a856eb + ea776aa commit 7fc2f00

File tree

13 files changed

+486
-17
lines changed

13 files changed

+486
-17
lines changed

docs/src/en/guide/essentials/settings.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The rules are consistent with [Vite Env Variables and Modes](https://vitejs.dev/
2121
console.log(import.meta.env.VITE_PROT);
2222
```
2323

24-
- Variables starting with `VITE_GLOB_*` will be added to the `_app.config.js` configuration file during packaging. :::
24+
- Variables starting with `VITE_GLOB_*` will be added to the `_app.config.js` configuration file during packaging.
2525

2626
:::
2727

@@ -138,6 +138,27 @@ To add a new dynamically modifiable configuration item, simply follow the steps
138138
}
139139
```
140140

141+
- In `packages/effects/hooks/src/use-app-config.ts`, add the corresponding configuration item, such as:
142+
143+
```ts
144+
export function useAppConfig(
145+
env: Record<string, any>,
146+
isProduction: boolean,
147+
): ApplicationConfig {
148+
// In production environment, directly use the window._VBEN_ADMIN_PRO_APP_CONF_ global variable
149+
const config = isProduction
150+
? window._VBEN_ADMIN_PRO_APP_CONF_
151+
: (env as VbenAdminProAppConfigRaw);
152+
153+
const { VITE_GLOB_API_URL, VITE_GLOB_OTHER_API_URL } = config; // [!code ++]
154+
155+
return {
156+
apiURL: VITE_GLOB_API_URL,
157+
otherApiURL: VITE_GLOB_OTHER_API_URL, // [!code ++]
158+
};
159+
}
160+
```
161+
141162
At this point, you can use the `useAppConfig` method within the project to access the newly added configuration item.
142163

143164
```ts

docs/src/en/guide/in-depth/access.md

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Access Control
6+
7+
The framework has built-in three types of access control methods:
8+
9+
- Determining whether a menu or button can be accessed based on user roles
10+
- Determining whether a menu or button can be accessed through an API
11+
- Mixed mode: Using both frontend and backend access control simultaneously
12+
13+
## Frontend Access Control
14+
15+
**Implementation Principle**: The permissions for routes are hardcoded on the frontend, specifying which permissions are required to view certain routes. Only general routes are initialized, and routes that require permissions are not added to the route table. After logging in or obtaining user roles through other means, the roles are used to traverse the route table to generate a route table that the role can access. This table is then added to the router instance using `router.addRoute`, achieving permission filtering.
16+
17+
**Disadvantage**: The permissions are relatively inflexible; if the backend changes roles, the frontend needs to be adjusted accordingly. This is suitable for systems with relatively fixed roles.
18+
19+
### Steps
20+
21+
- Ensure the current mode is set to frontend access control
22+
23+
Adjust `preferences.ts` in the corresponding application directory to ensure `accessMode='frontend'`.
24+
25+
```ts
26+
import { defineOverridesPreferences } from '@vben/preferences';
27+
28+
export const overridesPreferences = defineOverridesPreferences({
29+
// overrides
30+
app: {
31+
// Default value, optional
32+
accessMode: 'frontend',
33+
},
34+
});
35+
```
36+
37+
- Configure route permissions
38+
39+
#### If not configured, it is visible by default
40+
41+
```ts {3}
42+
{
43+
meta: {
44+
authority: ['super'],
45+
},
46+
},
47+
```
48+
49+
- Ensure the roles returned by the interface match the permissions in the route table
50+
51+
You can look under `src/store/auth` in the application to find the following code:
52+
53+
```ts
54+
// Set the login user information, ensuring that userInfo.roles is an array and contains permissions from the route table
55+
// For example: userInfo.roles=['super', 'admin']
56+
authStore.setUserInfo(userInfo);
57+
```
58+
59+
At this point, the configuration is complete. You need to ensure that the roles returned by the interface after login match the permissions in the route table; otherwise, access will not be possible.
60+
61+
### Menu Visible but Access Forbidden
62+
63+
Sometimes, we need the menu to be visible but access to it forbidden. This can be achieved by setting `menuVisibleWithForbidden` to `true`. In this case, the menu will be visible, but access will be forbidden, redirecting to a 403 page.
64+
65+
```ts
66+
{
67+
meta: {
68+
menuVisibleWithForbidden: true,
69+
},
70+
},
71+
```
72+
73+
## Backend Access Control
74+
75+
**Implementation Principle**: It is achieved by dynamically generating a routing table through an API, which returns data following a certain structure. The frontend processes this data into a recognizable structure, then adds it to the routing instance using `router.addRoute`, realizing the dynamic generation of permissions.
76+
77+
**Disadvantage**: The backend needs to provide a data structure that meets the standards, and the frontend needs to process this structure. This is suitable for systems with more complex permissions.
78+
79+
### Steps
80+
81+
- Ensure the current mode is set to backend access control
82+
83+
Adjust `preferences.ts` in the corresponding application directory to ensure `accessMode='backend'`.
84+
85+
```ts
86+
import { defineOverridesPreferences } from '@vben/preferences';
87+
88+
export const overridesPreferences = defineOverridesPreferences({
89+
// overrides
90+
app: {
91+
accessMode: 'backend',
92+
},
93+
});
94+
```
95+
96+
- Ensure the structure of the menu data returned by the interface is correct
97+
98+
You can look under `src/router/access.ts` in the application to find the following code:
99+
100+
```ts
101+
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
102+
return await generateAccessible(preferences.app.accessMode, {
103+
fetchMenuListAsync: async () => {
104+
// This interface is for the menu data returned by the backend
105+
return await getAllMenus();
106+
},
107+
});
108+
}
109+
```
110+
111+
- Interface returns menu data, see comments for explanation
112+
113+
::: details Example of Interface Returning Menu Data
114+
115+
```ts
116+
const dashboardMenus = [
117+
{
118+
// Here, 'BasicLayout' is hardcoded and cannot be changed
119+
component: 'BasicLayout',
120+
meta: {
121+
order: -1,
122+
title: 'page.dashboard.title',
123+
},
124+
name: 'Dashboard',
125+
path: '/',
126+
redirect: '/analytics',
127+
children: [
128+
{
129+
name: 'Analytics',
130+
path: '/analytics',
131+
// Here is the path of the page, need to remove 'views/' and '.vue'
132+
component: '/dashboard/analytics/index',
133+
meta: {
134+
affixTab: true,
135+
title: 'page.dashboard.analytics',
136+
},
137+
},
138+
{
139+
name: 'Workspace',
140+
path: '/workspace',
141+
component: '/dashboard/workspace/index',
142+
meta: {
143+
title: 'page.dashboard.workspace',
144+
},
145+
},
146+
],
147+
},
148+
];
149+
```
150+
151+
:::
152+
153+
At this point, the configuration is complete. You need to ensure that after logging in, the format of the menu returned by the interface is correct; otherwise, access will not be possible.
154+
155+
## Mixed Access Control
156+
157+
**Implementation Principle**: Mixed mode combines both frontend access control and backend access control methods. The system processes frontend fixed route permissions and backend dynamic menu data in parallel, ultimately merging both parts of routes to provide a more flexible access control solution.
158+
159+
**Advantages**: Combines the performance advantages of frontend control with the flexibility of backend control, suitable for complex business scenarios requiring permission management.
160+
161+
### Steps
162+
163+
- Ensure the current mode is set to mixed access control
164+
165+
Adjust `preferences.ts` in the corresponding application directory to ensure `accessMode='mixed'`.
166+
167+
```ts
168+
import { defineOverridesPreferences } from '@vben/preferences';
169+
170+
export const overridesPreferences = defineOverridesPreferences({
171+
// overrides
172+
app: {
173+
accessMode: 'mixed',
174+
},
175+
});
176+
```
177+
178+
- Configure frontend route permissions
179+
180+
Same as the route permission configuration method in [Frontend Access Control](#frontend-access-control) mode.
181+
182+
- Configure backend menu interface
183+
184+
Same as the interface configuration method in [Backend Access Control](#backend-access-control) mode.
185+
186+
- Ensure roles and permissions match
187+
188+
Must satisfy both frontend route permission configuration and backend menu data return requirements, ensuring user roles match the permission configurations of both modes.
189+
190+
At this point, the configuration is complete. Mixed mode will automatically merge frontend and backend routes, providing complete access control functionality.
191+
192+
## Fine-grained Control of Buttons
193+
194+
In some cases, we need to control the display of buttons with fine granularity. We can control the display of buttons through interfaces or roles.
195+
196+
### Permission Code
197+
198+
The permission code is the code returned by the interface. The logic to determine whether a button is displayed is located under `src/store/auth`:
199+
200+
```ts
201+
const [fetchUserInfoResult, accessCodes] = await Promise.all([
202+
fetchUserInfo(),
203+
getAccessCodes(),
204+
]);
205+
206+
userInfo = fetchUserInfoResult;
207+
authStore.setUserInfo(userInfo);
208+
accessStore.setAccessCodes(accessCodes);
209+
```
210+
211+
Locate the `getAccessCodes` corresponding interface, which can be adjusted according to business logic.
212+
213+
The data structure returned by the permission code is an array of strings, for example: `['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010']`
214+
215+
With the permission codes, you can use the `AccessControl` component and API provided by `@vben/access` to show and hide buttons.
216+
217+
#### Component Method
218+
219+
```vue
220+
<script lang="ts" setup>
221+
import { AccessControl, useAccess } from '@vben/access';
222+
223+
const { accessMode, hasAccessByCodes } = useAccess();
224+
</script>
225+
226+
<template>
227+
<!-- You need to specify type="code" -->
228+
<AccessControl :codes="['AC_100100']" type="code">
229+
<Button> Visible to Super account ["AC_1000001"] </Button>
230+
</AccessControl>
231+
<AccessControl :codes="['AC_100030']" type="code">
232+
<Button> Visible to Admin account ["AC_100010"] </Button>
233+
</AccessControl>
234+
<AccessControl :codes="['AC_1000001']" type="code">
235+
<Button> Visible to User account ["AC_1000001"] </Button>
236+
</AccessControl>
237+
<AccessControl :codes="['AC_100100', 'AC_100010']" type="code">
238+
<Button>
239+
Visible to Super & Admin account ["AC_100100","AC_1000001"]
240+
</Button>
241+
</AccessControl>
242+
</template>
243+
```
244+
245+
#### API Method
246+
247+
```vue
248+
<script lang="ts" setup>
249+
import { AccessControl, useAccess } from '@vben/access';
250+
251+
const { hasAccessByCodes } = useAccess();
252+
</script>
253+
254+
<template>
255+
<Button v-if="hasAccessByCodes(['AC_100100'])">
256+
Visible to Super account ["AC_1000001"]
257+
</Button>
258+
<Button v-if="hasAccessByCodes(['AC_100030'])">
259+
Visible to Admin account ["AC_100010"]
260+
</Button>
261+
<Button v-if="hasAccessByCodes(['AC_1000001'])">
262+
Visible to User account ["AC_1000001"]
263+
</Button>
264+
<Button v-if="hasAccessByCodes(['AC_100100', 'AC_1000001'])">
265+
Visible to Super & Admin account ["AC_100100","AC_1000001"]
266+
</Button>
267+
</template>
268+
```
269+
270+
#### Directive Method
271+
272+
> The directive supports binding single or multiple permission codes. For a single one, you can pass a string or an array containing one permission code, and for multiple permission codes, you can pass an array.
273+
274+
```vue
275+
<template>
276+
<Button class="mr-4" v-access:code="'AC_100100'">
277+
Visible to Super account 'AC_100100'
278+
</Button>
279+
<Button class="mr-4" v-access:code="['AC_100030']">
280+
Visible to Admin account ["AC_100010"]
281+
</Button>
282+
<Button class="mr-4" v-access:code="['AC_1000001']">
283+
Visible to User account ["AC_1000001"]
284+
</Button>
285+
<Button class="mr-4" v-access:code="['AC_100100', 'AC_1000001']">
286+
Visible to Super & Admin account ["AC_100100","AC_1000001"]
287+
</Button>
288+
</template>
289+
```
290+
291+
### Roles
292+
293+
The method of determining roles does not require permission codes returned by the interface; it directly determines whether buttons are displayed based on roles.
294+
295+
#### Component Method
296+
297+
```vue
298+
<script lang="ts" setup>
299+
import { AccessControl } from '@vben/access';
300+
</script>
301+
302+
<template>
303+
<AccessControl :codes="['super']">
304+
<Button> Visible to Super account </Button>
305+
</AccessControl>
306+
<AccessControl :codes="['admin']">
307+
<Button> Visible to Admin account </Button>
308+
</AccessControl>
309+
<AccessControl :codes="['user']">
310+
<Button> Visible to User account </Button>
311+
</AccessControl>
312+
<AccessControl :codes="['super', 'admin']">
313+
<Button> Super & Visible to Admin account </Button>
314+
</AccessControl>
315+
</template>
316+
```
317+
318+
#### API Method
319+
320+
```vue
321+
<script lang="ts" setup>
322+
import { useAccess } from '@vben/access';
323+
324+
const { hasAccessByRoles } = useAccess();
325+
</script>
326+
327+
<template>
328+
<Button v-if="hasAccessByRoles(['super'])"> Visible to Super account </Button>
329+
<Button v-if="hasAccessByRoles(['admin'])"> Visible to Admin account </Button>
330+
<Button v-if="hasAccessByRoles(['user'])"> Visible to User account </Button>
331+
<Button v-if="hasAccessByRoles(['super', 'admin'])">
332+
Super & Visible to Admin account
333+
</Button>
334+
</template>
335+
```
336+
337+
#### Directive Method
338+
339+
> The directive supports binding single or multiple permission codes. For a single one, you can pass a string or an array containing one permission code, and for multiple permission codes, you can pass an array.
340+
341+
```vue
342+
<template>
343+
<Button class="mr-4" v-access:role="'super'">
344+
Visible to Super account
345+
</Button>
346+
<Button class="mr-4" v-access:role="['admin']">
347+
Visible to Admin account
348+
</Button>
349+
<Button class="mr-4" v-access:role="['user']">
350+
Visible to User account
351+
</Button>
352+
<Button class="mr-4" v-access:role="['super', 'admin']">
353+
Super & Visible to Admin account
354+
</Button>
355+
</template>
356+
```

0 commit comments

Comments
 (0)