参考这个:blog.csdn.net/weixin_4481… blog.csdn.net/weixin_4481…
需要注意的坑:
最开始是无限循环,后面是那个权限有了但是路由有些渲染出来了,有些没渲染出来, 找了两天,路由的name和menu的key的名字有些一致有些不一致,const role = roles.find(role => auth.funcCode == role[0]);这一步权限判断的时候,路由的name和menu的key不一致会判断失误。
无限循环那个必须要加vuex存储的一个判断,不然会死循环
目录如下:
menu.js 菜单
import { getChannelList } from '@/services/core'
import { getHisDeptList } from '@/services/hospital'
import { getUserContext } from '@/utils/userContext'
import auth from '@/utils/auth'
let _init = false;
let _menu = [
// 仪表盘
{ level: 1, key: 'dashboard', name: '全院总览', url: '/dashboard', auth: [['dashboard', 'r']] },
// 预约
{ level: 1, key: 'appoint', name: '预约', },
{ level: 2, parent: 'appoint', key: 'appoint_consultor', name: '上门邀约', icon: 'el-icon-chat-dot-square', url: '/appoint/to_hospital_appoint', auth: [['to_hos_appoint', 'r']] },
{ level: 2, parent: 'appoint', key: 'appointing', name: '科室预约', icon: 'el-icon-timer', url: '/appoint/appointing', auth: [['appointing', 'r']] },
{ level: 2, parent: 'appoint', key: 'appoint_settings', name: '设置', icon: 'el-icon-setting' },
{ level: 3, parent: 'appoint_settings', key: 'frequency', url: '/appoint/frequency', name: '班次设置', auth: [['appoint_setting', 'm']] },
{ level: 3, parent: 'appoint_settings', key: 'scheduling', name: '排班', url: '/appoint/schedule', auth: [['appoint_setting', 's']] },
{ level: 3, parent: 'appoint_settings', key: 'apt_subject', url: '/appoint/apt_subject', name: '预约设置', auth: [['appoint_setting', 'm']] },
// 前厅
{ level: 1, key: 'todo', name: '前厅', },
{ level: 2, key: 'medicalGuide', parent: 'todo', name: '导医台', icon: 'el-icon-guide', url: '/lobby/guide', auth: [['guide', 'r']] },
{ level: 2, key: 'counsellingRoom', parent: 'todo', name: '接诊洽谈', icon: 'el-icon-chat-line-square', url: '/lobby/consulting', auth: [['consult', 'r']] },
// 财务
{ level: 1, key: 'finance', name: '收银' },
{ level: 2, parent: 'finance', key: 'bill', name: '收银', icon: 'el-icon-shopping-cart-full', url: '/finance/bill', auth: [['checkout', 'r']] },
{ level: 2, parent: 'finance', key: 'coupon', name: '优惠券' },
{ level: 3, parent: 'coupon', key: 'cash_coupon', name: '代金券发券', url: '/finance/cash_coupon', auth: [['coupon', 'r']] },
{ level: 2, parent: 'finance', key: 'clearing', name: '日结' },
{ level: 3, parent: 'clearing', key: 'daily_check', name: '收银日结', icon: 'el-icon-money', url: '/finance/daily_check', auth: [['clearing', 'c']] },
{ level: 3, parent: 'clearing', key: 'daily_knot', name: '划扣日结', icon: 'el-icon-check', url: '/finance/daily_knot', auth: [['clearing', 'i']] },
// 科室中心
{ level: 1, key: 'his_dept', name: '科室', },
// 回访中心
{ level: 1, key: 'revisit', name: '回访', },
{ level: 2, parent: "revisit", key: 'revisit_plan', name: '回访任务' },
{ level: 3, parent: 'revisit_plan', key: 'revisit_task', url: '/revisit/mytask', name: '我的任务', auth: [['do_revisit', 'r'], ['allot_revisit', 'w']] },
{ level: 2, parent: "revisit", key: 'revisit_settings', name: '回访设置' },
{ level: 3, parent: "revisit_settings", key: 'revisit_group', url: "/revisit/group", name: '回访包管理', auth: [['revisit_setting', 'w']] },
// { level: 3, parent: "revisit_settings", key: 'revisit_category_group', url: "/revisit/categoryGroup", name: '类别回访设置', auth: [['revisit_setting', 'w']] },
// { level: 3, parent: "revisit_settings", key: 'revisit_assistant', url: "/revisit/assistant", name: '绑定专家助理', auth: [['revisit_setting', 'w']] },
{ level: 3, parent: "revisit_settings", key: 'revisit_result', url: "/revisit/planResult", name: '回访结果设置', auth: [['revisit_setting', 'w']] },
{ level: 3, parent: "revisit_settings", key: 'revisit_type', url: "/revisit/visitType", name: '回访参数设置', auth: [['revisit_setting', 'w']] },
{ level: 2, parent: "revisit", key: 'revisit_sms', name: '短信' },
{ level: 3, parent: "revisit_sms", key: 'revisit_sms_sendRecord', url: "/sms/smsRecord", name: '短信查询', auth: [['sms_setting', 'r']] },
{ level: 3, parent: "revisit_sms", key: 'revisit_sms_template', url: "/sms/template", name: '模板设置', auth: [['sms_setting', 'm']] },
// 渠道
{ level: 1, key: 'channel', name: '渠道', },
{ level: 2, parent: 'channel', key: 'channel_customer', name: '我的客户', icon: 'el-icon-user', url: '/channel/my_customer', auth: [['channel_customer', 'r']] },
{ level: 2, parent: 'channel', key: 'channel_create', name: '渠道建档', icon: 'el-icon-edit-outline' },
// 客户
{ level: 1, key: 'customer_center', name: '客户中心', url: '/customer_center', auth: [['customer_info', 'r'], ['allot_revisit', 'a']], },
// 报表
{ level: 1, key: 'report', name: '报表', },
{ level: 2, parent: 'report', key: 'report_customer', name: '客户报表' },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_to_hospital_detail', name: '101 客户门诊明细表', url: '/report/ctm_to_hospital_detail', auth: [['report_ctm', 'c']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_payment_detail', name: '102 客户消费明细表', url: '/report/ctm_payment_detail', auth: [['report_ctm', 'a']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_execute_detail', name: '103 客户划扣明细表', url: '/report/ctm_execute_detail', auth: [['report_ctm', 'b']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_pdu_report', name: '104 客户剩余项目', url: '/report/ctm_pdu_report', auth: [['report_ctm', 'd']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_intention', name: '105 客户意向报表', url: '/report/ctm_intention', auth: [['report_ctm', 'g']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_to_hospital_cycle', name: '客户上门周期表', url: '/report/ctm_to_hospital_cycle', auth: [['report_ctm', 'e']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_classification', name: '客户分类报表', url: '/report/ctm_classification', auth: [['report_ctm', 'f']] },
{ level: 3, parent: 'report_customer', key: 'rpt_ctm_deposit_unused', name: '预收金未使用报表', url: '/report/ctm_deposit_unused', auth: [['report_ctm', 'h']] },
{ level: 2, parent: 'report', key: 'report_hospital', name: '运营报表' },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_consultor_achieve', name: '301 客服经理业绩报表', url: '/report/his_consultor_achieve', auth: [['report_his', 'a']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_consultor_achieve55', name: '355 客服经理业绩报表', url: '/report/his_consultor_achieve55', auth: [['report_his', '5']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_consultor_cat2_summary', name: '013 客服经理二级项目报表', url: '/report/his_consultor_cat2_summary', auth: [['report_his', 'd']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_developer_achieve2', name: '303 开发人业绩报表', url: '/report/his_developer_channel_achieve', auth: [['report_his', 'f']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_developer_cat2_summary', name: '开发人二级项目报表', url: '/report/his_developer_cat2_summary', auth: [['report_his', 'g']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_channel_summary', name: '802 渠道综合报表', url: '/report/his_channel_summary', auth: [['report_his', 'b']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_channel_cat2_summary', name: '014 渠道二级项目报表', url: '/report/his_channel_cat2_summary', auth: [['report_his', 'e']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_dept_summary', name: '804 科室业绩报表', url: '/report/his_dept_summary2', auth: [['report_his', 'c']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_dept_payment_detail', name: '308 科室二开报表', url: '/report/his_dept_payment_detail', auth: [['report_his', 'h']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_recommender_detail', name: '310 老带新报表', url: '/report/his_recommender_detail', auth: [['report_his', 'i']] },
{ level: 3, parent: 'report_hospital', key: 'rpt_his_operation_sign', name: '划扣签名报表', url: '/report/his_operation_sign', auth: [['report_his', 'z']] },
{ level: 2, parent: 'report', key: 'report_opt', name: '统一运营报表' },
{ level: 3, parent: 'report_opt', key: 'rpt_opt_001', name: 'O001 全院经营漏斗分析', url: '/report/O001', auth: [['report_opt', 'a']] },
{ level: 3, parent: 'report_opt', key: 'rpt_opt_008', name: 'O008 咨询经营漏斗分析', url: '/report/O008', auth: [['report_opt', 'b']] },
{ level: 3, parent: 'report_opt', key: 'rpt_opt_015', name: 'O015 产品经营漏斗分析', url: '/report/O015', auth: [['report_opt', 'c']] },
{ level: 3, parent: 'report_opt', key: 'rpt_opt_022', name: 'O022 渠道经营漏斗分析', url: '/report/O022', auth: [['report_opt', 'd']] },
{ level: 2, parent: 'report', key: 'report_finance', name: '财务报表' },
{ level: 3, parent: 'report_finance', key: 'rpt_fa_01', name: 'F01 财务综合报表', url: '/report/F01', auth: [['report_fin', 'a']] },
{ level: 3, parent: 'report_finance', key: 'report_fin_cash_detail', name: '收银日结报表', url: '/report/fin_cash_detail', auth: [['report_fin', 'b']] },
{ level: 3, parent: 'report_finance', key: 'report_fin_three_payment_method', name: '三方支付报表', url: '/report/fin_three_payment_method', auth: [['report_fin', 'c']] },
{ level: 3, parent: 'report_finance', key: 'report_fin_deposit', name: '预收金使用报表', url: '/report/fin_deposit_detail', auth: [['report_fin', 'd']] },
{ level: 3, parent: 'report_finance', key: 'report_fin_income_ext', name: '划扣补充报表', url: '/report/fin_income_ext', auth: [['report_fin', 'f']] },
{ level: 2, parent: 'report', key: 'report_revisit', name: '回访报表' },
{ level: 3, parent: 'report_revisit', key: 'report_result', name: '回访结果分析表', url: '/report/revisit/rptResult', auth: [['report_rev', 'a']] },
{ level: 3, parent: 'report_revisit', key: 'report_visit_count', name: '人员回访统计表', url: '/report/revisit/RptVisitUserCount', auth: [['report_rev', 'c']] },
//运营中心
{ level: 1, key: 'operate', name: '运营', },
{ level: 2, parent: 'operate', key: 'pdu_manage', name: '产品管理' },
{ level: 3, parent: 'pdu_manage', key: 'product', url: '/operate/product', name: '产品设置', auth: [['pdu', 'r']] },
{ level: 3, parent: 'pdu_manage', key: 'product_set', url: '/operate/product_set', name: '套餐设置', auth: [['pdu', 'r']] },
{ level: 3, parent: 'pdu_manage', key: 'storing_card', url: '/operate/storing_card_def', name: '储值卡设定', auth: [['marketing', 'r']] },
// { level: 2, parent: 'operate', key: 'activity_manage', name: '活动管理' },
// { level: 3, parent: 'activity_manage', key: 'cash_coupon_def', name: '代金券设定', url: '/operate/cash_coupon_def', auth: [['marketing', 'r']] },
// { level: 3, parent: 'activity_manage', key: 'full_discount_def', name: '满减活动设定', url: '/operate/full_discount_def', auth: [['marketing', 'r']] },
{ level: 2, parent: 'operate', key: 'cus_manager', name: '客户管理' },
{ level: 3, parent: 'cus_manager', key: 'indentures', url: '/operate/indentures', name: '客户归属改派', auth: [['ctm_indenture_chg', 'r']] },
{ level: 3, parent: 'cus_manager', key: 'indentures_his', url: '/operate/indentures_his', name: '客户归属改派记录', auth: [['ctm_indenture_chg', 'r']] },
{ level: 3, parent: 'cus_manager', key: 'channelChage', url: '/operate/channelChage', name: '客户渠道改派', auth: [['ctm_channel_chg', 'r']] },
{ level: 3, parent: 'cus_manager', key: 'channelChage_his', url: '/operate/channelChage_his', name: '客户渠道改派记录', auth: [['ctm_channel_chg', 'r']] },
// 会员中心
{ level: 1, key: 'member', name: '会员' },
{ level: 2, parent: 'member', key: 'membership', name: '会员管理' },
{ level: 3, parent: 'membership', key: 'member_mgr', name: '会籍管理', icon: 'el-icon-s-custom', url: '/member/membership', auth: [['member_info', 'r']] },
{ level: 3, parent: 'membership', key: 'member_level', name: '会员等级', icon: 'el-icon-medal', url: '/member/member_level', auth: [['member_info', 'r']] },
{ level: 3, parent: 'membership', key: 'member_up', name: '会员升降级', icon: 'el-icon-data-analysis', url: '/member/member_up', auth: [['member_info', 'r']] },
{ level: 2, parent: 'member', key: 'member_loyalty', name: '忠诚度管理' },
{ level: 3, parent: 'member_loyalty', key: 'member_rfm', name: '人群细分-RFM', url: '/member/member_rfm', auth: [['member_info', 'r']] },
{ level: 3, parent: 'member_loyalty', key: 'member_point', name: '会员积分', url: '/member/member_point', auth: [['member_info', 'r']] },
{ level: 3, parent: 'member_loyalty', key: 'member_recommend', name: '老带新', url: '/member/member_recommend', auth: [['member_info', 'r']] },
{ level: 2, parent: 'member', key: 'member_lifecycle', name: '生命周期管理' },
{ level: 3, parent: 'member_lifecycle', key: 'member_large_cycle', name: '大生命周期管理', icon: 'el-icon-refresh-right', url: '/member/member_large_cycle', auth: [['member_info', 'r']] },
{ level: 2, parent: 'member', key: 'member_setting', name: '会员设置' },
{ level: 3, parent: 'member_setting', key: 'member_card', name: '会员卡设置', url: '/member/member_card', auth: [['member_setting', 'r']] },
{ level: 3, parent: 'member_setting', key: 'member_large_cycle_setting', name: '大生命周期计算规则', url: '/member/member_large_cycle_setting', auth: [['member_setting', 'r']] },
{ level: 3, parent: 'member_setting', key: 'member_point_setting', name: '积分赠送比例', url: '/member/member_point_setting', auth: [['member_setting', 'r']] },
{ level: 3, parent: 'member_setting', key: 'member_point_double', name: '积分翻倍活动', url: '/member/member_point_double', auth: [['member_setting', 'r']] },
// 系统设置
{ level: 1, key: 'setting', name: '系统设置' },
{ level: 2, parent: 'setting', key: 'authorize', name: '系统授权' },
{ level: 3, parent: 'authorize', key: 'account', name: '账号', url: '/setting/account', auth: [['account', 'r']] },
{ level: 3, parent: 'authorize', key: 'post', name: '岗位', url: '/setting/post', auth: [['position', 'r']] },
{ level: 3, parent: 'authorize', key: 'auth', name: '功能', url: '/setting/auth', auth: [['position', 'r']] },
{ level: 2, parent: 'setting', key: 'deptmanage', name: '科室设置' },
{ level: 3, parent: 'deptmanage', key: 'dept', name: '科室', url: '/setting/dept', auth: [['main_data', 'm']] },
{ level: 2, parent: 'setting', key: 'finance_mgr', name: '财务设置' },
{ level: 3, parent: 'finance_mgr', key: 'payment_method', name: '支付方式', url: '/setting/pay', auth: [['main_data', 'm']] },
{ level: 2, parent: 'setting', key: 'channel_mgr', name: '渠道' },
{ level: 3, parent: 'channel_mgr', key: 'channel_method', name: '渠道设置', url: '/setting/channel', auth: [['main_data', 'm']] },
{ level: 2, parent: 'setting', key: 'options', name: '系统设置', url: '/setting/options', auth: [['system_set', 'd']] },
]
export async function menu() {
if (_init) return _menu;
const userContext = getUserContext()
const allowedHisDepts = userContext.userDTO.hisDepts ? userContext.userDTO.hisDepts.split(",") : []
const allowedChannels = userContext.userDTO.channels ? userContext.userDTO.channels.split(",") : []
// 渠道权限
const channels = await getChannelList()
const channelMap = new Map(channels.map(x => [x.id, x]))
for (let channel of channels) {
if (allowedChannels.indexOf(channel.id.toString()) < 0) continue;
channel.allowed = true
let parent = channelMap.get(channel.parentId)
while (parent) {
parent.allowed = true
parent = channelMap.get(channel.parentId)
}
}
const channelsMenu = channels
.filter(x => x.sourceLevel === 1 && x.allowed)
.map(x => ({ level: 3, parent: 'channel_create', key: x.id.toString(), name: x.name, url: `/channel/${x.id}`, auth: [['channel_customer', 'w']] }))
const channelIndex = _menu.findIndex(x => x.key === 'channel')
_menu.splice(channelIndex, 0, ...channelsMenu)
// 科室权限
const hisDepts = await getHisDeptList()
for (let hisDept of hisDepts) {
if (allowedHisDepts.indexOf(hisDept.hisDeptId) < 0) continue;
_menu.push({
level: 2,
parent: "his_dept",
key: hisDept.hisDeptId,
name: hisDept.hisDeptName,
url: `/his_dept/${hisDept.hisDeptId}`,
auth: [
["clinic", "r"],
["his_dept_billing", "r"],
["treatment", "r"],
["medicine", "r"],
],
});
}
// 权限筛选
for (let item of _menu) {
if (!item.auth) continue;
item.enabled =
item.auth.map((x) => auth(x[0], x[1])).filter((x) => x).length > 0;
}
// console.log('menu init', _menu)
_init = true
return _menu;
}
report.js 权限路由
export default [
/********************************************************************************************************
* 客户报表
*/
// 客户门诊明细表
{
name: "rpt_ctm_to_hospital_detail",
path: "/report/ctm_to_hospital_detail",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_to_hospital_detail"),
meta:{requiresAuth: true,roles:[["report_ctm","c"]]}
},
// 客户消费明细表
{
name: "rpt_ctm_payment_detail",
path: "/report/ctm_payment_detail",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_payment_detail"),
meta:{requiresAuth: true,roles:[["report_ctm","a"]]}
},
// 客户划扣明细表
{
name: "rpt_ctm_execute_detail",
path: "/report/ctm_execute_detail",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_execute_detail"),
meta:{requiresAuth: true,roles:[["report_ctm","b"]]}
},
// 客户未划扣明细表
{
name: "rpt_ctm_pdu_report",
path: "/report/ctm_pdu_report",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_pdu_report"),
meta:{requiresAuth: true,roles:[["report_ctm","d"]]}
},
// 客户意向报表
{
name: "rpt_ctm_intention",
path: "/report/ctm_intention",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_intention"),
meta:{requiresAuth: true,roles:[["report_ctm","g"]]}
},
// 客户上门周期表
{
name: "rpt_ctm_to_hospital_cycle",
path: "/report/ctm_to_hospital_cycle",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_to_hospital_cycle"),
meta:{requiresAuth: true,roles:[["report_ctm","e"]]}
},
// 客户分类报表
{
name: "rpt_ctm_classification",
path: "/report/ctm_classification",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_classification"),
meta:{requiresAuth: true,roles:[["report_ctm","f"]]}
},
// 预收金未使用报表
{
name: "rpt_ctm_deposit_unused",
path: "/report/ctm_deposit_unused",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/customer/ctm_deposit_unused"),
meta:{requiresAuth: true,roles:[["report_ctm","h"]]}
},
/********************************************************************************************************
* 运营报表
*/
// 客服经理业绩报表
{
name: 'rpt_his_consultor_achieve',
path: '/report/his_consultor_achieve',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_consultor_achieve'),
meta:{requiresAuth: true,roles:[["report_his","a"]]}
},
// 客服经理业绩报表55开
{
name: 'rpt_his_consultor_achieve55',
path: '/report/his_consultor_achieve55',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_consultor_achieve55'),
meta:{requiresAuth: true,roles:[["report_his","5"]]}
},
// 客服经理二级项目报表
{
name: 'rpt_his_consultor_cat2_summary',
path: '/report/his_consultor_cat2_summary',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_consultor_cat2_summary'),
meta:{requiresAuth: true,roles:[["report_his","d"]]}
},
// 开发人业绩报表
{
name: 'rpt_his_developer_achieve2',
// name: 'rpt_his_developer_channel_achieve',
path: '/report/his_developer_channel_achieve',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_developer_channel_achieve'),
meta:{requiresAuth: true,roles:[["report_his","f"]]}
},
// 开发人二级业绩报表
{
name: 'rpt_his_developer_cat2_summary',
path: '/report/his_developer_cat2_summary',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_developer_cat2_summary'),
meta:{requiresAuth: true,roles:[["report_his","g"]]}
},
// 渠道综合报表
{
name: 'rpt_his_channel_summary',
path: '/report/his_channel_summary',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_channel_summary'),
meta:{requiresAuth: true,roles:[["report_his","b"]]}
},
// 渠道二级项目报表
{
name: 'rpt_his_channel_cat2_summary',
path: '/report/his_channel_cat2_summary',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_channel_cat2_summary'),
meta:{requiresAuth: true,roles:[["report_his","e"]]}
},
// 804报表
{
name: 'rpt_his_dept_summary',
path: '/report/his_dept_summary2',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_dept_summary2'),
meta:{requiresAuth: true,roles:[["report_his","c"]]}
},
// 科室二开明细表
{
name: "rpt_his_dept_payment_detail",
path: "/report/his_dept_payment_detail",
component: () => import( /* webpackChunkName: "report" */ "@/views/report/hospital/his_dept_payment_detail"),
meta:{requiresAuth: true,roles:[["report_his","h"]]}
},
// 老带新报表
{
name: 'rpt_his_recommender_detail',
path: '/report/his_recommender_detail',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_recommender_detail'),
meta:{requiresAuth: true,roles:[["report_his","i"]]}
},
// 划扣签名报表
{
name: 'rpt_his_operation_sign',
path: '/report/his_operation_sign',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/hospital/his_operation_sign'),
meta:{requiresAuth: true,roles:[["report_his","z"]]}
},
// // 退款明细表
// {
// name: 'report_ctm_refund',
// path: '/report/ctm/ctm_refund',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/ctm_refund'),
// },
// // 产品渠道贡献报表
// {
// name: 'report_pdu_channel',
// path: '/report/ctm/pdu_channel',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/pdu_channel'),
// },
// // 产品购买排行
// {
// name: 'report_pdu_rank',
// path: '/report/ctm/pdu_rank',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/pdu_rank'),
// },
// // 分诊消费明细表
// {
// name: 'report_register_pay',
// path: '/report/ctm/register_pay',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/register_pay'),
// },
// // 客户区域报表
// {
// name: 'report_ctm_location',
// path: '/report/ctm/ctm_location',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/ctm_location'),
// },
// // 医院运营报表
// {
// name: 'report_hospital_op',
// path: '/report/ctm/hospital_op',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/ctm/hospital_op'),
// },
/********************************************************************************************************
* 统一运营报表
*/
// O001 全院经营漏斗分析
{
name: 'rpt_opt_001',
path: '/report/O001',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/opt/opt_001'),
meta:{requiresAuth: true,roles:[['report_opt', 'a']]}
},
// O008 咨询经营漏斗分析
{
name: 'rpt_opt_008',
path: '/report/O008',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/opt/opt_008'),
meta:{requiresAuth: true,roles:[['report_opt', 'b']]}
},
// O015 产品经营漏斗分析
{
name: 'rpt_opt_015',
path: '/report/O015',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/opt/opt_015'),
meta:{requiresAuth: true,roles:[['report_opt', 'c'],['pdu', 'r']]}
},
// O022 产品经营漏斗分析
{
name: 'rpt_opt_022',
path: '/report/O022',
component: () => import(/* webpackChunkName: "report" */ '@/views/report/opt/opt_022'),
meta:{requiresAuth: true,roles:[['report_opt', 'd']]}
},
// // 客户到院报表
// {
// name: 'report_ctm_to_hospital',
// path: '/report/frontend/ctm_to_hospital',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/frontend/ctm_to_hospital'),
// },
// // 网电报表
// {
// name: 'report_frontend_report',
// path: '/report/frontend/frontend_report',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/frontend/frontend_report'),
// },
// // 第一需求分析报表
// {
// name: 'report_ctm_first_require',
// path: '/report/frontend/ctm_first_require',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/frontend/ctm_first_require'),
// },
// // 渠道分析报表
// {
// name: 'report_ctm_channel',
// path: '/report/frontend/ctm_channel',
// component: () => import(/* webpackChunkName: "report" */ '@/views/report/frontend/ctm_channel'),
// },
/********************************************************************************************************
* 财务报表
*/
// 财务综合报表
{
name: 'rpt_fa_01',
path: "/report/f01",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/report/finance/fa_01"
),
meta:{requiresAuth: true,roles:[['report_fin', 'a']]}
},
// 收银日结报表
{
name: "report_fin_cash_detail",
path: "/report/fin_cash_detail",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/report/finance/fin_cash_detail"
),
meta:{requiresAuth: true,roles:[['report_fin', 'b']]}
},
//电商第三方支付方式报表
{
name: "report_fin_three_payment_method",
path: "/report/fin_three_payment_method",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/report/finance/fin_three_payment_method"
),
meta:{requiresAuth: true,roles:[['report_fin', 'c']]}
},
// 预收明细
{
name: "report_fin_deposit",
path: "/report/fin_deposit_detail",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/report/finance/fin_deposit_detail"
),
meta:{requiresAuth: true,roles:[['report_fin', 'd']]}
},
// 营收补充报表
{
name: "report_fin_income_ext",
path: "/report/fin_income_ext",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/report/finance/fin_income_ext"
),
meta:{requiresAuth: true,roles:[['report_fin', 'f']]}
},
/********************************************************************************************************
* 回访报表
*/
// 回访结果分析表
{
name: "report_result",
path: "/report/revisit/rptResult",
component: () =>
import(/* webpackChunkName: "report" */ "@/views/revisit/rpt/RptResult"),
meta:{requiresAuth: true,roles:[['report_rev', 'a']]}
},
// 回访消费情况明细表
// {
// name: "report_visit_consum",
// path: "/report/revisit/RptVisitConsum",
// component: () =>
// import(
// /* webpackChunkName: "report" */ "@/views/revisit/rpt/RptVisitConsum"
// ),
// },
// 人员回访统计表
{
name: "report_visit_count",
path: "/report/revisit/RptVisitUserCount",
component: () =>
import(
/* webpackChunkName: "report" */ "@/views/revisit/rpt/RptVisitUserCount"
),
meta:{requiresAuth: true,roles:[['report_rev', 'c']]}
},
];
index.js 所有路由
router.beforeEach路由守卫,权限在这个函数里判断
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '@/views/auth/home';
import Login from '@/views/auth/login';
import channel from './channel';
import operate from './operate';
import depart from './depart';
import setting from './setting';
import customer from './customer';
import lobby from './lobby'; //前厅中心
import revisit from './revisit'; //前厅中心
import finance from './finance';
import appoint from './appoint';
import member from './member';
import report from './report';
import Stomp from '../assets/js/stomp'
import store from '../store'
Vue.use(VueRouter);
Vue.prototype.Stomp = Stomp
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
// const router = new VueRouter({
// mode: "history",
// routes: [
// ],
// });
export const routes = [
{
name: 'Home',
path: '/',
component: Home,
},
{
name: 'Login',
path: '/login',
component: Login,
meta: {
singlePage: true,
}
},
{
name: 'uniquery',
path: '/uniquery/:viewName/:queryId',
props: true,
component: () => import(/* webpackChunkName: "components" */ '@/components/uniquery/QueryEditor'),
meta: {
singlePage: true,
unKeepLive: true,
}
},
{
name: '404',
path: '/404',
props: true,
component: () => import(/* webpackChunkName: "components" */ '@/views/components/404'),
meta: {
singlePage: true,
unKeepLive: true,
}
},
{
name: 'noPermis',
path: '/noPermis',
props: true,
component: () => import(/* webpackChunkName: "components" */ '@/views/components/noPermis'),
meta: {
singlePage: true,
unKeepLive: true,
}
},
{
name: 'noPower',
path: '/noPower',
props: true,
component: () => import(/* webpackChunkName: "components" */ '@/views/components/noPower'),
},
// // 仪表盘
// {
// name: 'dashboard',
// path: '/dashboard',
// component: () => import(/* webpackChunkName: "components" */ '@/views/dashboard'),
// },
// ...channel,
// ...customer,
// 这个菜单里没有 客户中心 customer里的
{
name: 'customer-clinical',
path: '/customer-clinical',
component: () => import(/* webpackChunkName: "channel" */ '@/views/customer-detail/customer-clinical.vue'),
},
// ...operate,
// 这个菜单里没有 operate里的
{
name: 'contact_logs',
path: '/operate/contact_logs',
component: () => import(/* webpackChunkName: "setting" */ '@/views/operate/cus_manager/contact_logs/index'),
},
// ...depart,
// ...setting,
// 这个菜单里没有 setting里的
{
name: 'demo',
path: '/setting/demo',
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/demo'),
},
// ...lobby,
// ...revisit,
// ...finance,
// ...appoint,
// ...member,
// 这个菜单里没有 member里的
{
name: 'member_overview',
path: '/member/overview',
component: () => import(/* webpackChunkName: "operate" */ '@/views/member/overview'),
},
// 这个菜单里没有 member里的
{
name: 'member_small_cycle',
path: '/member/member_small_cycle',
component: () => import(/* webpackChunkName: "operate" */ '@/views/member/small_cycle'),
meta:{requiresAuth: true,roles:[["report_ctm","c"]]}
},
// ...report,
]
export const asyncRouter = [
// 仪表盘
{
name: 'dashboard',
path: '/dashboard',
component: () => import(/* webpackChunkName: "components" */ '@/views/dashboard'),
meta:{requiresAuth: true,roles:[['dashboard', 'r']]}
},
...channel,
...customer,
...operate,
...depart,
...setting,
...lobby,
...revisit,
...finance,
...appoint,
...member,
...report,
]
const router = new VueRouter({
mode: "history",
routes
})
router.beforeEach((to, from, next) => {
let token = localStorage.getItem("token");
if(token){
let userContext = localStorage.getItem("userContext")
let auths = JSON.parse(userContext).auths
let asyncRouterRoles = []
let authsAry = []
let asyncPath = []
asyncRouter.forEach((v)=>{
asyncPath.push(v.path)
if(v.meta && v.meta.roles){
asyncRouterRoles.push(v.meta.roles[0])
}
})
auths.forEach((v,i)=>{
authsAry.push(v.funcCode)
})
function compare(arr1,arr2){
return arr1.filter((v)=>{
return arr2.includes(v)
})
}
// let roles = ["report_ctm","report_his","report_opt","report_fin","report_rev"]
let roles = compare(authsAry, asyncRouterRoles)
if(store.state.addRouters.length){
let routersPath = []
store.state.Routers.forEach((v)=>{
routersPath.push(v.path)
})
if( /^\/channel\/\d/.test(to.path) || /^\/his_dept\/\d/.test(to.path) || to.path.includes('/uniquery/') ){
next()
return
}
if(!routersPath.includes(to.path)){
next({path:'/noPermis'}) // 没有权限
return
}
// console.log("yes")
next()
}else{
// console.log("not")
store.dispatch("asyncGetRouter",{roles}).then(res=>{
router.addRoutes(store.state.addRouters)
})
next({...to})
}
// next()
}else{
if(to.path=='/login'){
next();
}else{
next({path:'/login'})
}
next()
}
})
export default router;
store.js vuex:保存动态添加的路由
hasPermission函数处理有权限的路由
import Vue from "vue"
import Vuex from "vuex"
import router,{routes,asyncRouter} from "./router"
function hasPermission(route,roles){
// console.log("route.meta.roles",route.meta.roles)
let authTag
if(route.meta && route.meta.roles){
let userContext = localStorage.getItem("userContext")
let auths = JSON.parse(userContext).auths
let roles = route.meta.roles
let authsAry = []
auths.forEach((v,i)=>{
authsAry.push(v.abilities)
})
let aa = roles.some((role)=>{
if( route.meta.roles.indexOf(role)>-1 ){
// auths.forEach((v,i)=>{
// console.log("v===",v)
// console.log("route.meta.roles",route.meta.roles)
// if(v.funcCode == route.meta.roles[0]){
// if(v.abilities.indexOf( route.meta.roles[1] ) > -1){
// authTag = true
// }
// }
// })
// for(var i=0;i<auths.length;i++){
// for(var j=0;j<roles.length;j++){
// if(auths[i].funcCode == roles[j][0]){
// if( auths[i].abilities.indexOf(roles[j][1]) > - 1 ){
// console.log("route.meta.roles",route.meta.roles)
// authTag = true
// }
// }
// }
// }
auths.forEach(auth=>{
const role = roles.find(role => auth.funcCode == role[0]);
console.log("roles.find",role)
if(role&&auth.abilities.includes(role[1])){
authTag = true;
}
})
}
})
return authTag
}else{
// return true
return false
}
}
/*
递归过滤异步路由表 返回符合用户角色的路由
@param asyncRouter 异步路由
@param roles 用户角色
*/
function filterAsyncRouter(asyncRouter, roles) {
let filterRouter = asyncRouter.filter(route =>{
if(hasPermission(route, roles)) {
if(route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, roles)
}
return true
}
return false
})
return filterRouter
}
Vue.use(Vuex)
export default new Vuex.Store({
state: {
addRouters: [],
Routers: []
},
mutations: {
getRouter(state, paload) {
// console.log(paload)
state.Routers = routes.concat(paload)
state.addRouters = paload
// router.addRoutes(paload)
}
},
actions: {
asyncGetRouter({ commit }, data) {
const { roles } = data
return new Promise(resolve =>{
let addAsyncRouters = filterAsyncRouter(asyncRouter, roles)
console.log("addAsyncRouters========",addAsyncRouters)
commit('getRouter', addAsyncRouters)
resolve()
})
}
}
})
以下文件不是主要逻辑,主要是获取权限的步骤,和渲染左侧菜单
api.js
//权限列表allAuthList
export const getAuthConfig = (params) => {
// return http.get('/api/user/v1/auth/', params)
return cache.get('authList', () => http.get('/api/user/v1/auth/'), true)
}
login.vue
Desktop.vue 渲染左侧菜单
由于我们的路由是平级处理的,这里需要单独处理二级,三级子菜单
这个页面的参考意义不大,根据项目做不同写法
<template>
<div :class="`desktop ${leftMenu && leftMenu.length ? '' : 'no-aside'}`">
<header class="nav-top">
<div class="logo">
<span class="logo-istar">iStar</span>
<span>-CRM{{ region }}</span>
</div>
<el-menu
:default-active="topActive"
class="menu"
mode="horizontal"
@select="onTopMenuSelect"
>
<template v-for="item in menu">
<!-- 一级菜单 -->
<el-menu-item :key="item.key" :index="String(item.key)">
<i :class="`menu-item-logo ${item.icon}`"></i>
<b>{{ item.name }}</b>
</el-menu-item>
</template>
</el-menu>
<user-avatar class="ys-query_avatar" />
</header>
<aside v-if="leftMenu && leftMenu.length">
<el-menu
:default-active="leftActive"
class="nav-left menu"
@select="onLeftMenuSelect"
>
<template v-for="item in leftMenu">
<!-- 一级菜单 -->
<el-menu-item v-if="!item.children" :key="item.key" :index="item.key">
<i :class="`menu-item-logo ${item.icon}`"></i>
<b>{{ item.name }}</b>
</el-menu-item>
<!-- 一级子菜单 -->
<el-submenu v-else :key="item.key" :index="item.key">
<template slot="title">
<i :class="`menu-item-logo ${item.icon}`"></i>
<b>{{ item.name }}</b>
</template>
<template v-for="item2 in item.children">
<!-- 二级菜单 -->
<el-menu-item
v-if="!item2.children"
:key="item2.key"
:index="item2.key"
>
<!-- <i :class="`menu-item-logo ${item2.icon}`"></i> -->
<b style="margin-left: 10px">{{ item2.name }}</b>
</el-menu-item>
<!-- 二级菜单分组 -->
<el-menu-item-group v-else :key="item2.key" :index="item2.key">
<span class="menu-level2__title" slot="title">
{{ item2.name }}
</span>
<!-- 三级菜单 -->
<el-menu-item
v-for="item3 in item2.children"
:key="item3.key"
:index="item3.key"
>
<!-- <i :class="`menu-item-logo ${item3.icon}`"></i> -->
<b>{{ item3.name }}</b>
</el-menu-item>
</el-menu-item-group>
</template>
</el-submenu>
</template>
</el-menu>
</aside>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
import { menu, getChannelCascader } from "@/services/core";
import UserAvatar from "@/components/core/UserAvatar";
import { getSystemConfig } from "@/utils/userContext";
import { getUserContext } from "@/utils/userContext";
export default {
components: {
"user-avatar": UserAvatar,
},
data() {
return {
map: new Map(),
routerMap: new Map(),
menu: [],
topActive: null,
leftActive: null,
};
},
async mounted() {
await this.initMenu();
this.redirect();
},
computed: {
leftMenu() {
if (!this.topActive) return [];
const lv1 = this.map.get(this.topActive);
if (!lv1) return [];
return lv1.children;
},
region() {
const { company_name } = getSystemConfig();
let region = "";
if (company_name === "武汉") {
const {
userDTO: { loginHospitalId },
} = getUserContext();
region =
loginHospitalId === 1 ? "武昌" : loginHospitalId === 2 ? "汉口" : "";
} else {
region = company_name;
}
return `${region ? `-` + region : ""}`;
},
},
watch: {
$route: {
async handler(matched) {
if (!matched || !matched.name) return;
let menuItem = this.routerMap.get(matched.name);
if (!menuItem) return;
// 缓存 url
menuItem.currentUrl = matched.fullPath;
this.leftActive = null;
this.topActive = null;
// 设置当前选中的菜单
while (menuItem) {
if (menuItem.level > 1 && this.leftActive === null) {
switch (menuItem.route) {
case "his_dept":
case "channel":
this.leftActive = matched.params.pathMatch;
break;
default:
this.leftActive = menuItem.key;
break;
}
const item = this.map.get(this.leftActive);
this.getRootHistory(item);
} else if (menuItem.level === 1) {
this.topActive = menuItem.key;
}
menuItem = this.map.get(menuItem.parent);
}
if (!this.menu) await this.initMenu();
},
immediate: false,
},
},
methods: {
async initMenu() {
let _menu = JSON.parse(JSON.stringify(await menu()));
// 菜单整理成多层结构
this.map = new Map(_menu.map((x) => [x.key, x]));
// 三级菜单
for (let item of _menu.filter((x) => x.level === 3)) {
if (item.auth && !item.enabled) continue;
const parent = this.map.get(item.parent);
if (!parent) {
console.error("menu error", item);
continue;
}
if (!parent.children) parent.children = [];
parent.children.push(item);
}
// 二级菜单
for (let item of _menu.filter((x) => x.level === 2)) {
if (
!(
(item.auth && item.enabled) ||
(!item.auth && item.children && item.children.length)
)
)
continue;
const parent = this.map.get(item.parent);
if (!parent) {
console.error("menu error", item);
continue;
}
if (!parent.children) parent.children = [];
parent.children.push(item);
}
// 菜单与路由的映射关系
for (let x of _menu) {
if (!x.url) continue;
const matched = this.$router.match(x.url);
if (!matched.name) continue;
x.route = matched.name;
if (this.routerMap.get(matched.name)) continue;
this.routerMap.set(matched.name, x);
}
this.menu = _menu.filter(
(x) =>
x.level === 1 &&
((x.auth && x.enabled) ||
(!x.auth && x.children && x.children.length))
);
},
// 根目录跳转设置
async redirect() {
const url = new URL(window.location.href);
if (url.pathname !== "/") return;
let index = 0;
let menuItem = this.menu[index];
while (menuItem) {
if (menuItem.url) {
this.$router.push(menuItem.url);
console.log("redirect", menuItem.url);
return;
}
if (menuItem.children && menuItem.children.length) {
menuItem = menuItem.children[0];
} else {
index++;
menuItem = this.menu[index];
}
}
console.error("没有任何权限");
},
async onTopMenuSelect(key) {
const item = this.map.get(key);
if (item.url) {
this.$router.push(item.currentUrl || item.url);
return;
}
//历史选中
if (item.history) {
this.$router.push(item.history.currentUrl || item.history.url);
return;
}
if (!item.children) return;
for (let lv2 of item.children) {
if (lv2.url) {
this.$router.push(lv2.currentUrl || lv2.url);
return;
}
if (!lv2.children) continue;
for (let lv3 of lv2.children) {
if (lv3.url) {
this.$router.push(lv3.currentUrl || lv3.url);
return;
}
}
}
},
getRootHistory(item) {
if (!item) {
this.$router.push("/404");
return;
}
if (item.parent) {
const item2 = this.map.get(item.parent);
if (item2.parent) {
const item3 = this.map.get(item2.parent);
item3["history"] = item;
} else {
item2["history"] = item;
}
}
},
async onLeftMenuSelect(key) {
const item = this.map.get(key);
this.leftActive = item.key;
this.getRootHistory(item);
this.$router.push(item.url);
},
},
};
</script>
<style lang="scss" scoped>
$menu-top: rpx(56);
$menu-left: rpx(170);
$menu-active: $primary;
$menu-bg-active: rgba($primary, 0.1);
.desktop {
position: absolute;
top: $menu-top;
left: $menu-left;
right: rpx(0);
bottom: rpx(0);
overflow: auto;
&.no-aside {
left: rpx(0);
}
}
.nav-top {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
height: $menu-top;
overflow: hidden;
border-bottom: 1px solid $border-color;
z-index: 100;
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
box-shadow: 0 0 10px #00000014;
.logo {
// width: $menu-left !important;
height: $menu-top;
line-height: $menu-top;
padding: 0 rpx(12);
font-size: rpx(17);
font-weight: 600;
// width: 1.6rem;
font-family: Mircosoft Yahei;
color: #333;
.logo-istar {
color: $primary;
}
}
.ys-query_avatar {
position: absolute;
right: 5px;
}
.menu {
height: $menu-top;
border-right: rpx(0);
border-bottom: rpx(0);
.el-menu-item {
padding: 0 rpx(30) 0 rpx(25) !important;
height: rpx(55);
line-height: $menu-top;
i.menu-item-logo {
font-size: rpx(24);
margin-right: rpx(5);
}
b {
font-size: rpx(15) !important;
line-height: rpx(24) !important;
font-weight: bold;
}
}
.el-menu-item:hover {
i.menu-item-logo {
margin-right: rpx(5);
color: $menu-active;
}
b {
color: $menu-active;
}
}
.el-menu-item.is-active {
border-bottom: rpx(2) solid $menu-active;
i.menu-item-logo {
margin-right: rpx(5);
color: $menu-active;
}
b {
color: $menu-active;
}
}
}
}
.nav-left {
position: fixed;
top: $menu-top;
left: 0px;
width: $menu-left;
bottom: 0px;
overflow-x: hidden;
overflow-y: auto;
z-index: 100;
border-right: 1px solid #ebeef5;
box-shadow: 0 10px 10px #00000014;
&.menu {
width: $menu-left;
.el-menu-item {
padding: 0 rpx(10) !important;
height: rpx(33);
min-width: auto;
line-height: rpx(32);
margin: rpx(10);
border-radius: 5px;
i.menu-item-logo {
font-size: rpx(24);
margin-right: rpx(5);
}
b {
font-size: rpx(12) !important;
line-height: rpx(24) !important;
}
}
.el-menu-item:hover {
background-color: $menu-bg-active;
}
.el-menu-item.is-active {
background-color: $menu-active;
color: rgba($white, 0.9);
&:after {
content: "";
width: 3px;
height: 50%;
position: absolute;
top: 25%;
right: -8px;
background-color: $primary;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
}
.el-submenu {
padding: rpx(0) !important;
width: $menu-left;
/deep/ .el-submenu__title {
height: rpx(33);
line-height: rpx(33);
color: #333;
i.menu-item-logo {
font-size: rpx(24);
margin-right: rpx(5);
}
b {
font-size: rpx(12) !important;
line-height: rpx(24) !important;
}
}
}
}
}
</style>