This series of articles is not a step-by-step tutorial. It mainly introduces the core ideas and explains the core code. Friends can star and clone the complete code on GitHub to study. In addition, I originally planned to run the project and put it online for friends to view, but in order to save money before buying a server, the memory was only 512M, and the two applications could not run (there is already a V Tribe open source project running), so friends We can only look at the screenshot below. There is a deployment tutorial on GitHub. You can also deploy it locally to see the complete effect.
Project address: https://github.com/lenve/vhr
We have basically solved the previous articles It solves server-side problems and encapsulates front-end requests. In this article, we mainly talk about login and dynamic loading of components.
This article is the fifth in this series. It is recommended that you read the previous articles first to help you better understand this article:
1. SpringBoot+Vue separates the front and back ends, and uses SpringSecurity to perfectly handle permission issues (1)
2. SpringBoot+Vue separates the front and back ends, and uses SpringSecurity to perfectly handle permission issues (2)
3. Password salting in SpringSecurity and SpringBoot Unified exception handling
4.axios request encapsulation and unified exception handling
When the user logs in successfully, it needs to be The current user's login information is saved locally for later use. The specific implementation is as follows:
After the login operation is successfully executed, the data is submitted to the store through the commit operation. The core The code is as follows:
<span style="font-size: 14px;">this.postRequest('/login', {<br> username: this.loginForm.username,<br> password: this.loginForm.password<br>}).then(resp=> {<br> if (resp && resp.status == 200) {<br> var data = resp.data;<br> _this.$store.commit('login', data.msg);<br> var path = _this.$route.query.redirect;<br> _this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});<br> }<br>});<br></span>
The core code of store is as follows:
<span style="font-size: 14px;">export default new Vuex.Store({<br> state: {<br> user: {<br> name: window.localStorage.getItem('user' || '[]') == null ? '未登录' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,<br> userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface<br> }<br> },<br> mutations: {<br> login(state, user){<br> state.user = user;<br> window.localStorage.setItem('user', JSON.stringify(user));<br> },<br> logout(state){<br> window.localStorage.removeItem('user');<br> }<br> }<br>});<br></span>
In order to reduce Trouble, the data after the user successfully logs in will be saved in localStorage (to prevent data loss after the user presses F5 to refresh), stored in the form of a string, and then converted to json when retrieved. When the user logs out, clear the data in localStorage.
In the permission management module, this is the core of the front-end.
After the user successfully logs in and before entering the home page, he sends a request to the server to obtain the current menu information and For component information, the server returns a json string based on the role of the current user and the resources corresponding to the role. The format is as follows:
<span style="font-size: 14px;">[<br> {<br> "id": 2,<br> "path": "/home",<br> "component": "Home",<br> "name": "员工资料",<br> "iconCls": "fa fa-user-circle-o",<br> "children": [<br> {<br> "id": null,<br> "path": "/emp/basic",<br> "component": "EmpBasic",<br> "name": "基本资料",<br> "iconCls": null,<br> "children": [],<br> "meta": {<br> "keepAlive": false,<br> "requireAuth": true<br> }<br> },<br> {<br> "id": null,<br> "path": "/emp/adv",<br> "component": "EmpAdv",<br> "name": "高级资料",<br> "iconCls": null,<br> "children": [],<br> "meta": {<br> "keepAlive": false,<br> "requireAuth": true<br> }<br> }<br> ],<br> "meta": {<br> "keepAlive": false,<br> "requireAuth": true<br> }<br> }<br>]<br></span>
After the front end gets this string , do two things: 1. Dynamically add json to the current route; 2. Save the data to the store, and then each page renders the menu based on the data in the store.
The core idea is not difficult. Let’s take a look at the implementation steps.
This is very important.
Some friends may ask why this is so difficult. Isn’t it enough to request after logging in successfully? Yes, it is possible to request menu resources after successful login. After the request is received, we will save it in the store for next time use. However, there will be another problem. If the user clicks on a certain sub-child after successfully logging in, Page, enter the sub-page, and then press F5 to refresh. At this time, it is GG, because the data in the store is gone after F5 is refreshed, and we only requested the menu resources once when the login is successful. There are two ideas to solve this problem: 1. Don’t save the menu resources to the store, but save them to localStorage, so that the data is still there even after F5 refreshes; 2. Directly in the mounted method of each page, go Load menu resources once.
Since the menu resources are very sensitive, it is best not to save them locally, so I abandoned option 1, but the workload of option 2 was a bit heavy, so I took a step to save it locally. The way to simplify it is to use the navigation guard in routing.
My specific implementation is as follows. First, create a routes array in the store, which is an empty array. Then turn on the routing global guard, as follows:
<span style="font-size: 14px;">router.beforeEach((to, from, next)=> {<br> if (to.name == 'Login') {<br> next();<br> return;<br> }<br> var name = store.state.user.name;<br> if (name == '未登录') {<br> if (to.meta.requireAuth || to.name == null) {<br> next({path: '/', query: {redirect: to.path}})<br> } else {<br> next();<br> }<br> } else {<br> initMenu(router, store);<br> next();<br> }<br> }<br>)<br></span>
The code here is very short, let me give a simple explanation:
1. If the page you want to go to is the login page, this Nothing to say, just go ahead.
2.如果不是登录页面的话,我先从store中获取当前的登录状态,如果未登录,则通过路由中meta属性的requireAuth属性判断要去的页面是否需要登录,如果需要登录,则跳回登录页面,同时将要去的页面的path作为参数传给登录页面,以便在登录成功之后跳转到目标页面,如果不需要登录,则直接过(事实上,本项目中只有Login页面不需要登录);如果已经登录了,则先初始化菜单,再跳转。
初始化菜单的操作如下:
<span style="font-size: 14px;">export const initMenu = (router, store)=> {<br> if (store.state.routes.length > 0) {<br> return;<br> }<br> getRequest("/config/sysmenu").then(resp=> {<br> if (resp && resp.status == 200) {<br> var fmtRoutes = formatRoutes(resp.data);<br> router.addRoutes(fmtRoutes);<br> store.commit('initMenu', fmtRoutes);<br> }<br> })<br>}<br>export const formatRoutes = (routes)=> {<br> let fmRoutes = [];<br> routes.forEach(router=> {<br> let {<br> path,<br> component,<br> name,<br> meta,<br> iconCls,<br> children<br> } = router;<br> if (children && children instanceof Array) {<br> children = formatRoutes(children);<br> }<br> let fmRouter = {<br> path: path,<br> component(resolve){<br> if (component.startsWith("Home")) {<br> require(['../components/' + component + '.vue'], resolve)<br> } else if (component.startsWith("Emp")) {<br> require(['../components/emp/' + component + '.vue'], resolve)<br> } else if (component.startsWith("Per")) {<br> require(['../components/personnel/' + component + '.vue'], resolve)<br> } else if (component.startsWith("Sal")) {<br> require(['../components/salary/' + component + '.vue'], resolve)<br> } else if (component.startsWith("Sta")) {<br> require(['../components/statistics/' + component + '.vue'], resolve)<br> } else if (component.startsWith("Sys")) {<br> require(['../components/system/' + component + '.vue'], resolve)<br> }<br> },<br> name: name,<br> iconCls: iconCls,<br> meta: meta,<br> children: children<br> };<br> fmRoutes.push(fmRouter);<br> })<br> return fmRoutes;<br>}<br></span>
在初始化菜单中,首先判断store中的数据是否存在,如果存在,说明这次跳转是正常的跳转,而不是用户按F5或者直接在地址栏输入某个地址进入的。否则就去加载菜单。拿到菜单之后,首先通过formatRoutes方法将服务器返回的json转为router需要的格式,这里主要是转component,因为服务端返回的component是一个字符串,而router中需要的却是一个组件,因此我们在formatRoutes方法中动态的加载需要的组件即可。数据格式准备成功之后,一方面将数据存到store中,另一方面利用路由中的addRoutes方法将之动态添加到路由中。
最后,在Home页中,从store中获取菜单json,渲染成菜单即可,相关代码可以在<span style="font-size: 14px;">Home.vue</span>
中查看,不赘述。
OK,如此之后,不同用户登录成功之后就可以看到不同的菜单了。
相关推荐:
The above is the detailed content of Detailed explanation of dynamically loading Vue component instances in the permission management module. For more information, please follow other related articles on the PHP Chinese website!