vue-element-admin-master框架结合element UI 组件 权限的问题以及element 组件使用中遇到的问题
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.省市区 选择
安装: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>