preface
Once our project dependencies are configured, the most important step is how to handle user permissions and assign the specified menus to users. First you need to know that our routing table will only a small part of the configuration, this part is not need any permission will be able to access, namely white list, such as login routing, routing, routing, and so on 500, 404 other we can from the interface to get inside, so that we can according to user’s permission to do some filter processing, In this way, different users can display different menus and routes.
Of course, there are other ways to do this, and I personally prefer this way for now. All right, let’s cut to the chase.
Configure the AXIos interceptor
Like configuring the AXIos interceptor, there are probably a thousand ways to write it for a thousand people, but the core logic is the same, just doing some setup, some request headers and some processing of the response data.
Create an instance
First we create an instance of axios, using the axios.create(config) method. BaseURL will automatically precede our url (unless the url is an absolute url), and timeout indicates the timeout that the request will be interrupted if there is no response after this time.
const service = axios.create({
baseURL: defaultConfig.zhtApiUrl,
timeout: timeout * 1000
})
Copy the code
Once the instance is created, you can configure the interceptor.
Request interceptor
In the request interceptor, we can do some processing on the request header. For example, we can add tokens to request interceptors like this:
service.interceptors.request.use(
config= > {
const TOKEN = getToken()
if (TOKEN) {
config.headers.Authorization = TOKEN
}
return config
},
error= > {
Promise.reject(error)
}
)
Copy the code
Response interceptor
In the response interceptor, the response data is mainly processed, such as the response status, response data, and some error handling.
service.interceptors.response.use(
response= > {
const { url: apiUrl } = response.config
const { status, statusText, data } = response
if (status === 200) {
if(apiUrl! .includes('auth/social/token')) {
// The interface to obtain the user token returns a different format from other interfaces.
return {
code: 0.msg: 'success',
data
}
} else {
return data
}
} else {
/ / an error
message.error('Error' status:${status} - statusText: ${statusText}`)
return Promise.reject({
code: 1.msg: statusText
})
}
},
error= > {
// { response, message, request, config } = error
if (error.response) {
const { status, statusText } = error.response
if (status === 401) {
message.error('Interface has no permission to access, please check the interface or parameters! status:${status} - statusText: ${statusText}. `)}else if (status === 500) {
message.error('Please check the interface or server status! status:${status} - statusText: ${statusText}. `)
router.push('/portal-error500')}else {
message.error('Wrong! message:${error.message} - statusText: ${statusText}. `)}}else {
message.error('No access! Error message:${error}`)}})Copy the code
Each person or different projects may have different configurations. Therefore, the configurations here are for reference only, and specific configurations need to be based on business requirements.
Cancel interceptor
If you want to cancel the interceptor at some point, you can do this:
const myInterceptor = axios.interceptors.request.use(function () {/ *... * /});
axios.interceptors.request.eject(myInterceptor);
Copy the code
Navigation guard
Vue-router provides navigation guards that are used to guard navigation by jumping or canceling. There are many ways to implement route navigation: global, proprietary to a single route, or component-level.
We can create a permissions. Ts file to write some global navigational guards.
Global front guard
You can register a global front-guard using router.beforeEach.
router.beforeEach((to, from) = > {
// ...
// Return false to cancel navigation
return false
})
Copy the code
In permissions. Ts file, we can go to configuration, in front of the navigation jump, we need to determine whether the current user login, if he didn’t want to go to the login page, so we let him jump to the login page, if logged in, will determine whether he has obtained the permission, without obtaining permission, we will go to the interface, Otherwise, we go straight to the project’s home page (if you remember the redirect, redirect to that page, not the home page).
Our login interface only returns token-related information, so we also need to obtain the permission of the user according to the TOKEN, and other information.
router.beforeEach(async (to, from, next) => {
const token = getToken()
const isWhite = whiteList.findIndex(w= > w === to.path)
NProgress.start()
if (token) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// Get the application
await store.dispatch(`userModule/${userAction.GetApplictions}`)
// Obtain the user transfer permission
const roles = await store.dispatch(`userModule/${userAction.GetInfo}`)
// Get the route configuration, which can be filtered by roles
const accessRoutes = await store.dispatch(`permissionsModule/${permissionAction.GenerateRoutes}`, roles)
next({ path: '/'.replace: true})}catch (error) {
next(`/login? redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
if (to.path === '/login') {
next()
NProgress.done()
} else if (isWhite > -1) {
// Within the whitelist range
next()
} else {
next('/login')}}})Copy the code
Global post-hook
We can also create global post-hooks, which are useful for analyzing, changing page titles, declaring pages, and many other things.
router.afterEach((to, from) = > {
NProgress.done()
})
Copy the code
Status Management Store
Permissions module
Create a user module (store/modules/permissions/index. The ts), as used to store here, permissions, the center of the processing route.
State the type
First we define the state type in permissions, I only have one routes data in this, so we just need to define the routes type.
export type ChildRouteType= Array<RouteType>
export interface RouteType {
name: string,
path: string,
component: string, redirect? : string, children? : ChildRouteType }export default interface PermissionsStateTypes {
routes: Array<RouteType>
}
Copy the code
Mutations and Actions constants
We then define our MutationTypes and ActionTypes, which is fine if we don’t, but the page may be loaded with magic strings. (The official recommendation is that we define some constants in this way, and then we only need to change one place.)
export enum MutationTypes {
SetRoutes = "SET_ROUTES"
}
export enum ActionTypes {
GenerateRoutes = "GENERATE_ROUTES"
}
Copy the code
Mutations and Actions method types
Once we’ve defined this, we also need to define the mutations and Actions method types.
export type Mutations<T = PermissionsStateTypes> = {
[MutationTypes.SetRoutes](state: T, routes: RouteType[]): void
}
type ActionArgs = Omit<ActionContext<PermissionsStateTypes, RootState>, 'commit'> & {
commit<k extends keyof Mutations>(
key: k,
payload: Parameters<Mutations[k]>[1]
): ReturnType<Mutations[k]>
}
export type Actions = {
[ActionTypes.GenerateRoutes]({ commit }: ActionArgs, roles: string[]): void
}
Copy the code
implementation
With all the types defined, it’s time to implement the core logic in Permissions.
In actions, we can retrieve route data from the interface and filter it by roles. If we have access to routes, we can add them dynamically by router.addroute.
Since the route returned by the interface is not a real component,
const state: PermissionsStateTypes = {
routes: []}const mutations: MutationTree<PermissionsStateTypes> & Mutations = {
[MutationTypes.SetRoutes](state: PermissionsStateTypes, routes: RouteType[]) {
state.routes = routes
}
}
const handleParseChildRoutes = (childs: ChildRouteType, prePath: string): ChildRouteType= > {
if (childs) {
// @ts-ignore
return childs.map((c: RouteType) = > {
return {
name: c.name,
path: c.path,
component: c.component === 'RouterView' ? RouterView : () = > import(`@/views${prePath}/index.vue`)}}}else {
return[]}}const actions: ActionTree<PermissionsStateTypes, RootState> & Actions = {
[ActionTypes.GenerateRoutes]({ commit }, roles: string[]) {
// Optionally return a route based on roles
// console.log('permissions roles', roles)
return new Promise<void> ((resolve, reject) = > {
// asyncRoutesMap is static data that simulates calling data from a background interface
asyncRoutesMap.forEach((route: RouteType) = > {
router.addRoute({
name: route.name,
path: route.path,
redirect: route.redirect ? route.redirect : undefined.component: () = > import(` @ /${route.component.toLowerCase()}/index.vue`),
children: handleParseChildRoutes((route.children as ChildRouteType), route.path)
})
})
After processing, you can return accessRoutes
commit(MutationTypes.SetRoutes, asyncRoutesMap)
resolve(asyncRoutesMap)
})
}
}
Copy the code
User modules
Create a user module (store/modules/user/index. The ts), used to store user information, the user’s permissions and application and the center of the menu.
State the type
The state of the user module contains the user name, permissions, profile picture, introduction, currentApp object (currentApp) and array collection of all apps.
exportinterface ApplicationType { id? : number appId? : number path? : string menuName? : string childMenu? :Array<ApplicationType>
[propName:string]: any
}
export default interface UserModuleStateType {
name: string
roles: Array<string>
currentApp: ApplicationType
applications: Array<ApplicationType> avatar? : string introduction? : string }Copy the code
Mutations and Actions constants
export enum MutationTypes {
SetRoles = "SET_ROLES",
SetApplications = "SET_APPLICATIONS",
SetCurrentApp = "SET_CURRENT_APP"
}
export enum ActionTypes {
GetInfo = "GET_INFO",
GetApplictions = "GET_APPLICATONS"
}
Copy the code
Mutations and Actions method types
type ActionArgs = Omit<ActionContext<UserModuleStateType, RootState>, 'commit'> & {
commit<k extends keyof Mutations>(
key: k,
payload: Parameters<Mutations[k]>[1]
): ReturnType<Mutations[k]>
}
export type Mutations<T = UserModuleStateType> = {
[MutationTypes.SetRoles](state: T, roles: Array<string>): void
[MutationTypes.SetApplications](state: T, apps: Array<ApplicationType>): void
[MutationTypes.SetCurrentApp](state: T, app: ApplicationType): void
}
export type Actions = {
[ActionTypes.GetInfo]({ commit }: ActionArgs): void
[ActionTypes.GetApplictions]({ commit }: ActionArgs): void
}
Copy the code
implementation
Once you’ve defined all the types, it’s time to implement the concrete logic.
This includes setting the current user role SET_ROLSE (permissions), getting the application data set SET_APPLICATIONS, and setting the application object SET_CURRENT_APP that is currently connected to the central platform. The data is static data, simulating the data called from the background interface.
const state: UserModuleStateType = {
name: ' '.avatar: ' '.introduction: ' '.roles: [].applications: [].currentApp: {}}const mutations: MutationTree<UserModuleStateType> & Mutations = {
[MutationTypes.SET_ROLES](state: UserModuleStateType, roles: Array<string>) {
state.roles = roles
},
[MutationTypes.SET_APPLICATIONS](state: UserModuleStateType, apps: Array<ApplicationType>) {
state.applications = apps
},
[MutationTypes.SET_CURRENT_APP](state: UserModuleStateType, app: ApplicationType) {
state.currentApp = app
}
}
const actions: ActionTree<UserModuleStateType, RootState> & Actions = {
[ActionTypes.GetInfo]({ commit }) {
return new Promise((resolve, reject) = > {
// getInfo() gets the interface
const roles = ['admin'.'editor']
commit(MutationTypes.SET_ROLES, roles)
resolve(roles)
})
},
[ActionTypes.GetApplictions]({ commit }) {
return new Promise<void> ((resolve, reject) = > {
commit(MutationTypes.SET_APPLICATIONS, applications)
commit(MutationTypes.SET_CURRENT_APP, applications[0])
resolve()
})
}
}
const UserModule: Module<UserModuleStateType, RootState> = {
namespaced: true,
state,
mutations,
actions
}
export default UserModule
Copy the code
At this point, our user module is configured.
conclusion
We use a lot of TypeScript syntax in Vuex, many of which are built-in TypeScript utility types, such as:
-
Pick<T, K>
The ability to take a given property and its type from an existing object type and build a new object type.
-
Omit<T, K>
Complementary to the “Pick<T, K>” utility type, it can strip a given attribute from an existing object type and then build a new object type.
There are a lot of useful tool types to discover and use, and there are a lot of problems with TypeScript that take time to develop, and the more you write, the better you get at it.
To communicate with
Whatever problem you’re having, or just want to make a friend and talk about technology (= =), can join our organization, with us ~
Enjoy this part of the content, join our QQ group, and share the technology with you
QQ group: 1032965518