feat(menu): 优化菜单服务性能并新增医生排班功能
- 添加菜单缓存注解以提升查询性能 - 实现菜单完整路径计算优化,解决 N+1 查询问题 - 新增 selectAllMenus 方法供路径计算使用 - 添加今日医生排班查询功能 - 重构前端图标显示逻辑,使用 SVG 图标替代 Element 图标 - 添加前端菜单数据本地缓存机制 - 更新菜单管理界面的表单组件绑定方式 - 新增预约管理、门诊管理和药房管理路由配置
This commit is contained in:
@@ -59,9 +59,7 @@
|
||||
@click="handleQuickAccess(func)"
|
||||
>
|
||||
<div class="quick-icon">
|
||||
<el-icon :size="28" :color="func.iconColor">
|
||||
<component :is="func.icon" />
|
||||
</el-icon>
|
||||
<svg-icon :icon-class="func.icon" :style="{ fontSize: '28px', color: func.iconColor }" />
|
||||
</div>
|
||||
<div class="quick-label">{{ func.label }}</div>
|
||||
</div>
|
||||
@@ -138,6 +136,7 @@ import { getHomeStatistics, getPendingEmrCount } from '@/api/home'
|
||||
import { listTodo } from '@/api/workflow/task.js'
|
||||
import { getCurrentUserConfig } from '@/api/system/userConfig'
|
||||
import { listMenu, getMenuFullPath } from '@/api/system/menu'
|
||||
import { getTodayDoctorScheduleList } from '@/api/appointmentmanage/doctorSchedule'
|
||||
import { ElDivider } from 'element-plus'
|
||||
import {
|
||||
User,
|
||||
@@ -220,6 +219,7 @@ import {
|
||||
|
||||
// 为别名单独导入
|
||||
import { InfoFilled as InfoFilledIcon, QuestionFilled as QuestionFilledIcon, SuccessFilled } from '@element-plus/icons-vue'
|
||||
import SvgIcon from '@/components/SvgIcon'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
@@ -400,8 +400,8 @@ const convertMenuIdsToQuickAccess = async (menuIds) => {
|
||||
return {
|
||||
key: menuItem.menuId,
|
||||
label: menuItem.menuName,
|
||||
icon: getIconComponent(menuItem.icon || 'Document'), // 使用菜单项的图标,如果没有则使用默认图标
|
||||
iconColor: getIconColorByMenuType(menuItem.menuType) || '#67C23A', // 使用菜单类型的颜色,如果没有则使用默认颜色
|
||||
icon: menuItem.icon || 'document', // 使用数据库中的图标类名
|
||||
iconColor: menuItem.iconColor || getIconColorByMenuType(menuItem.menuType) || '#67C23A', // 优先使用数据库中的颜色,否则使用菜单类型的颜色
|
||||
route: route
|
||||
};
|
||||
}).filter(item => item.route); // 过滤掉 route 为空的项
|
||||
@@ -436,8 +436,8 @@ const convertMenuIdsToQuickAccess = async (menuIds) => {
|
||||
return {
|
||||
key: matchedMenu.perms || matchedMenu.path || `menu_${matchedMenu.menuId}`,
|
||||
label: matchedMenu.menuName,
|
||||
icon: getIconComponent(matchedMenu.icon),
|
||||
iconColor: getIconColorByMenuType(matchedMenu.menuType),
|
||||
icon: matchedMenu.icon || 'document', // 使用数据库中的图标类名
|
||||
iconColor: matchedMenu.iconColor || getIconColorByMenuType(matchedMenu.menuType),
|
||||
route: fullPath || matchedMenu.path // 确保 route 不为空
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -446,8 +446,8 @@ const convertMenuIdsToQuickAccess = async (menuIds) => {
|
||||
return {
|
||||
key: matchedMenu.perms || matchedMenu.path || `menu_${matchedMenu.menuId}`,
|
||||
label: matchedMenu.menuName,
|
||||
icon: getIconComponent(matchedMenu.icon),
|
||||
iconColor: getIconColorByMenuType(matchedMenu.menuType),
|
||||
icon: matchedMenu.icon || 'document', // 使用数据库中的图标类名
|
||||
iconColor: matchedMenu.iconColor || getIconColorByMenuType(matchedMenu.menuType),
|
||||
route: matchedMenu.path || matchedMenu.fullPath || '/' // 确保 route 不为空
|
||||
};
|
||||
}
|
||||
@@ -490,81 +490,6 @@ const flattenMenuTree = (menuTree) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
// 获取图标组件
|
||||
const getIconComponent = (iconName) => {
|
||||
if (!iconName) return Document;
|
||||
// 移除前缀,如 fa-, el-icon-
|
||||
const cleanIconName = iconName.replace(/^(fa-|el-icon-)/, '').toLowerCase();
|
||||
|
||||
const iconMap = {
|
||||
'menu': markRaw(Menu),
|
||||
'grid': markRaw(Grid),
|
||||
'folder': markRaw(Folder),
|
||||
'tickets': markRaw(Tickets),
|
||||
'document': markRaw(Document),
|
||||
'setting': markRaw(Setting),
|
||||
'user': markRaw(User),
|
||||
'goods': markRaw(Goods),
|
||||
'chat-dot-square': markRaw(ChatDotSquare),
|
||||
'histogram': markRaw(Histogram),
|
||||
'wallet': markRaw(Wallet),
|
||||
'office-building': markRaw(OfficeBuilding),
|
||||
'postcard': markRaw(Postcard),
|
||||
'collection': markRaw(Collection),
|
||||
'video-play': markRaw(VideoPlay),
|
||||
'camera': markRaw(Camera),
|
||||
'headset': markRaw(Headset),
|
||||
'phone': markRaw(Phone),
|
||||
'message': markRaw(Message),
|
||||
'chat-line-square': markRaw(ChatLineSquare),
|
||||
'chat-round': markRaw(ChatRound),
|
||||
'guide': markRaw(Guide),
|
||||
'help': markRaw(Help),
|
||||
'info-filled': markRaw(InfoFilled),
|
||||
'circle-check': markRaw(CircleCheck),
|
||||
'circle-close': markRaw(CircleClose),
|
||||
'warning': markRaw(Warning),
|
||||
'question-filled': markRaw(QuestionFilled),
|
||||
'star': markRaw(Star),
|
||||
'link': markRaw(Link),
|
||||
'position': markRaw(Position),
|
||||
'picture': markRaw(Picture),
|
||||
'upload': markRaw(Upload),
|
||||
'download': markRaw(Download),
|
||||
'caret-left': markRaw(CaretLeft),
|
||||
'caret-right': markRaw(CaretRight),
|
||||
'more': markRaw(More),
|
||||
'close': markRaw(Close),
|
||||
'check': markRaw(Check),
|
||||
'arrow-up': markRaw(ArrowUp),
|
||||
'arrow-down': markRaw(ArrowDown),
|
||||
'arrow-left': markRaw(ArrowLeft),
|
||||
'arrow-right': markRaw(ArrowRight),
|
||||
'plus': markRaw(Plus),
|
||||
'minus': markRaw(Minus),
|
||||
'zoom-in': markRaw(ZoomIn),
|
||||
'zoom-out': markRaw(ZoomOut),
|
||||
'refresh': markRaw(Refresh),
|
||||
'search': markRaw(Search),
|
||||
'edit': markRaw(Edit),
|
||||
'delete': markRaw(Delete),
|
||||
'share': markRaw(Share),
|
||||
'view': markRaw(View),
|
||||
'switch-button': markRaw(SwitchButton),
|
||||
'hide': markRaw(Hide),
|
||||
'finished': markRaw(Finished),
|
||||
'circle-plus': markRaw(CirclePlus),
|
||||
'remove': markRaw(Remove),
|
||||
'circle-check-filled': markRaw(CircleCheckFilled),
|
||||
'circle-close-filled': markRaw(CircleCloseFilled),
|
||||
'warning-filled': markRaw(WarningFilled),
|
||||
'info-filled-icon': markRaw(InfoFilledIcon),
|
||||
'success-filled': markRaw(SuccessFilled),
|
||||
'question-filled-icon': markRaw(QuestionFilledIcon)
|
||||
};
|
||||
|
||||
return iconMap[cleanIconName] || markRaw(Document);
|
||||
};
|
||||
|
||||
// 根据菜单类型获取图标颜色
|
||||
const getIconColorByMenuType = (menuType) => {
|
||||
@@ -581,48 +506,48 @@ const getDefaultQuickAccessConfig = () => {
|
||||
switch (role) {
|
||||
case 'doctor':
|
||||
return [
|
||||
{ key: 'outpatient', label: '门诊接诊', icon: markRaw(ChatDotRound), iconColor: '#409eff', route: '/doctorstation' },
|
||||
{ key: 'emr', label: '病历管理', icon: markRaw(Document), iconColor: '#67c23a', route: '/doctorstation/doctorphrase' },
|
||||
{ key: 'prescription', label: '开立处方', icon: markRaw(Box), iconColor: '#e6a23c', route: '/clinicmanagement/ePrescribing' },
|
||||
{ key: 'history', label: '历史处方', icon: markRaw(Clock), iconColor: '#f56c6c', route: '/clinicmanagement/historicalPrescription' },
|
||||
{ key: 'schedule', label: '排班管理', icon: markRaw(Calendar), iconColor: '#909399', route: '/appoinmentmanage/deptManage' },
|
||||
{ key: 'inquiry', label: '患者查询', icon: markRaw(Search), iconColor: '#409eff', route: '/patientmanagement' }
|
||||
{ key: 'outpatient', label: '门诊接诊', icon: 'chat-dot-round', iconColor: '#409eff', route: '/doctorstation' },
|
||||
{ key: 'emr', label: '病历管理', icon: 'document', iconColor: '#67c23a', route: '/doctorstation/doctorphrase' },
|
||||
{ key: 'prescription', label: '开立处方', icon: 'box', iconColor: '#e6a23c', route: '/clinicmanagement/ePrescribing' },
|
||||
{ key: 'history', label: '历史处方', icon: 'clock', iconColor: '#f56c6c', route: '/clinicmanagement/historicalPrescription' },
|
||||
{ key: 'schedule', label: '排班管理', icon: 'calendar', iconColor: '#909399', route: '/appoinmentmanage/deptManage' },
|
||||
{ key: 'inquiry', label: '患者查询', icon: 'search', iconColor: '#409eff', route: '/patientmanagement' }
|
||||
];
|
||||
case 'nurse':
|
||||
return [
|
||||
{ key: 'ward', label: '病房管理', icon: markRaw(User), iconColor: '#409eff', route: '/inpatientNurse/inpatientNurseStation' },
|
||||
{ key: 'execution', label: '医嘱执行', icon: markRaw(Operation), iconColor: '#67c23a', route: '/inpatientNurse/medicalOrderExecution' },
|
||||
{ key: 'proofread', label: '医嘱核对', icon: markRaw(Document), iconColor: '#e6a23c', route: '/inpatientNurse/medicalOrderProofread' },
|
||||
{ key: 'drugCollect', label: '领药管理', icon: markRaw(Box), iconColor: '#f56c6c', route: '/inpatientNurse/medicineCollect' },
|
||||
{ key: 'tpr', label: '体温单', icon: markRaw(Monitor), iconColor: '#909399', route: '/inpatientNurse/tprsheet' },
|
||||
{ key: 'nursing', label: '护理记录', icon: markRaw(ChatDotRound), iconColor: '#409eff', route: '/inpatientNurse/nursingRecord' }
|
||||
{ key: 'ward', label: '病房管理', icon: 'user', iconColor: '#409eff', route: '/inpatientNurse/inpatientNurseStation' },
|
||||
{ key: 'execution', label: '医嘱执行', icon: 'operation', iconColor: '#67c23a', route: '/inpatientNurse/medicalOrderExecution' },
|
||||
{ key: 'proofread', label: '医嘱核对', icon: 'document', iconColor: '#e6a23c', route: '/inpatientNurse/medicalOrderProofread' },
|
||||
{ key: 'drugCollect', label: '领药管理', icon: 'box', iconColor: '#f56c6c', route: '/inpatientNurse/medicineCollect' },
|
||||
{ key: 'tpr', label: '体温单', icon: 'monitor', iconColor: '#909399', route: '/inpatientNurse/tprsheet' },
|
||||
{ key: 'nursing', label: '护理记录', icon: 'chat-dot-round', iconColor: '#409eff', route: '/inpatientNurse/nursingRecord' }
|
||||
];
|
||||
case 'pharmacist':
|
||||
return [
|
||||
{ key: 'dispensing', label: '发药管理', icon: markRaw(Box), iconColor: '#409eff', route: '/pharmacymanagement' },
|
||||
{ key: 'prescription', label: '处方审核', icon: markRaw(Document), iconColor: '#67c23a', route: '/pharmacymanagement' },
|
||||
{ key: 'inventory', label: '库存管理', icon: markRaw(Van), iconColor: '#e6a23c', route: '/medicineStorage' },
|
||||
{ key: 'purchase', label: '采购管理', icon: markRaw(ShoppingCart), iconColor: '#f56c6c', route: '/medicineStorage' },
|
||||
{ key: 'warning', label: '效期预警', icon: markRaw(Warning), iconColor: '#f56c6c', route: '/medicationmanagement/statisticalManagement/statisticalManagement' },
|
||||
{ key: 'statistics', label: '用药统计', icon: markRaw(DataLine), iconColor: '#909399', route: '/monitor' }
|
||||
{ key: 'dispensing', label: '发药管理', icon: 'box', iconColor: '#409eff', route: '/pharmacymanagement' },
|
||||
{ key: 'prescription', label: '处方审核', icon: 'document', iconColor: '#67c23a', route: '/pharmacymanagement' },
|
||||
{ key: 'inventory', label: '库存管理', icon: 'van', iconColor: '#e6a23c', route: '/medicineStorage' },
|
||||
{ key: 'purchase', label: '采购管理', icon: 'shopping-cart', iconColor: '#f56c6c', route: '/medicineStorage' },
|
||||
{ key: 'warning', label: '效期预警', icon: 'warning', iconColor: '#f56c6c', route: '/medicationmanagement/statisticalManagement/statisticalManagement' },
|
||||
{ key: 'statistics', label: '用药统计', icon: 'data-line', iconColor: '#909399', route: '/monitor' }
|
||||
];
|
||||
case 'cashier':
|
||||
return [
|
||||
{ key: 'registration', label: '挂号收费', icon: markRaw(Money), iconColor: '#409eff', route: '/charge/outpatientregistration' },
|
||||
{ key: 'clinicCharge', label: '门诊收费', icon: markRaw(Wallet), iconColor: '#67c23a', route: '/charge/cliniccharge' },
|
||||
{ key: 'refund', label: '退费管理', icon: markRaw(Document), iconColor: '#e6a23c', route: '/charge/clinicrefund' },
|
||||
{ key: 'invoice', label: '发票打印', icon: markRaw(Files), iconColor: '#f56c6c', route: '/basicmanage/InvoiceManagement' },
|
||||
{ key: 'record', label: '收费记录', icon: markRaw(Clock), iconColor: '#909399', route: '/charge/clinicRecord' },
|
||||
{ key: 'insurance', label: '医保结算', icon: markRaw(Bell), iconColor: '#409eff', route: '/ybmanagement' }
|
||||
{ key: 'registration', label: '挂号收费', icon: 'money', iconColor: '#409eff', route: '/charge/outpatientregistration' },
|
||||
{ key: 'clinicCharge', label: '门诊收费', icon: 'wallet', iconColor: '#67c23a', route: '/charge/cliniccharge' },
|
||||
{ key: 'refund', label: '退费管理', icon: 'document', iconColor: '#e6a23c', route: '/charge/clinicrefund' },
|
||||
{ key: 'invoice', label: '发票打印', icon: 'files', iconColor: '#f56c6c', route: '/basicmanage/InvoiceManagement' },
|
||||
{ key: 'record', label: '收费记录', icon: 'clock', iconColor: '#909399', route: '/charge/clinicRecord' },
|
||||
{ key: 'insurance', label: '医保结算', icon: 'bell', iconColor: '#409eff', route: '/ybmanagement' }
|
||||
];
|
||||
default: // admin
|
||||
return [
|
||||
{ key: 'patient', label: '患者管理', icon: markRaw(User), iconColor: '#409eff', route: '/patient/patientmgr' },
|
||||
{ key: 'appointment', label: '预约管理', icon: markRaw(Calendar), iconColor: '#67c23a', route: '/appoinmentmanage' },
|
||||
{ key: 'doctor', label: '医生管理', icon: markRaw(User), iconColor: '#e6a23c', route: '/doctorstation' },
|
||||
{ key: 'surgery', label: '手术管理', icon: markRaw(Operation), iconColor: '#f56c6c', route: '/surgerymanage' },
|
||||
{ key: 'drug', label: '药品管理', icon: markRaw(Box), iconColor: '#909399', route: '/pharmacymanagement' },
|
||||
{ key: 'statistic', label: '数据统计', icon: markRaw(TrendCharts), iconColor: '#409eff', route: '/monitor' }
|
||||
{ key: 'patient', label: '患者管理', icon: 'user', iconColor: '#409eff', route: '/patient/patientmgr' },
|
||||
{ key: 'appointment', label: '预约管理', icon: 'calendar', iconColor: '#67c23a', route: '/appoinmentmanage' },
|
||||
{ key: 'doctor', label: '医生管理', icon: 'user', iconColor: '#e6a23c', route: '/doctorstation' },
|
||||
{ key: 'surgery', label: '手术管理', icon: 'operation', iconColor: '#f56c6c', route: '/surgerymanage' },
|
||||
{ key: 'drug', label: '药品管理', icon: 'box', iconColor: '#909399', route: '/pharmacymanagement' },
|
||||
{ key: 'statistic', label: '数据统计', icon: 'trend-charts', iconColor: '#409eff', route: '/monitor' }
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -639,12 +564,7 @@ const updatePendingEmrTodo = () => {
|
||||
}
|
||||
|
||||
// 今日日程
|
||||
const scheduleList = ref([
|
||||
{ id: 1, time: '09:00', title: '科室晨会', location: '第一会议室', type: 'info', tag: '日常' },
|
||||
{ id: 2, time: '10:30', title: '病例讨论', location: '第二会议室', type: 'primary', tag: '会议' },
|
||||
{ id: 3, time: '14:00', title: '专家查房', location: '住院部3楼', type: 'warning', tag: '重要' },
|
||||
{ id: 4, time: '16:00', title: '新药培训', location: '培训中心', type: 'success', tag: '培训' }
|
||||
])
|
||||
const scheduleList = ref([])
|
||||
|
||||
// 获取问候语
|
||||
const getGreeting = () => {
|
||||
@@ -980,8 +900,58 @@ const getTaskIcon = (category) => {
|
||||
|
||||
// 获取日程数据(实际应用中应该从API获取)
|
||||
const fetchScheduleList = async () => {
|
||||
// TODO: 调用API获取真实数据
|
||||
console.log('Fetching schedule list...')
|
||||
try {
|
||||
console.log('Fetching schedule list...')
|
||||
const response = await getTodayDoctorScheduleList()
|
||||
if (response.code === 200) {
|
||||
// 将API返回的数据转换为前端所需的格式
|
||||
const scheduleData = response.data.map((schedule, index) => {
|
||||
// 根据排班类型设置标签类型
|
||||
let tagType = 'info'
|
||||
if (schedule.weekday) {
|
||||
tagType = schedule.weekday.toLowerCase()
|
||||
} else if (schedule.timePeriod) {
|
||||
tagType = schedule.timePeriod.toLowerCase()
|
||||
}
|
||||
|
||||
// 确定标题
|
||||
const title = schedule.doctor ? `${schedule.doctor}医生排班` : '医生排班'
|
||||
|
||||
// 确定位置
|
||||
const location = schedule.clinic || schedule.deptId || '未知科室'
|
||||
|
||||
return {
|
||||
id: schedule.id || index,
|
||||
time: schedule.startTime || '未知时间',
|
||||
title: title,
|
||||
location: location,
|
||||
type: tagType,
|
||||
tag: schedule.timePeriod || '排班'
|
||||
}
|
||||
})
|
||||
|
||||
// 更新日程列表
|
||||
scheduleList.value = scheduleData
|
||||
} else {
|
||||
console.error('获取排班信息失败:', response.msg)
|
||||
// 如果API调用失败,使用默认数据
|
||||
scheduleList.value = [
|
||||
{ id: 1, time: '09:00', title: '科室晨会', location: '第一会议室', type: 'info', tag: '日常' },
|
||||
{ id: 2, time: '10:30', title: '病例讨论', location: '第二会议室', type: 'primary', tag: '会议' },
|
||||
{ id: 3, time: '14:00', title: '专家查房', location: '住院部3楼', type: 'warning', tag: '重要' },
|
||||
{ id: 4, time: '16:00', title: '新药培训', location: '培训中心', type: 'success', tag: '培训' }
|
||||
]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取排班信息异常:', error)
|
||||
// 如果出现异常,使用默认数据
|
||||
scheduleList.value = [
|
||||
{ id: 1, time: '09:00', title: '科室晨会', location: '第一会议室', type: 'info', tag: '日常' },
|
||||
{ id: 2, time: '10:30', title: '病例讨论', location: '第二会议室', type: 'primary', tag: '会议' },
|
||||
{ id: 3, time: '14:00', title: '专家查房', location: '住院部3楼', type: 'warning', tag: '重要' },
|
||||
{ id: 4, time: '16:00', title: '新药培训', location: '培训中心', type: 'success', tag: '培训' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 监听本地存储变化,以便在其他标签页或窗口中修改配置后更新当前页面
|
||||
|
||||
Reference in New Issue
Block a user