|
|
|
|
@@ -1,5 +1,5 @@
|
|
|
|
|
import { useEffect, lazy, Suspense, useMemo } from 'react';
|
|
|
|
|
import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
|
|
|
import { HashRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
|
|
|
|
|
import { ConfigProvider, theme as antdTheme, Spin, Result } from 'antd';
|
|
|
|
|
import zhCN from 'antd/locale/zh_CN';
|
|
|
|
|
import MainLayout from './layouts/MainLayout';
|
|
|
|
|
@@ -84,52 +84,71 @@ function FrozenRoute() {
|
|
|
|
|
return <Result status="info" title="功能暂未开放" subTitle="该功能正在优化中,敬请期待" />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ForbiddenPage() {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '60vh' }}>
|
|
|
|
|
<Result
|
|
|
|
|
status="403"
|
|
|
|
|
title="权限不足"
|
|
|
|
|
subTitle="您没有访问此页面的权限,请联系管理员"
|
|
|
|
|
extra={<button onClick={() => navigate('/')} style={{ cursor: 'pointer', color: 'var(--ant-color-primary)', background: 'none', border: 'none', fontSize: 14 }}>返回首页</button>}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ROUTE_PERMISSIONS: Record<string, string[]> = {
|
|
|
|
|
'/users': ['user.list', 'user.manage'],
|
|
|
|
|
'/roles': ['role.list', 'role.manage'],
|
|
|
|
|
'/organizations': ['organization.list', 'organization.manage'],
|
|
|
|
|
'/workflow': ['workflow.list', 'workflow.read'],
|
|
|
|
|
'/messages': ['message.list'],
|
|
|
|
|
'/settings': ['config.settings.list', 'config.settings.manage'],
|
|
|
|
|
'/plugins/admin': ['plugin.list', 'plugin.manage'],
|
|
|
|
|
'/health/patients': ['health.patient.list', 'health.patient.manage'],
|
|
|
|
|
'/health/doctors': ['health.doctor.list', 'health.doctor.manage'],
|
|
|
|
|
'/health/follow-up-tasks': ['health.follow-up.list', 'health.follow-up.manage'],
|
|
|
|
|
'/health/consultations': ['health.consultation.list', 'health.consultation.manage'],
|
|
|
|
|
'/health/action-inbox': ['health.action-inbox.list', 'health.action-inbox.manage'],
|
|
|
|
|
'/health/follow-up-templates': ['health.follow-up-templates.list', 'health.follow-up-templates.manage'],
|
|
|
|
|
'/health/diagnoses': ['health.health-data.list', 'health.health-data.manage'],
|
|
|
|
|
'/health/consents': ['health.consent.list', 'health.consent.manage'],
|
|
|
|
|
'/health/realtime-monitor': ['health.device-readings.list', 'health.device-readings.manage'],
|
|
|
|
|
'/health/alert-dashboard': ['health.alerts.list', 'health.alerts.manage'],
|
|
|
|
|
'/health/alerts': ['health.alerts.list', 'health.alerts.manage'],
|
|
|
|
|
'/health/devices': ['health.devices.list', 'health.devices.manage'],
|
|
|
|
|
'/health/ble-gateways': ['health.ble-gateways.list', 'health.ble-gateways.manage'],
|
|
|
|
|
'/health/critical-value-thresholds': ['health.critical-value-thresholds.list', 'health.critical-value-thresholds.manage'],
|
|
|
|
|
'/health/articles': ['health.articles.list', 'health.articles.manage'],
|
|
|
|
|
'/health/points-rules': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/points-products': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/points-orders': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/offline-events': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/ai-prompts': ['ai.prompt.list', 'ai.prompt.manage'],
|
|
|
|
|
'/health/ai-analysis': ['ai.analysis.list', 'ai.analysis.manage'],
|
|
|
|
|
'/health/ai-usage': ['ai.usage.list'],
|
|
|
|
|
'/health/oauth-clients': ['health.oauth.list', 'health.oauth.manage'],
|
|
|
|
|
'/health/statistics': ['health.health-data.list', 'health.dashboard.manage'],
|
|
|
|
|
'/health/tags': ['health.patient.list', 'health.patient.manage'],
|
|
|
|
|
'/health/daily-monitoring': ['health.device-readings.list', 'health.device-readings.manage'],
|
|
|
|
|
'/health/alert-rules': ['health.alert-rules.list', 'health.alert-rules.manage'],
|
|
|
|
|
'/health/medication-records': ['health.medication-records.manage'],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function PrivateRoute({ children }: { children: React.ReactNode }) {
|
|
|
|
|
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
|
|
|
const permissions = useAuthStore((s) => s.permissions);
|
|
|
|
|
const location = useLocation();
|
|
|
|
|
|
|
|
|
|
if (!isAuthenticated) return <Navigate to="/login" replace />;
|
|
|
|
|
|
|
|
|
|
// 路由级权限检查:如果用户对某个模块完全没有权限,重定向到首页
|
|
|
|
|
const path = window.location.hash.replace('#', '');
|
|
|
|
|
const routePermissions: Record<string, string[]> = {
|
|
|
|
|
'/users': ['user.list', 'user.manage'],
|
|
|
|
|
'/roles': ['role.list', 'role.manage'],
|
|
|
|
|
'/organizations': ['organization.list', 'organization.manage'],
|
|
|
|
|
'/workflow': ['workflow.list', 'workflow.read'],
|
|
|
|
|
'/messages': ['message.list'],
|
|
|
|
|
'/settings': ['config.settings.list', 'config.settings.manage'],
|
|
|
|
|
'/plugins/admin': ['plugin.list', 'plugin.manage'],
|
|
|
|
|
'/health/patients': ['health.patient.list', 'health.patient.manage'],
|
|
|
|
|
'/health/doctors': ['health.doctor.list', 'health.doctor.manage'],
|
|
|
|
|
'/health/follow-up-tasks': ['health.follow-up.list', 'health.follow-up.manage'],
|
|
|
|
|
'/health/consultations': ['health.consultation.list', 'health.consultation.manage'],
|
|
|
|
|
'/health/action-inbox': ['health.action-inbox.list', 'health.action-inbox.manage'],
|
|
|
|
|
'/health/follow-up-templates': ['health.follow-up-templates.list', 'health.follow-up-templates.manage'],
|
|
|
|
|
'/health/diagnoses': ['health.health-data.list', 'health.health-data.manage'],
|
|
|
|
|
'/health/consents': ['health.consent.list', 'health.consent.manage'],
|
|
|
|
|
'/health/realtime-monitor': ['health.device-readings.list', 'health.device-readings.manage'],
|
|
|
|
|
'/health/alert-dashboard': ['health.alerts.list', 'health.alerts.manage'],
|
|
|
|
|
'/health/devices': ['health.devices.list', 'health.devices.manage'],
|
|
|
|
|
'/health/ble-gateways': ['health.ble-gateways.list', 'health.ble-gateways.manage'],
|
|
|
|
|
'/health/critical-value-thresholds': ['health.critical-value-thresholds.list', 'health.critical-value-thresholds.manage'],
|
|
|
|
|
'/health/articles': ['health.articles.list', 'health.articles.manage'],
|
|
|
|
|
'/health/points-rules': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/points-products': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/points-orders': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/offline-events': ['health.points.list', 'health.points.manage'],
|
|
|
|
|
'/health/ai-prompts': ['ai.prompt.list', 'ai.prompt.manage'],
|
|
|
|
|
'/health/ai-analysis': ['ai.analysis.list', 'ai.analysis.manage'],
|
|
|
|
|
'/health/ai-usage': ['ai.usage.list'],
|
|
|
|
|
'/health/oauth-clients': ['health.oauth.list', 'health.oauth.manage'],
|
|
|
|
|
'/health/statistics': ['health.health-data.list', 'health.dashboard.manage'],
|
|
|
|
|
'/health/tags': ['health.patient.list', 'health.patient.manage'],
|
|
|
|
|
};
|
|
|
|
|
const matchedPrefix = Object.keys(routePermissions).find((prefix) => path.startsWith(prefix));
|
|
|
|
|
const path = location.pathname;
|
|
|
|
|
const matchedPrefix = Object.keys(ROUTE_PERMISSIONS).find((prefix) => path.startsWith(prefix));
|
|
|
|
|
if (matchedPrefix) {
|
|
|
|
|
const required = routePermissions[matchedPrefix];
|
|
|
|
|
const required = ROUTE_PERMISSIONS[matchedPrefix];
|
|
|
|
|
const hasAccess = required.some((r) => permissions.includes(r));
|
|
|
|
|
if (!hasAccess) return <Navigate to="/" replace />;
|
|
|
|
|
if (!hasAccess) return <ForbiddenPage />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 冻结路由检查
|
|
|
|
|
|