feat(web): migrate statistics analytics workflows
This commit is contained in:
1 parent
069e2b300b
commit
d3ff37516b
7 files changed
+205
-2
No files matched your search
@@ -57,6 +57,8 @@
|
||||
| `/classroom-repairs` | 教室报修与维修 | SuperAdmin、AcademicAdmin、MaintenanceAdmin、MaintenanceStaff | 工单筛选、分派/开工/完工、详情、设备档案与扫码报修二维码 | verification |
|
||||
| `/grades/:sheetId/statistics` | 课程成绩统计 | 具备该成绩单数据范围的已登录用户 | 班级/专业/学院/全校分数分布、对比、课程讨论与互动 | verification |
|
||||
| `/event-analytics` | 运行数据分析 | SuperAdmin、AcademicAdmin、CollegeAdmin、Leader | ClickHouse 考勤、成绩与全校范围访问审计只读聚合 | verification |
|
||||
| `/statistics` | 统计报表 | SuperAdmin、AcademicAdmin、CollegeAdmin、Leader | 学生、课程、成绩、通过率、教师工作量和教室利用率;筛选、可视化和 Excel 导出 | verification |
|
||||
| `/grade-analytics` | 成绩分析中心 | SuperAdmin、AcademicAdmin、CollegeAdmin、Leader、Teacher | 教学班/课程成绩比较、刷新设置、Word 报告与课程讨论 | verification |
|
||||
| `/plugins` | 统一插件平台 | SuperAdmin | 运行状态、签名包暂存/激活/回滚、无数据删除的安全停用 | verification |
|
||||
| `/activate` | 学生账号激活 | 公开 | 学籍选项、身份匹配、强密码和一次性激活 | verification |
|
||||
| `/verify/:verificationCode?` | 电子凭证验真 | 公开 | 唯一编号、实时状态、签发信息和 PDF 哈希 | verification |
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { lazy, Suspense, useEffect } from 'react'
|
||||
import { lazy, Suspense, useEffect, type ReactNode } from 'react'
|
||||
import { App as AntdApp, ConfigProvider, Skeleton } from 'antd'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
|
||||
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
||||
import zhCN from 'antd/locale/zh_CN'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import http from '../api/http'
|
||||
|
||||
const LoginPage = lazy(() => import('../pages/LoginPage'))
|
||||
const DashboardPage = lazy(() => import('../pages/DashboardPage'))
|
||||
@@ -57,6 +58,8 @@ const CourseGradeStatisticsPage = lazy(() => import('../pages/CourseGradeStatist
|
||||
const EventAnalyticsPage = lazy(() => import('../pages/EventAnalyticsPage'))
|
||||
const PluginsPage = lazy(() => import('../pages/PluginsPage'))
|
||||
const OperationsPage = lazy(() => import('../pages/OperationsPage'))
|
||||
const StatisticsPage = lazy(() => import('../pages/StatisticsPage'))
|
||||
const GradeAnalyticsPage = lazy(() => import('../pages/GradeAnalyticsPage'))
|
||||
const AccountActivationPage = lazy(() => import('../pages/AccountActivationPage'))
|
||||
const OfficialDocumentVerifyPage = lazy(() => import('../pages/OfficialDocumentVerifyPage'))
|
||||
const ClassroomRepairReportPage = lazy(() => import('../pages/ClassroomRepairReportPage'))
|
||||
@@ -92,6 +95,13 @@ function SessionExpiryBridge() {
|
||||
return null
|
||||
}
|
||||
|
||||
function PluginRoute({ pluginId, children }: { pluginId: string; children: ReactNode }) {
|
||||
const runtime = useQuery({ queryKey: ['plugin-runtime'], queryFn: async () => (await http.get<{ id: string; isEnabled: boolean }[]>('/plugins/runtime')).data })
|
||||
if (runtime.isLoading) return <div style={{ maxWidth: 1180, margin: '64px auto', padding: 24 }}><Skeleton active paragraph={{ rows: 5 }} /></div>
|
||||
if (runtime.data?.some((item) => item.id === pluginId && !item.isEnabled)) return <Navigate to="/dashboard" replace />
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
function AppRoutes() {
|
||||
return (
|
||||
<Suspense fallback={<div style={{ maxWidth: 1180, margin: '64px auto', padding: 24 }}><Skeleton active paragraph={{ rows: 8 }} /></div>}>
|
||||
@@ -162,6 +172,8 @@ function AppRoutes() {
|
||||
<Route path="/event-analytics" element={<EventAnalyticsPage />} />
|
||||
<Route path="/plugins" element={<PluginsPage />} />
|
||||
<Route path="/operations" element={<OperationsPage />} />
|
||||
<Route path="/statistics" element={<PluginRoute pluginId="mingxu.statistics"><StatisticsPage /></PluginRoute>} />
|
||||
<Route path="/grade-analytics" element={<PluginRoute pluginId="mingxu.statistics"><GradeAnalyticsPage /></PluginRoute>} />
|
||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
CarryOutOutlined,
|
||||
SettingOutlined,
|
||||
ToolOutlined,
|
||||
BarChartOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Avatar, Badge, Button, Layout, Menu, Tag } from 'antd'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
@@ -63,6 +64,8 @@ export default function AppShell() {
|
||||
queryFn: async () => (await http.get<{ count: number }>('/notifications/unread-count')).data.count,
|
||||
refetchInterval: 60_000,
|
||||
})
|
||||
const pluginRuntime = useQuery({ queryKey: ['plugin-runtime'], queryFn: async () => (await http.get<{ id: string; isEnabled: boolean }[]>('/plugins/runtime')).data })
|
||||
const statisticsEnabled = pluginRuntime.data?.find((item) => item.id === 'mingxu.statistics')?.isEnabled !== false
|
||||
|
||||
useEffect(() => { void refreshUser().catch(() => undefined) }, [refreshUser])
|
||||
|
||||
@@ -122,6 +125,12 @@ export default function AppShell() {
|
||||
{ key: '/course-enrollments', icon: <BookOutlined />, label: '修读课程管理' },
|
||||
{ key: '/campus-wallets', icon: <WalletOutlined />, label: '学生余额管理' },
|
||||
] : []),
|
||||
...(statisticsEnabled && roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Leader'].includes(role)) ? [
|
||||
{ key: '/statistics', icon: <BarChartOutlined />, label: '统计报表' },
|
||||
] : []),
|
||||
...(statisticsEnabled && roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Leader', 'Teacher'].includes(role)) ? [
|
||||
{ key: '/grade-analytics', icon: <LineChartOutlined />, label: roles.includes('Teacher') && !roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Leader'].includes(role)) ? '教学班成绩分析' : '成绩分析中心' },
|
||||
] : []),
|
||||
...(roles.some((role) => ['SuperAdmin', 'AcademicAdmin'].includes(role)) ? [
|
||||
{ key: '/venue-displays', icon: <FundProjectionScreenOutlined />, label: '场地信息展牌' },
|
||||
{ key: '/base-data/organization', icon: <ApartmentOutlined />, label: '组织机构' },
|
||||
|
||||
Loaded 3 of 7 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user