在线预览

https://github.com/PanJiaChen/vue-element-admin/blob/master/README.zh-CN.md       (总连接)

 使用文档

一、权限问题

1.settings.js 文件


  1.showSettings: false,//右边设置 是否关闭

  2.tagsView: false,导航栏是否关闭

  3.fixedHeader: false,

  4.sidebarLogo: true,左边菜单顶部是否显示标题和logo

 2.登录逻辑 权限控制  代码分析

A. 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', '/auth-redirect'] // no redirect whitelist 没有重定向白名单

router.beforeEach(async (to, from, next) => {
	// 开始进度条
	NProgress.start()

	// 设置页面标题
	document.title = getPageTitle(to.meta.title)

	// 确定用户是否已登录
	const hasToken = getToken()

	if (hasToken) {
		if (to.path === '/login') {
			// 如果已登录,则重定向到主页
			next({ path: '/' })
			NProgress.done()
		} else {
			// determine whether the user has obtained his permission roles through getInfo
			const hasRoles = store.getters.roles && store.getters.roles.length > 0
			if (hasRoles) {
				//有用户信息
				next()
			} else {
				//无用户信息
				try {
					// 获得用户信息 实际是请求用户信息后返回,这里是模拟数据,直接从store中取
					// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
					const { roles } = await store.dispatch('user/getInfo')

					// generate accessible routes map based on roles  
                    //方法generateRoutes在store/modules/permission.js 
					const accessRoutes = await store.dispatch('permission/generateRoutes', roles)//生成可访问的路由表

					// dynamically add accessible routes
					router.addRoutes(accessRoutes) //动态添加可访问路由表

					// hack method to ensure that addRoutes is complete
					// set the replace: true, so the navigation will not leave a history record
					next({ ...to, replace: true })  //hack方法 确保addRoutes已完成 
				} catch (error) {
					//  删除token,进入登录页面重新登录
					await store.dispatch('user/resetToken')
					Message.error(error || 'Has Error')
					next(`/login?redirect=${to.path}`)
					NProgress.done()
				}
			}
		}
	} else {
		/* has no token*/

		if (whiteList.indexOf(to.path) !== -1) {
			// in the free login whitelist, go directly 在免费登录白名单,直接去
			next()
		} else {
			// other pages that do not have permission to access are redirected to the login page.
			//没有访问权限的其他页面被重定向到登录页面。
			next(`/login?redirect=${to.path}`)
			NProgress.done()
		}
	}
})

router.afterEach(() => {
	// finish progress bar完成进度条
	NProgress.done()
})
/*
有token:再看看是不是去登录页的,登录页肯定不能拦截的,如果是登录页就直接放行。如果不是登录页,就要看看本地有没有用户信息,看看cookie中有没有用户信息(不一定是token,也可能是localstorage)。如果有用户信息,放行。如果没有用户信息,就调用接口去获取登录信息,然后后面还有一点代码,涉及到了动态添加路由(这里先说到这,后面具体说动态添加权限路由的事)。获取到用户信息后放行。如果在获取用户信息的过程中报错,则回到登录页

无token:先看看用户要进入的页面是不是在白名单内,一般登录、注册、忘记密码都是在白名单内的,这些页面,在无token的情况下也是直接放行。如果不在白名单内,滚回登录页。
 */

2.router

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* 这是框 */
import Layout from '@/layout'

/*添加的路由模块 */
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'

/**
 * Note: sub-menu only appear when route children.length >= 1
 * 注意: 子菜单只在路由子菜单时长度> = 1的时候出现
 * 参考网址: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 *
 * hidden: true                   如果设置为true,项目将不会显示在侧栏中(默认为false)
 * alwaysShow: true               如果设置为true,将始终显示根菜单
 *                                如果不设置alwaysShow, 当项目有多个子路由时,它将成为嵌套模式,否则不显示根菜单
 * redirect: noRedirect           如果设置noRedirect,则不会在面包屑中重定向
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
    roles: ['admin','editor']    控制页面角色(可以设置多个角色)'admin','editor'
    title: 'title'               名称显示在侧边栏和面包屑(推荐集)
    icon: 'svg-name'             图标显示在侧栏中
    breadcrumb: false            如果设置为false,则该项将隐藏在breadcrumb中(默认为true)
    activeMenu: '/example/list'  如果设置路径,侧栏将突出显示您设置的路径
  }
 */

/**
 * constantRoutes
 * a base page that does not have permission requirements
 * all roles can be accessed
 *	没有权限要求的基本页
 * 所有角色都可以访问
 * 不需要动态判断权限的路由
 */
export const constantRoutes = [
	{
		path: '/redirect',
		component: Layout,
		hidden: true,
		children: [
			{
				path: '/redirect/:path*',
				component: () => import('@/views/redirect/index')
			}
		]
	},
	{
		path: '/login',
		component: () => import('@/views/login/index'),
		hidden: true
	},
	{
		path: '/auth-redirect',
		component: () => import('@/views/login/auth-redirect'),
		hidden: true
	},
	{
		path: '/404',
		component: () => import('@/views/error-page/404'),
		hidden: true
	},
	{
		path: '/401',
		component: () => import('@/views/error-page/401'),
		hidden: true
	},
	{
		path: '/',
		component: Layout,
		redirect: '/dashboard',
		children: [
			{
				path: 'dashboard',
				component: () => import('@/views/dashboard/index'),
				name: 'Dashboard',
				meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
			}
		]
	},
	{
		path: '/documentation',
		component: Layout,
		children: [
			{
				path: 'index',
				component: () => import('@/views/documentation/index'),
				name: 'Documentation',
				meta: { title: 'Documentation', icon: 'documentation', affix: true }
			}
		]
	}
    ...
]

/**
 * asyncRoutes
 * the routes that need to be dynamically loaded based on user roles
 * 异步挂载的路由
 * 动态需要根据权限加载的路由表 
 */
export const asyncRoutes = [
	{
		path: '/permission',
		component: Layout,
		redirect: '/permission/page',
		alwaysShow: true, // will always show the root menu
		name: 'Permission',
		meta: {
			title: 'Permission',
			icon: 'lock',
			roles: ['admin', 'editor'] // you can set roles in root nav
		},
		children: [
			{
				path: 'page',
				component: () => import('@/views/permission/page'),
				name: 'PagePermission',
				meta: {
					title: 'Page Permission',
					roles: ['admin'] // or you can only set roles in sub nav
				}
			},
			{
				path: 'directive',
				component: () => import('@/views/permission/directive'),
				name: 'DirectivePermission',
				meta: {
					title: 'Directive Permission'
					// if do not set roles, means: this page does not require permission
				}
			},
			{
				path: 'role',
				component: () => import('@/views/permission/role'),
				name: 'RolePermission',
				meta: {
					title: 'Role Permission',
					roles: ['admin']
				}
			}
		]
	},
    ...
	// 404页面必须放在最后
	{ path: '*', redirect: '/404', hidden: true }
]
//创建路由
const createRouter = () => new Router({
	// mode: 'history', // require service support
	scrollBehavior: () => ({ y: 0 }),
	routes: constantRoutes
})

const router = createRouter()
// 重置路由
//参考网址: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
	const newRouter = createRouter()
	router.matcher = newRouter.matcher // reset router
}

export default router

/***
 *
 *  constantRoutes 和 asyncRoutes这2个是routes中分两块路由配置,一块是固定的,无权限的路由配置,也就是不管是管理员身份,还是超级管理员身份,都会显示的路由配置。
第二块是,带权限的路由配置,根据用户权限来显示侧边栏。注意,带权限的配置里的meta中都有role项,代表是权限
 */

参考材料

二、业务操作

1.主菜单的字体图标更改

首先可以去阿里下载svg图片,放在src/icons/svg的文件里,然后在路由菜单中icon设置svg的名称(icon: 'reconciliation')

2.基于vue-admin-template-master框架开发 :权限控制

      一般说来,权限管理可以分为两种情况:第一种为页面级访问权限(菜单权限),第二种为数据级操作权限(按钮权限)。第一种情况是非常常见的,即用户是否能够看到页面;第二种情况是用户能否对数据进行增删改查等操作。
     前端的权限控制实质上就是用于展示,让操作变得更加友好,真正的安全实际上是由后端控制的。

登录拦截,动态路由

A.router 路由配置: constantRoutes 和 asyncRoutes设置

//公共路由
export const constantRoutes = [
	{
		path: '/login',
		component: () => import('@/views/login/index'),
		hidden: true
	},
	...
	{
		path: 'external-link',
		component: Layout,
		children: [
			{
				path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
				meta: { title: 'External Link', icon: 'link' }
			}
		]
	},


]

//动态路由
export const asyncRoutes = [
	{
		path: '/permission',
		component: Layout,
		redirect: '/permission/page',
		alwaysShow: true, // will always show the root menu
		name: 'Permission',
		meta: {
			title: '权限测试页',
			icon: 'lock',
			roles: ['admin', 'editor'] // you can set roles in root nav
		},
		children: [
			{
				path: 'role',
				component: () => import('@/views/permission/role'),
				name: 'RolePermission',
				meta: {
					title: '角色权限',
					roles: ['admin']
				}
			},
			{
				path: 'page',
				component: () => import('@/views/permission/page'),
				name: 'PagePermission',
				meta: {
					title: '页面权限',
					roles: ['admin'] // or you can only set roles in sub nav
				}
			},
			{
				path: 'directive',
				component: () => import('@/views/permission/directive'),
				name: 'DirectivePermission',
				meta: {
					title: '指令权限'
					// if do not set roles, means: this page does not require permission
				}
			}
		]
	},
	// 404 一定要放最后面
	{ path: '*', redirect: '/404', hidden: true }

]

B.permission.js 访问  获取动态的路由

router.beforeEach(async (to, from, next) => {
    // start progress bar
    NProgress.start()

    // set page title
    document.title = getPageTitle(to.meta.title)

    // determine whether the user has logged in
    const hasToken = localStorage.getItem('token');
    if (hasToken) {
        if (to.path === '/login') {
            localStorage.removeItem('token');
            // if is logged in, redirect to the home page
            next(`/login`)
            NProgress.done()
        } else {
            //这个是获取按钮权限
            store.commit('permission/SET_PERMISSIONS', to.meta.permission);
            //这个是state里面的路由列表
            const hasRoutes = store.getters.permission_routes && store.getters.permission_routes.length > 4;
            if (hasRoutes) {
                next()
                // 全局变量的数据加载
            } else {
                try {
                    const accessRoutes = await store.dispatch('permission/generateRoutes')
                    router.addRoutes(accessRoutes)//动态加载路由
                    next({ ...to, replace: true })  //hack方法 确保addRoutes已完成

                } catch (error) {
                    // remove token and go to login page to re-login
                    // await store.dispatch('user/resetToken')
                    localStorage.removeItem('token');
                    // Message.error(error || 'Has Error')
                    next(`/login`)//next(`/login?redirect=${to.path}`)
                    NProgress.done()
                }
            }
        }
    } else {
        /* has no token*/

        if (whiteList.indexOf(to.path) !== -1) {
            // in the free login whitelist, go directly
            next()

        } else {
            localStorage.removeItem('token');
            // other pages that do not have permission to access are redirected to the login page.
            next(`/login`)
            NProgress.done()
        }
    }
})

router.afterEach(() => {
    // finish progress bar
    NProgress.done()
})

在store请求接口 获取路由数据 :store/modules/permission.js

import { asyncRoutes, constantRoutes } from '@/router'
import { getPower } from '@/api/user'

/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
    if (route.meta && route.meta.roles) {
        return roles.some(role => route.meta.roles.includes(role))
    } else {
        return true
    }
}


/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 */
export function formatAsyncRoutes(routes) {
    const res = []

    routes.forEach(route => {
        const tmp = { ...route }
        if (tmp.children) {
            tmp.children = formatAsyncRoutes(tmp.children)
        }
        if (tmp.componentUrl) {
            //这是webpack4 兼容 
            tmp.component = (resolve) => require([`@/${tmp.componentUrl}.vue`], resolve)
            // import(`@/${tmp.componentUrl}.vue`)
        } else {
            tmp.component = () => import('@/layout/index.vue')
        }

        res.push(tmp)
    })

    return res
}

const state = {
    routes: [],
    addRoutes: [],
    permissions: [] //这是页面的所有按钮权限
}


const mutations = {
    SET_ROUTES: (state, routes) => {
        state.addRoutes = routes
        state.routes = constantRoutes.concat(routes, asyncRoutes)
    },
    SET_PERMISSIONS: (state, permissions) => {
        state.permissions = permissions;
    }
}

const actions = {
    /**
     * 
     *roles是用户所带的权限
    这个方法就是把动态路由配置里符合用户权限的配置筛选出来,
    组成一个数组,然后,和固定路由合并到了一起,存到了vuex中
     */
    generateRoutes({ commit }) {
        return new Promise((resolve, reject) => {
            getPower().then(response => {
                let accessedRoutes = [];
                let routerList = response.payload.routerList;
                if (routerList.length > 0) {
                    accessedRoutes = formatAsyncRoutes(routerList)
                }
                 // 生成左侧导航菜单
                commit('SET_ROUTES', accessedRoutes)
                resolve(accessedRoutes);
            }).catch(error => {
                reject(error)
            })
        })
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}

菜单路由的数据:大概如以下:

/*
    JSON注解
    {
        "path": "/reconciliation", 一级菜单path
        "name": "Reconciliation", 一级菜单路由名称(ID)
        "componentUrl": "Layout", 一级菜单父组件
        "meta": { 当没有二级菜单时,此属性为空
            "title": "对账汇总", 
            "icon": "reconciliation"
        },
        "children": [{
            "path": "index", 二级菜单path
            "name": "Index", 二级菜音路由名称(ID)
            "componentUrl": "views/reconciliaction/index", 二级菜单路径
            "meta": {
                "title": "对账汇总", 二级菜单显示文字
                "icon": "reconciliation", 二级菜单显示图标
                "permissions": [
                    "facilitator", "facilitator_btn", "payment", "payment_btn" 二级菜单页面对应按钮权限
                ],
                "activeMenu": "/hospitalManagement/index" // 这个字段是为了隐藏路由的时候,可以指定主菜单显示高亮
            }
        }]
    }
*/
"routerList":[{
    "path": "/reconciliation",
    "weigth": 1, //这是后端设置的
    "children": [{
        "path": "index",
        "weigth": 1, //这是后端设置的
        "meta": {
            "icon": "reconciliation",
            "permission": ["facilitator", "facilitator_btn", "payment", "payment_btn"],
            "title": "对账汇总"
        },
        "name": "Index",
        "menuType": "1", //这是后端设置的
        "id": 1, //这是后端设置的
        "componentUrl": "views/reconciliation/index"
    }],
    "meta": {},
    "name": "Reconciliation",
    "menuType": "0", //这是后端设置的
    "id": 27 //这是后端设置的
}, {
    "path": "/abnormalAccount",
    "weigth": 2,
    "children": [{
        "path": "index",
        "weigth": 2,
        "meta": {
            "icon": "abnormalAccount",
            "permission": ["flatAccount"],
            "title": "细表"
        },
        "name": "Index",
        "menuType": "1",
        "id": 2,
        "componentUrl": "views/abnormalAccount/index"
    }],
    "meta": {},
    "name": "AbnormalAccount",
    "menuType": "0",
    "id": 28
}, {
    "path": "/exceptionHandling",
    "weigth": 3,
    "children": [{
        "path": "index",
        "weigth": 3,
        "meta": {
            "icon": "exceptionHandling",
            "permission": ["long", "long_btn", "short", "short_btn", "refund", "refund_btn"],
            "title": "处理"
        },
        "name": "Index",
        "menuType": "1",
        "id": 3,
        "componentUrl": "views/exceptionHandling/index"
    }],
    "meta": {},
    "name": "ExceptionHandling",
    "menuType": "0",
    "id": 29
}, {
    "path": "/reconciliationAudit",
    "weigth": 4,
    "children": [{
        "path": "index",
        "weigth": 4,
        "meta": {
            "icon": "reconciliationAudit",
            "permission": ["account", "account_btn", "record"],
            "title": "审核"
        },
        "name": "Index",
        "menuType": "1",
        "id": 4,
        "componentUrl": "views/reconciliationAudit/index"
    }],
    "meta": {},
    "name": "ReconciliationAudit",
    "menuType": "0",
    "id": 30
}, {
    "path": "/hospitalManagement",
    "weigth": 5,
    "children": [{
        "path": "index",
        "weigth": 5,
        "meta": {
            "icon": "hospitalManagement",
            "permission": ["account", "application"],
            "title": "医院管理"
        },
        "name": "Index",
        "menuType": "1",
        "id": 5,
        "componentUrl": "views/hospitalManagement/index"
    }, {
        "path": "roleSetting",
        "weigth": 27,
        "hidden": "true",
        "meta": {
            "activeMenu": "/hospitalManagement/index",
            "icon": "hospitalManagement",
            "permission": [],
            "title": "账号管理"
        },
        "name": "RoleSetting",
        "menuType": "1",
        "id": 33,
        "componentUrl": "views/hospitalManagement/roleSetting"
    }, {
        "path": "application",
        "weigth": 28,
        "hidden": "true",
        "meta": {
            "activeMenu": "/hospitalManagement/index",
            "icon": "hospitalManagement",
            "permission": [],
            "title": "应用管理"
        },
        "name": "Application",
        "menuType": "1",
        "id": 34,
        "componentUrl": "views/hospitalManagement/application"
    }],
    "meta": {
        "icon": "",
        "title": ""
    },
    "name": "HospitalManagement",
    "menuType": "0",
    "id": 31
}, {
    "path": "/orderManagement",
    "weigth": 6,
    "children": [{
        "path": "index",
        "weigth": 6,
        "meta": {
            "icon": "orderManagement",
            "permission": ["order", "his", "thirdParty", "ftp"],
            "title": "订单管理"
        },
        "name": "Index",
        "menuType": "1",
        "id": 6,
        "componentUrl": "views/orderManagement/index"
    }],
    "meta": {},
    "name": "OrderManagement",
    "menuType": "0",
    "id": 32
}]

 把值存在state里面

store/index.js


import permission from './modules/permission'

const store = new Vuex.Store({
	modules: {
		permission
	},
	getters
})


store/getters.js


const getters = {
	roles: state => state.user.roles,//角色
	permission_routes: state => state.permission.routes,//动态路由
}
export default getters

C.渲染页面components /sidebar /index.vue

<sidebar-item
    v-for="route in permission_routes"
    :key="route.path"
    :item="route"
    :base-path="route.path"
/>


...mapGetters(["permission_routes"]),

(1).页面权限(菜单权限):通过roles: ['admin']这个来区别不同角色访问不同页面

(2).按钮控制权限: 通过permission: ['add', 'edit']这个来判断不同角色可以操作不同的按钮

在路由里面配置

export const asyncRoutes = [
	{
		path: '/btnPermission',
		component: Layout,
		children: [
			{
				path: 'index',
				name: 'btnPermission',
				component: () => import('@/views/btnPermission/index'),
				meta: { title: '按钮控制页面显示', icon: 'form', permission: ['add', 'edit'] }
			}
		]
	},
]

(1)v-hasPermissionBtn 适用与 按钮 ,tabs  

directive/hasPermission/index.js封装一个指令的函数

const hasPermissionBtn = {
	inserted(el, binding, vnode) {
		const { value } = binding;
		let permissionList = vnode.context.$route.meta.permission;
		if (value && value instanceof Array && value.length > 0) {
			const permissionRoles = value;

			const hasPermission = permissionList.some(role => {
				return permissionRoles.includes(role);
			})
			if (!hasPermission) {
				el.parentNode && el.parentNode.removeChild(el);
			}
		}
	}
}
const install = function (Vue) {
	Vue.directive('hasPermissionBtn', hasPermissionBtn)
}

if (window.Vue) {
	window['hasPermissionBtn'] = hasPermissionBtn
	Vue.use(install); // eslint-disable-line
}

hasPermissionBtn.install = install;

export default hasPermissionBtn

 views\btnPermission/index.vue:页面操作

<template>
	<div class="app-container">
		<el-button type="primary" v-hasPermissionBtn="['add']" @click="handleBtn('add')">新增</el-button>
		<el-button type="primary" v-hasPermissionBtn="['del']" @click="handleBtn('del')">删除</el-button>
		<el-button type="primary" v-hasPermissionBtn="['edit']" @click="handleBtn('edit')">修改</el-button>
	</div>
</template>

<script>
import hasPermissionBtn from "@/directive/hasPermission/index.js"; // 权限判断指令
export default {
	data() {
		return {};
	},
	computed: {},
	created() {},
	directives: {hasPermissionBtn},
	methods: {
		handleBtn(types) {
			this.$message({
				message: "该按钮的操作是" + types,
				type: "warning"
			});
		}
	}
};
</script>
<style lang="scss" scoped>
</style>

(2)v-permissionBtn="['account']"  适用 span   行内元素

1.directive/module/permission.js


import store from '@/store'

export default {
    componentUpdated(el, binding, vnode) {
        const { value } = binding
        const permissions = store.getters && store.getters.permissions

        if (value && value instanceof Array && value.length > 0) {
            const permissionRoles = value

            const hasPermission = permissions.some(role => {
                return permissionRoles.includes(role)
            })

            if (!hasPermission) {
                el.parentNode && el.parentNode.removeChild(el)
            }
        } else {
            throw new Error(`need permissions! Like v-permission="['update','edit']"`)
        }
    }
}

2.directive/directives.js

import defaultval from './module/defaultval'
import hotkey from './module/hotkey'
import permissionBtn from './module/permission'
const directives = {
    defaultval,
    hotkey,
    permissionBtn
}

export default directives


3.directive/index.js

import directive from './directives'

const importDirective = Vue => {
   
    /**
     * permissionBtn 权限指令
     * 指令用法:
     *  - 在需要控制按钮 级别权限的组件上使用 v-permissionBtn="['account_btn']" , 如下:
     *    <a-button v-permissionBtn="['add']">添加用户</a-button>
     *    <a-button v-permissionBtn="['del']">删除用户</a-button>
     *
     *  - 当前用户没有权限时,组件上使用了该指令则会被隐藏
     */
    Vue.directive("permissionBtn", directive.permissionBtn);
}

export default importDirective


4.main.js

import Vue from 'vue'

import importDirective from '@/directive';

/* 注册指令*/
importDirective(Vue);
组件中使用:
<span @click="handleUpdate(row,'roleSetting')" v-permissionBtn="['account']">账号管理</span>

 数据操作:

(1).账号权限 角色

A。新增角色

<el-form
    ref="rolesRef"
    class="dialogcnt_form query"
    :model="rolesRuleForm"
    :rules="rolesRules"
    label-width="90px"
    label-position="left"
>
    <el-form-item label="角色名称:" prop="name">
        <el-input v-model="rolesRuleForm.name" placeholder="请输入角色名称" maxlength="50" />
    </el-form-item>
    <el-form-item label="权限状态:" prop="delFlag">
        <el-select v-model="rolesRuleForm.delFlag" placeholder="请选择权限状态">
            <el-option label="正常" value="1"></el-option>
            <el-option label="禁用" value="0"></el-option>
        </el-select>
    </el-form-item>
    <div class="query_group">
        <div class="query_group-item powersRoles">
            <label>
                <i class="icon-red">*</i>权限列表:
            </label>
            <div class="powersRoles_cnt">
                <el-tree
                    v-model="rolesRuleForm.menuIds"
                    ref="rolesTree"
                    :check-on-click-node="true"
                    node-key="id"
                    :data="routesData"
                    :props="defaultProps"
                    show-checkbox
                    @check-change="treeCheckChange"
                    class="permission-tree"
                />
            </div>
        </div>
    </div>
</el-form>

data(){
    return{
        rolesRuleForm: {
            name: "",
            delFlag: 1,
            menuIds: []
        },
        rolesRules: {
            name: [
                {
                    required: true,
                    message: "请输入角色名称",
                    trigger: "blur"
                }
            ],
            delFlag: [
                {
                    required: true,
                    message: "请输入权限状态",
                    trigger: "change"
                }
            ]
        },
        routesData:[],//菜单路由的数据
        //这个 菜单列表
        defaultProps: {
            children: "children",
            label: "title"
        },
    }
}

methods: {
    //菜单权限 tree选中事件
    treeCheckChange() {
        this.rolesRuleForm.menuIds = this.$refs.rolesTree.getCheckedKeys();
    },
    handleSave(types) {
        this.$refs.rolesRef.validate(valid => {
            if (valid) {
                let params = Object.assign({}, this.rolesRuleForm);
                params.menuIds =
                    "33,34," +
                    this.$refs.rolesTree
                        .getCheckedKeys()
                        .toString(); //获取选中的节点id
                if (this.rolesRuleForm.menuIds == "") {
                        this.$message({
                            message: "请配置权限列表"
                        });
                        return false;
                    }

                    Api.addRole(params)
                        .then(res => {
                            this.$message({
                                message: "新增角色成功",
                                type: "success"
                            });
                           
                        })
                        .catch(err => {
                            this.userDialoading = false;
                        });
            } else {
                return false;
            }
        });
    }
}

 B. 按钮权限

数据:

"data": [{
			"path": "/reconciliation",
			"weigth": 1,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 1,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "facilitator",
					"weigth": 7,
					"hidden": "",
					"activeMenu": "",
					"name": "服务商厂商汇总",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 7,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "facilitator_btn",
					"weigth": 8,
					"hidden": "",
					"activeMenu": "",
					"name": "服务厂商汇总差错账明细权限(包含查看明细按钮及长/短款异常账快捷查看)",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 8,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "payment",
					"weigth": 9,
					"hidden": "",
					"activeMenu": "",
					"name": "支付渠道汇总",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 9,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "payment_btn",
					"weigth": 10,
					"hidden": "",
					"activeMenu": "",
					"name": "支付渠道汇总差错账明细权限(包含查看明细按钮及长/短款异常账快捷查看)",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 10,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "reconciliation",
				"checked": true,
				"menuType": "1",
				"id": 1,
				"title": "汇总",
				"componentUrl": "views/reconciliation/index"
			}],
			"name": "Reconciliation",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 27,
			"title": "",
			"componentUrl": ""
		}, {
			"path": "/abnormalAccount",
			"weigth": 2,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 2,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "flatAccount",
					"weigth": 11,
					"hidden": "",
					"activeMenu": "",
					"name": "列表操作栏平账处理权限",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 11,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "abnormalAccount",
				"checked": true,
				"menuType": "1",
				"id": 2,
				"title": "细表",
				"componentUrl": "views/abnormalAccount/index"
			}],
			"name": "AbnormalAccount",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 28,
			"title": "",
			"componentUrl": ""
		}, {
			"path": "/exceptionHandling",
			"weigth": 3,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 3,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "long",
					"weigth": 12,
					"hidden": "",
					"activeMenu": "",
					"name": "长款异常账",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 12,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "long_btn",
					"weigth": 13,
					"hidden": "",
					"activeMenu": "",
					"name": "长款异常账列表操作栏权限",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 13,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "short",
					"weigth": 14,
					"hidden": "",
					"activeMenu": "",
					"name": "短款异常账",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 14,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "short_btn",
					"weigth": 15,
					"hidden": "",
					"activeMenu": "",
					"name": "短款异常账列表操作栏权限",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 15,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "refund",
					"weigth": 16,
					"hidden": "",
					"activeMenu": "",
					"name": "退款异常处理",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 16,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "refund_btn",
					"weigth": 17,
					"hidden": "",
					"activeMenu": "",
					"name": "退款异常处理列表操作栏权限",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 17,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "exceptionHandling",
				"checked": true,
				"menuType": "1",
				"id": 3,
				"title": "处理",
				"componentUrl": "views/exceptionHandling/index"
			}],
			"name": "ExceptionHandling",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 29,
			"title": "",
			"componentUrl": ""
		}, {
			"path": "/reconciliationAudit",
			"weigth": 4,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 4,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "account",
					"weigth": 18,
					"hidden": "",
					"activeMenu": "",
					"name": "账本审核",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 18,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "account_btn",
					"weigth": 19,
					"hidden": "",
					"activeMenu": "",
					"name": "审核权限(包含一)",
					"icon": "",
					"checked": false,
					"menuType": "2",
					"id": 19,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "record",
					"weigth": 20,
					"hidden": "",
					"activeMenu": "",
					"name": "已审核列表记录",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 20,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "reconciliationAudit",
				"checked": true,
				"menuType": "1",
				"id": 4,
				"title": "对账审核",
				"componentUrl": "views/reconciliationAudit/index"
			}],
			"name": "ReconciliationAudit",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 30,
			"title": "",
			"componentUrl": ""
		}, {
			"path": "/hospitalManagement",
			"weigth": 5,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 5,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "account",
					"weigth": 21,
					"hidden": "",
					"activeMenu": "",
					"name": "帐号管理",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 21,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "application",
					"weigth": 22,
					"hidden": "",
					"activeMenu": "",
					"name": "应用配置",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 22,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "hospitalManagement",
				"checked": true,
				"menuType": "1",
				"id": 5,
				"title": "医院管理",
				"componentUrl": "views/hospitalManagement/index"
			}, {
				"path": "roleSetting",
				"weigth": 27,
				"hidden": "true",
				"activeMenu": "/hospitalManagement/index",
				"name": "RoleSetting",
				"icon": "hospitalManagement",
				"checked": true,
				"menuType": "1",
				"id": 33,
				"title": "账号管理",
				"componentUrl": "views/hospitalManagement/roleSetting"
			}, {
				"path": "application",
				"weigth": 28,
				"hidden": "true",
				"activeMenu": "/hospitalManagement/index",
				"name": "Application",
				"icon": "hospitalManagement",
				"checked": true,
				"menuType": "1",
				"id": 34,
				"title": "应用管理",
				"componentUrl": "views/hospitalManagement/application"
			}],
			"name": "HospitalManagement",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 31,
			"title": "",
			"componentUrl": ""
		}, {
			"path": "/orderManagement",
			"weigth": 6,
			"hidden": "",
			"activeMenu": "",
			"children": [{
				"path": "index",
				"weigth": 6,
				"hidden": "",
				"activeMenu": "",
				"children": [{
					"path": "order",
					"weigth": 23,
					"hidden": "",
					"activeMenu": "",
					"name": "订单记录",
					"icon": "",
					"checked": false,
					"menuType": "2",
					"id": 23,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "his",
					"weigth": 24,
					"hidden": "",
					"activeMenu": "",
					"name": "HIS明细账单",
					"icon": "",
					"checked": false,
					"menuType": "2",
					"id": 24,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "thirdParty",
					"weigth": 25,
					"hidden": "",
					"activeMenu": "",
					"name": "第三方账单列表",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 25,
					"title": "",
					"componentUrl": ""
				}, {
					"path": "ftp",
					"weigth": 26,
					"hidden": "",
					"activeMenu": "",
					"name": "FTP账单列表",
					"icon": "",
					"checked": true,
					"menuType": "2",
					"id": 26,
					"title": "",
					"componentUrl": ""
				}],
				"name": "Index",
				"icon": "orderManagement",
				"checked": true,
				"menuType": "1",
				"id": 6,
				"title": "订单管理",
				"componentUrl": "views/orderManagement/index"
			}],
			"name": "OrderManagement",
			"icon": "",
			"checked": true,
			"menuType": "0",
			"id": 32,
			"title": "",
			"componentUrl": ""
		}]

代码:

<div class="dialogcnt" style="margin-bottom: -55px">
    <el-form
        ref="deployRef"
        class="dialogcnt_form query"
        :model="deployRuleForm"
        :rules="deployRules"
        label-width="90px"
        label-position="left"
    >
        <div class="overflowAuto">
            <div class="query_group">
                <div class="query_group-item" style="margin-bottom:10px">
                    <label>
                        <i class="icon-red">*</i>角色名称:
                    </label>
                    <div class="item_cnt" style="margin-left: 30px;">{{deployRuleForm.name}}</div>
                </div>
            </div>
            <el-form-item label="权限状态:" prop="delFlag">
                <el-select v-model="deployRuleForm.delFlag" placeholder="请选择权限状态">
                    <el-option label="正常" value="1"></el-option>
                    <el-option label="禁用" value="0"></el-option>
                </el-select>
            </el-form-item>
            <div class="query_group">
                <div class="query_group-item powersRoles">
                    <label>
                        <i class="icon-red">*</i>权限:
                    </label>
                    <div class="powersRoles_cnt">
                        <div v-for="el in btnPower" :key="el.fatherId">
                            <div class="head">{{el.title}}</div>
                            <div class="checkboxFlx">
                                <el-checkbox-group class="checkboxFlx_box" v-model="btnPowerSelectValue">
                                    <el-checkbox
                                        v-for="item in el.children"
                                        :key="item.id"
                                        :checked="item.checked"
                                        :label="`${item.id}`"
                                        style="width:40%;"
                                    >{{item.name}}</el-checkbox>
                                </el-checkbox-group>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </el-form>
</div>
<span slot="footer" class="dialog-footer">
    <el-button type="primary" :loading="deployDialoading" @click="handleSave('deploy')">保存</el-button>
</span>

data(){
    return{
        deployRuleForm: {},
		deployRules: {
        delFlag: [
            {
                required: true,
                message: "请输入权限状态",
                trigger: "change"
            }
        ],
        btnPower: [], //页面按钮权限 数据列表显示
        btnPowerSelectValue: [], //按钮选中
    }
}
created(){
    this.getBtnPower()
}
methods: {
//按钮权限数据
    getBtnPower(){
    
        let payload = infos.payload.data;
        let btnPower = [];
        let menusIdAll = [];
        _.each(payload, function(element, index, list) {
            let elInfo =
                element.children[0] === undefined
                    ? {}
                    : element.children[0];
            let menusid = "";
            elInfo.fatherId = element.id;
            elInfo.fatherName = element.name;
            elInfo.selectValue = [];
            btnPower.push(elInfo);
            menusid =
                element.id +
                "," +
                (elInfo === undefined ? "" : elInfo.id);
            menusIdAll.push(menusid);
        });
        this.btnPower = btnPower;
    },
    handleSave(types) {
       this.$refs.deployRef.validate(valid => {
            if (valid) {
                let params = Object.assign({}, this.deployRuleForm);
                params.menuIds =
                    params.menusIdAll +
                    ",33,34," +
                    this.btnPowerSelectValue.toString();
                if (this.btnPowerSelectValue.toString() == "") {
                    this.$message({
                        message: "请先选择权限"
                    });
                    return false;
                }
                delete params.menusIdAll;
                this.deployDialoading = true;
                Api.updateRole(params)
                    .then(res => {
                        this.$message({
                            message: "修改权限配置成功",
                            type: "success"
                        });
                        this.deployVisible = false;
                        this.tableQuery.page = 1;
                        this.initTableData();
                        this.deployDialoading = false;
                        this.btnPowerSelectValue = [];
                        this.btnPower = [];
                    })
                    .catch(err => {
                        this.deployDialoading = false;
                    });
            } else {
                return false;
            }
        });
    }
}

3. 登录后默认显示第一个路由

permission.js


router.beforeEach((to, from, next) => {
    NProgress.start();
    if (getToken()) {
        to.meta.title && store.dispatch('settings/setTitle', to.meta.title);
        /* has token*/
        if (to.path === '/login') {
            next({ path: '/' });
            NProgress.done();
        } else {
            if (store.getters.roles.length === 0) {
                // 判断当前用户是否已拉取完user_info信息
                store
                    .dispatch('GetInfo')
                    .then(() => {
                        store.dispatch('GenerateRoutes').then(accessRoutes => {
                            let path = '/';
                            if (accessRoutes && accessRoutes[0].path != path) {
                                if (accessRoutes[0].children) {
                                    path =
                                        accessRoutes[0].path +
                                        '/' +
                                        accessRoutes[0].children[0].path;
                                } else {
                                    path = accessRoutes[0].path;
                                }
                            } else {
                                path = path + accessRoutes[0].children[0].path;
                            }
                            // 根据roles权限生成可访问的路由表
                            router.addRoutes(accessRoutes); // 动态添加可访问路由表
                            if (to.fullPath == '/' || to.fullPath == '/index') {
                                next({ path, replace: true });
                            } else {
                                next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
                            }
                        });
                    })
                    .catch(err => {
                        store.dispatch('LogOut').then(() => {
                            Message.error(err);
                            next({ path: '/' });
                        });
                    });
            } else {
                next();
            }
        }
    } else {
        // 没有token
        if (whiteList.indexOf(to.path) !== -1) {
            // 在免登录白名单,直接进入
            next();
        } else {
            next(`/login?redirect=${to.fullPath}`); // 否则全部重定向到登录页
            NProgress.done();
        }
    }
});

三、问题

1.element-ui的el-form验证和el-dialog关闭时清除验证

使用清除验证this.$refs.formName.clearValidate(); 

el-input中focus事件的调用

<el-input  ref="inpt"/>

 this.$refs.inpt.$refs.input.focus(); 

2.el-avatar组件下头像切换不显示的问题

问题:table里面使用el-avatar组件,点击分页后,返回上一页,头像就不显示了,不更新

解决:加:key

3.element-ui的el-select  el-option 内容太长,导致页面不好看

 最简单的解决方式:换行

 // el-select el-option 内容太长,导致页面不好看
.el-select-dropdown__item {
    max-width: 300px;
    display: table;
    width: 100%;
    white-space: pre-wrap;
    border-bottom: 1px solid #f2f2f2;
}

4.element  Tree 树形控件 增加横向纵向滚动条,以及展示第一个层级

 <el-tree
	ref="tree"
	class="flow-tree"
	:data="deptOptions"
	:props="defaultProps"
	:highlight-current="true"
	:expand-on-click-node="false"
	:filter-node-method="filterNode"
	node-key="id"
	:default-expanded-keys="treeExpandData"
	@node-click="handleNodeClick"
/>
// 给el-tree 组件横向滚动条和纵向滚动条问题
.flow-tree {
    height: 650px;
    width: 100%;
    overflow: scroll;
    >.el-tree-node {
        display: inline-block !important;
        min-width: 100% !important;
      }
}

deptOptions:[];//科室数据
defaultProps: {
	children: 'children',
	label: 'label'
},
treeExpandData: [], // 自己定义的用于接收tree树id的数组
provincialCenterId: '',

// 获取树形结构默认展开节点 
getRoleTreeRootNode(provincialCenterId) {
	this.treeExpandData.push(provincialCenterId);
},
/** 查询部门下拉树结构 */
getTreeselect() {
	this.pageLoading = this.setLoading('数据加载中...');
	treeselect()
		.then(response => {
			this.deptOptions = response.data;
			this.provincialCenterId =
				response.data.length > 0 ? response.data[0].id : ''; // 默认展开第一个节点: 展示第一个层级
			this.getRoleTreeRootNode(this.provincialCenterId);
		})
		.catch(e => {
		});
},
// 筛选节点
filterNode(value, data) {
	if (!value) return true;
	return data.label.indexOf(value) !== -1;
},
// 节点单击事件
handleNodeClick(data) {
	this.queryParams.basicInstitution = data.id;
},

5.vue-treeselect 增加横向滚动条

<treeselect
	v-model="queryParams.inid"
	class="my-treeselect"
	style="width: 260px"
	:options="deptOptions"
	:show-count="true"
	placeholder="请选择机关"
	clearable
/>
.my-treeselect {
    .vue-treeselect__label {
        overflow: inherit!important;
    }
    .vue-treeselect__menu {
        overflow: auto!important;
    }
}

6. el-date-picke 设置初始化以及禁止日期

 <el-form-item label="时间范围">
	<el-date-picker
		v-model="queryParams.time"
		clearable
		:picker-options="pickerOptions"
		type="daterange"
		value-format="yyyy-MM-dd"
		range-separator="至"
		start-placeholder="开始日期"
		end-placeholder="结束日期"
	/>
</el-form-item>
export default {
	data(){
		return {
		 // 查询参数
			queryParams: {
				time: [],
				starTime: undefined,
				endTime: undefined
			},
			 // 查询日期设置禁止选今天之后的日期,包括今天
            pickerOptions: {
                disabledDate(time) {
                    return time.getTime() > Date.now() - 8.64e7;
                }
            }
		}
	},
	watch: {
        'queryParams.time': {
            deep: true,
            handler(val) {
                if (!val) {
                    this.queryParams.startTime = undefined;
                    this.queryParams.endTime = undefined;
                } else {
                    this.$set(this.queryParams, 'startTime', val[0]);
                    this.$set(this.queryParams, 'endTime', val[1]);
                }
            }
        }
    },
    created() {
        // 前一天设定的日期时间
        let date = moment().subtract(1, 'days').format('YYYY-MM-DD');
        this.queryParams.time = [date, date];
    },
}

禁止选择当前月以及未来时间

 

pickerOptionsObj: {
                disabledDate(time) {
                    // 获取当前的月份信息
                    const date = new Date(); // 获取当前的时间基本信息
                    const year = date.getFullYear(); // 获取当前年份
                    let month = date.getMonth() + 1; // 获取当前月份
                    if (month >= 1 && month <= 9) {
                        // 如果是1月到9月就要在前面补上一个0  
                        month = "0" + month;
                    }
                    const nowDate = year.toString() + month.toString(); 
                    // 获取时间选择器的月份信息
                    const timeyear = time.getFullYear(); // 获取时间选择器的年份(有很多)
                    let timemonth = time.getMonth() + 1; // 与上面同理
                    if (timemonth >= 1 && timemonth <= 9) {
                        timemonth = "0" + timemonth;
                    }
                    const elTimeData = timeyear.toString() + timemonth.toString();
                    // 大于等于当前月份都不可选 
                    return elTimeData >= nowDate;
                }
            },

 

 7.省市区 选择

git逛网  案例

安装:npm install element-china-area-data -S

<template>
  <div id="app">
    <el-cascader
      size="large"
      :options="options"
      v-model="selectedOptions"
      @change="handleChange">
    </el-cascader>
  </div>
</template>

<script>
  import { regionData } from 'element-china-area-data'
  export default {
    data () {
      return {
        options: regionData,
        selectedOptions: []
      }
    },

    methods: {
      handleChange (value) {
        console.log(value)
      }
    }
  }
</script>


<div class="bind">
      <div>绑定值:{{selectedOptions2}}</div>
      <div>区域码转汉字:{{CodeToText[selectedOptions2[0]]}},{{CodeToText[selectedOptions2[1]]}},{{CodeToText[selectedOptions2[2]]}}</div>
      <div>汉字转区域码:{{convertTextToCode(CodeToText[selectedOptions2[0]], CodeToText[selectedOptions2[1]], CodeToText[selectedOptions2[2]])}}</div>
    </div>

Logo

前往低代码交流专区

更多推荐