preface
The content of the first stage of the back-end part has been almost completed, and the front-end part has not officially started. During this period of time, I will first turn to the front-end part, and continue to add follow-up content with the front-end scaffolding built before. This article focuses on the idea of front-end login and routing modularity, and in passing looks at the use of custom ICONS for the elementui-Admin footreback.
The login
As a background management system, the login module is indispensable. Here is a brief description of the login module needs to do and matters needing attention.
User state
- Not logged in
Users who are not logged in will be redirected to the login page if they visit all pages.
- Is logged in
Users who have logged in can view only the menus and buttons of their own rights and perform operations on them.
User status storage
After the front and back ends are separated, logged in users need to store session information locally. Cookies are commonly used to store tokens. Cookies are also used here in this framework. Of course, in addition to using Cookies, you can also use Windows. LocalStorage in H5, etc.
Clearing User Status
If a user has logged in, the login status of the user is cleared in the following cases
- Users voluntarily quit;
- The interface returned the token expiration status code. Procedure
In both cases, the code is called to clear the local session information and redirect to the login page to re-log in.
Login interface document
Requested address:
{{api_base_url}}/sys/login
Data type:
application/json
Example request:
{
"password": "123456"."userName": "admin"
}
Copy the code
Sample response:
{" code ": 0, / / return a status code 0, successful" MSG ", "login succeeds", / / the message description "data" : {" token ": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtbGRvbmciLCJleHAiOjE1OTI0OTIyMDEsInVzZXJOYW1lIjoiYWRtaW4iLCJpYXQiOjE1OT I0ODUwMDEsInVzZXJJZCI6MX0. HldmyCcL2EV8rtIeIiYsei963Cb3qIDHJRMOYo0iXkU ", / / temporary token, after logging in, other interfaces need to carry the parameter "userId" : 1, // user id "userName": "admin", // userName" realName": "avatar": "", // user avatar": "accessList": [], // Permission identifier "menuList": [] // menu set}}Copy the code
The login module involves modified files
src/views/login.vue
Login page, login form, code snippet.
handleLogin() {
// Do form validation here
this.$refs.loginForm.validate(valid= > {
if (valid) {
this.loading = true
/ / check through, calls to action - > user/login, corresponding to the SRC/store/modules/user. The action of js. The login method
this.$store.dispatch('user/login'.this.loginForm).then((a)= > {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch((a)= > {
this.loading = false})}else {
console.log('error submit!! ')
return false}})}Copy the code
src/store/modules/user.js
Vue state management, code snippets
const getDefaultState = (a)= > {
return {
token: getToken(),
name: ' '.avatar: ' '}}const state = getDefaultState()
const mutations = {
RESET_STATE: (state) = > {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) = > {
state.token = token
},
SET_NAME: (state, name) = > {
state.name = name
},
SET_AVATAR: (state, avatar) = > {
state.avatar = avatar
}
}
const actions = {
// user login
login({ commit }, userInfo) {
// this.$store.dispatch('user/login', this.loginform)
const { username, password } = userInfo
return new Promise((resolve, reject) = > {
// call SRC/API /user.js login
login({ username: username.trim(), password: password }).then(response= > {
const { data } = response
/ / set the token
commit('SET_TOKEN', data.token)
// Set cookies. This calls the setToken method of the SRC /utils/auth.js file
setToken(data.token)
resolve()
}).catch(error= > {
reject(error)
})
})
},
// Get user's personal information
getInfo({ commit, state }) {
return new Promise((resolve, reject) = > {
getInfo(state.token).then(response= > {
const { data } = response
if(! data) { reject('Verification failed, please Login again.')}// Here the backend returns information for modification
const { userName, avatar } = data
commit('SET_NAME', userName)
commit('SET_AVATAR', avatar)
resolve(data)
}).catch(error= > {
reject(error)
})
})
},
// User logs in to the system
logout({ commit, state }) {
return new Promise((resolve, reject) = > {
logout(state.token).then((a)= > {
removeToken() // must remove token first
resetRouter()
commit('RESET_STATE')
resolve()
}).catch(error= > {
reject(error)
})
})
},
Copy the code
src/utils/auth.js
Operations related to cookies
import Cookies from 'js-cookie'
const TokenKey = 'vue_admin_template_token'
/ / access token
export function getToken() {
return Cookies.get(TokenKey)
}
/ / set the token
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
/ / delete the token
export function removeToken() {
return Cookies.remove(TokenKey)
}
Copy the code
src/api/user.js
The interface for logging in, logging out, and obtaining personal information can be modified according to the back-end interface.
import request from '@/utils/request'
/ / login
export function login(data) {
return request({
url: '/sys/login'.method: 'post'.data: {
// We need to modify the input parameter username-> username
userName: data.username,
password: data.password
}
})
}
// Get user's personal information
export function getInfo(token) {
return request({
url: '/sys/user/info'.method: 'post'})}// Log in to the system
export function logout() {
return request({
url: '/sys/logout'.method: 'post'})}Copy the code
src/utils/request.js
HTTP request tool class, here to do global request header, request parameters, return data processing
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config= > {
// do something before request is sent
if (store.getters.token) {
// If there is a token, put it in the request header
X-token -> auth-token
config.headers['Auth-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
// do global return data processing here
response => {
const res = response.data
// If the status code is not 0, it is abnormal.
if(res.code ! = =0) {
Message({
message: res.msg || 'Server exception'.type: 'error'.duration: 5 * 1000
})
// The status code here can be modified according to the back-end status code
if (res.code === 401) {
// to re-login
MessageBox.confirm('You have logged out and will leave this page, are you sure to log out? ', {
confirmButtonText: 'Log in again'.cancelButtonText: 'cancel'.type: 'warning'
}).then((a)= > {
store.dispatch('user/resetToken').then((a)= > {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))}else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error'.duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
Copy the code
Routing modularization
Scaffold made most of the functionality, but for the routing of modularization, and functional module directory hierarchy or not, this needs us to according to the situation of the project, the following is my own hierarchical scheme, here only involves routing and page, the next talk about specific CURD sample will be complete when stratification.
The directory structure
├─ SRC/Router ├── CMS. Router. Js Content Management Route ├─ Index.js Route Main Entry ├─ oms.router Sys.router. Js System Management ├── all views/ Modules ├─ article ├─ vue ├─ class ├─ vue ├─ Vue ├── vue ├─ vue ├─ vue ├─ PMS ├─ brand ├─ index. Vue ├─ product ├─ index. Vue ├─ dict ├─ Vue ├── all, all, all, all, all, all, all, all, all, allCopy the code
The above modules are not meant to be implemented immediately, but to explain routing modularity and page modularity.
The sample
src/router/sys.router.js
import Layout from '@/layout'
export default[{path: '/sys'.// The modular identifier is consistent with the background menu management permission identifier
name: 'sys'.meta: {
icon: 'sys'.title: 'System Settings'.access: ['admin'.'sys'].notCache: true.showAlways: true
},
component: Layout,
children: [{path: '/sys/menu/index'.name: 'sys:menu:index'.// Consistent with the menu management permission identifier
meta: {
icon: ' '.title: 'Menu Management'.access: ['admin'.'sys:menu:index'].notCache: true
},
component: (resolve) = > {
import('@/views/modules/sys/menu/index.vue').then(m= > {
resolve(m)
})
}
},
{
path: '/sys/user/index'.name: 'sys:user:index'.meta: {
icon: ' '.title: 'User Management'.access: ['admin'.'sys:user:index'].notCache: true
},
component: (resolve) = > {
import('@/views/modules/sys/user/index.vue').then(m= > {
resolve(m)
})
}
},
{
path: '/sys/role/index'.name: 'sys:role:index'.meta: {
icon: ' '.title: 'Role Management'.access: ['admin'.'sys:role:index'].notCache: true
},
component: (resolve) = > {
import('@/views/modules/sys/role/index.vue').then(m= > {
resolve(m)
})
}
},
{
path: '/sys/dict/index'.name: 'sys:dict:index'.meta: {
icon: ' '.title: 'Dictionary Management'.access: ['admin'.'sys:dict:index'].notCache: true
},
component: (resolve) = > {
import('@/views/modules/sys/dict/index.vue').then(m= > {
resolve(m)
})
}
}
]
}
]
Copy the code
src/router/index.js
Route management main entry, where webpack_require is used to dynamically load routing files
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/** * Note: sub-menu only appear when route children.length >= 1 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html * * hidden: true if set true, item will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu * if not set alwaysShow, when item has more than one children route, * it will becomes nested mode, otherwise not show the root menu * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb * name:'router-name' the name is used by
(must set!!!) * meta : { roles: ['admin','editor'] control the page roles (you can set multiple roles) title: 'title' the name show in sidebar and breadcrumb (recommend set) icon: 'svg-name' the icon show in the sidebar breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) activeMenu: '/example/list' if set path, the sidebar will highlight the path you set } */
/** * constantRoutes * a base page that does not have permission requirements * all roles can be accessed */
export const constantRoutes = [
{
path: '/login'.component: (a)= > import('@/views/login/index'),
hidden: true
},
{
path: '/ 404'.component: (a)= > import('@/views/404'),
hidden: true
},
{
path: '/'.component: Layout,
redirect: '/dashboard'.hidden: true.children: [{
path: 'dashboard'.name: 'Dashboard'.component: (a)= > import('@/views/dashboard/index'),
meta: { title: 'home'.icon: 'dashboard'}}}]]/** * webpack_require dynamically loads routing files */
const routersFiles = require.context('/'.true, /\.js$/)
const routerList = []
const routers = routersFiles.keys().reduce((modules, routerPath) = > {
// set './app.js' => 'app'
const routerName = routerPath.replace(/^\.\/(.*)\.\w+$/.'$1')
const value = routersFiles(routerPath)
if(routerName ! = ='index') { routerList.push(... value.default) }return routers
}, {})
Router. AddRoutes (accessRoutes) */
export const asyncRoutes = [
... routerList,
// The 404 page must be placed at the end of the page, otherwise asynchronous loading, refresh page is not found directly to constantRoutes 404
{ path: The '*'.redirect: {
name: 'm404'
}, hidden: true}]const createRouter = (a)= > new Router({
// mode: 'history', // require service support
scrollBehavior: (a)= > ({ y: 0 }),
routes: [
... constantRoutes,
// Because the permission has not been done, here is directly placed here, later when the permission is done, then modify. asyncRoutes ] })const router = createRouter()
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
Copy the code
Route interceptor
The route interceptor, similar to the request interceptor, is placed in the scaffolding
src/permission.js
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
// enter page-front interception
router.beforeEach(async(to, from, next) => {
// Progress bar starts
NProgress.start()
// Reset the page title
document.title = getPageTitle(to.meta.title)
// Obtain the token to check whether you have logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// If you have logged in and it is a login page, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
// Check whether the user information exists. If yes, go to the page
if (hasGetUserInfo) {
next()
} else {
try {
// User information does not exist and needs to be retrieved
await store.dispatch('user/getInfo')
// If yes, the page is displayed
next()
} catch (error) {
// If the user information fails to be obtained, the session information is deleted and the login page is displayed
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login? redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/ * no token * /
if(whiteList.indexOf(to.path) ! = =- 1) {
// If the page is whitelist, you can enter the page
next()
} else {
// If it is not whitelisted, jump to the login page
next(`/login? redirect=${to.path}`)
NProgress.done()
}
}
})
// Enter the page
router.afterEach((a)= > {
// Progress bar completed
NProgress.done()
})
Copy the code
About custom ICONS
The custom ICONS used in the left menu are defined in SVG files, which can be obtained from Ali’s vector icon library.
www.iconfont.cn/
Directory for Storing ICONS
src/icons/svg/xxxx.svg
rendering
summary
This paper only briefly introduces the login module and routing modular layering, the basic layering idea is borrowed from the back end. The permission section is not expanded in this article because it involves too much content. A follow-up article will be devoted to explain in detail the permission management after the separation of the front and back ends.
Project source code address
- The back-end
Gitee.com/mldong/mldo…
- The front end
Gitee.com/mldong/mldo…
Related articles
Create a suitable for their own rapid development framework – the pilot
Build a suitable for their own rapid development framework – front-end scaffolding