Files
OJ2/apps/web/src/routes.ts
T
xuyueandClaude Opus 5.5 b344bd1dd6
Deploy / deploy (push) Canceled after 0s
feat(布置作业): 能按知识点点选题目;一个班一天能布置几节课;数据统计认老师布置的课;后台能管布置过的课
- 课堂看板「选题」弹框:左边知识点(加必会题),右边搜题号/题名、点选,按点的先后排;
  只列这次作业的语言能交的题(/problems、/problem-tags 加 ?language=)
- 一天多节课:0040 拿掉 class_lesson (class_name, day) 唯一约束,每次布置一行;
  看板「改」只改当前一节,加「新的一节」;这一节布置超过 100 分钟时先问这节课做哪几道题。
  不带 lessonId/newLesson 的老请求照旧改今天最后一节
- 数据统计「一行一节课」:布置过的课以布置为准(题目和顺序照布置、做完按全班算、标「布置」),
  这些提交不再参与推断切段,也不算零散;详情页「做完」注明是这一天/这节课/这段时间
- 后台「课堂作业」:一节一行,改(班级/日子/时刻/语言/题目)、看统计、删除、补一节;
  看板加「布置过的课」入口。题号校验和「做完」口径抽到 services/lesson.ts,三处共用

⚠️ 机房站点要和服务器一起升级:旧代码布置时 ON CONFLICT (class_name, day) 找不到约束会报错

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:41:25 +08:00

354 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { RouteRecordRaw } from 'vue-router'
export const ojs: RouteRecordRaw = {
path: '/',
component: () => import('shared/layout/default.vue'),
children: [
// 登录后是个人首页,没登录还是题目列表(见 oj/home/index.vue)
{ path: '', component: () => import('oj/home/index.vue'), name: 'home' },
{ path: 'problem', component: () => import('oj/problem/list.vue'), name: 'problems' },
{
path: 'problem/:problemID',
component: () => import('oj/problem/detail.vue'),
props: true,
name: 'problem',
},
{
path: 'submission',
component: () => import('oj/submission/list.vue'),
name: 'submissions',
},
{
path: 'submission/:submissionID',
component: () => import('oj/submission/detail.vue'),
props: true,
meta: { requiresAuth: true },
},
{
path: 'contest',
component: () => import('oj/contest/list.vue'),
name: 'contests',
},
{
path: 'contest/:contestID',
component: () => import('oj/contest/detail.vue'),
props: true,
meta: { requiresAuth: true },
children: [
{
path: '',
component: () => import('oj/contest/pages/problems.vue'),
props: true,
meta: { requiresAuth: true },
name: 'contest problems',
},
{
path: 'submission',
component: () => import('oj/submission/list.vue'),
meta: { requiresAuth: true },
name: 'contest submissions',
},
{
path: 'rank',
component: () => import('oj/contest/pages/rank.vue'),
props: true,
meta: { requiresAuth: true },
name: 'contest rank',
},
// 老师的「全班情况」,考完是「成绩」(代替原来后台的「审核」页)
{
path: 'class',
component: () => import('oj/contest/pages/class.vue'),
props: true,
meta: { requiresAuth: true },
name: 'contest class',
},
],
},
{
path: 'contest/:contestID/problem/:problemID',
component: () => import('oj/problem/detail.vue'),
props: true,
name: 'contest problem',
meta: { requiresAuth: true },
},
{
path: 'rank',
name: 'rank',
component: () => import('oj/rank/list.vue'),
},
{
path: 'classroom',
name: 'classroom board',
component: () => import('oj/classroom/board.vue'),
meta: { requiresAuth: true, requiresTeacherAdmin: true },
},
{
path: 'statistics',
name: 'statistics',
component: () => import('oj/statistics/index.vue'),
meta: { requiresAuth: true, requiresTeacherAdmin: true },
},
{
path: 'class',
name: 'class',
component: () => import('oj/class/pk.vue'),
meta: { requiresAuth: true },
},
{
path: 'announcement',
name: 'announcements',
component: () => import('oj/announcement/list.vue'),
},
{
path: 'user',
component: () => import('oj/user/index.vue'),
meta: { requiresAuth: true },
},
{
path: 'achievement',
name: 'achievement',
component: () => import('oj/achievement/index.vue'),
meta: { requiresAuth: true },
},
{
path: 'learn',
name: 'learn home',
component: () => import('oj/learn/home.vue'),
},
{
path: 'learn/:type/:step',
name: 'learn',
component: () => import('oj/learn/index.vue'),
props: true,
},
{
path: 'ai-analysis',
name: 'ai',
component: () => import('oj/ai/analysis.vue'),
meta: { requiresAuth: true },
},
{
path: 'problemset',
component: () => import('oj/problemset/list.vue'),
name: 'problemsets',
},
{
path: 'problemset/:problemSetId',
component: () => import('oj/problemset/detail.vue'),
props: true,
name: 'problemset',
},
{
path: 'problemset/:problemSetId/problem/:problemID',
component: () => import('oj/problem/detail.vue'),
props: true,
name: 'problemset problem',
meta: { requiresAuth: true },
},
// 兜底:没有它,打错地址是一整页空白,连顶栏都没有。vue-router 按路径具体程度排序,
// 不按注册顺序,所以它吃不掉别的路由;/admin 下打错的也落到这里
{
path: ':pathMatch(.*)*',
name: 'not found',
component: () => import('shared/components/NotFound.vue'),
props: { title: '这个页面不存在', hint: '可能是地址打错了,或者这个页面已经下线' },
},
],
}
export const admins: RouteRecordRaw = {
path: '/admin',
component: () => import('shared/layout/admin.vue'),
children: [
{
path: '',
name: 'admin home',
component: () => import('admin/setting/home.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'config',
name: 'admin config',
component: () => import('admin/setting/config.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'user/list',
name: 'admin user list',
component: () => import('admin/user/list.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'user/generate',
name: 'admin user generate',
component: () => import('admin/user/generate.vue'),
meta: { requiresSuperAdmin: true },
},
// admin和super_admin都可以访问的路由 (需要题目权限)
{
path: 'problem/list',
name: 'admin problem list',
component: () => import('admin/problem/list.vue'),
meta: { requiresProblemPermission: true },
},
{
path: 'problem/tags',
name: 'admin tag list',
component: () => import('admin/problem/tags.vue'),
meta: { requiresProblemPermission: true },
},
{
path: 'problem/create',
name: 'admin problem create',
component: () => import('admin/problem/editor.vue'),
meta: { requiresProblemPermission: true },
},
{
path: 'problem/edit/:problemID',
name: 'admin problem edit',
component: () => import('admin/problem/editor.vue'),
props: true,
meta: { requiresProblemPermission: true },
},
{
path: 'contest/list',
name: 'admin contest list',
component: () => import('admin/contest/list.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'contest/create',
name: 'admin contest create',
component: () => import('admin/contest/detail.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'contest/edit/:contestID',
name: 'admin contest edit',
component: () => import('admin/contest/detail.vue'),
props: true,
meta: { requiresTeacherAdmin: true },
},
{
path: 'contest/:contestID/problem/list',
name: 'admin contest problem list',
component: () => import('admin/problem/list.vue'),
props: true,
meta: { requiresTeacherAdmin: true },
},
{
path: 'contest/:contestID/problem/create',
name: 'admin contest problem create',
component: () => import('admin/problem/editor.vue'),
props: true,
meta: { requiresTeacherAdmin: true },
},
{
path: 'contest/:contestID/problem/edit/:problemID',
name: 'admin contest problem edit',
component: () => import('admin/problem/editor.vue'),
props: true,
meta: { requiresTeacherAdmin: true },
},
// 只有super_admin可以访问的路由
{
path: 'announcement/list',
name: 'admin announcement list',
component: () => import('admin/announcement/list.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'announcement/create',
name: 'admin announcement create',
component: () => import('admin/announcement/detail.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'announcement/edit/:announcementID',
name: 'admin announcement edit',
component: () => import('admin/announcement/detail.vue'),
props: true,
meta: { requiresSuperAdmin: true },
},
{
path: 'tutorial/list',
name: 'admin tutorial list',
component: () => import('admin/tutorial/list.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'tutorial/create',
name: 'admin tutorial create',
component: () => import('admin/tutorial/detail.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'tutorial/edit/:tutorialID',
name: 'admin tutorial edit',
component: () => import('admin/tutorial/detail.vue'),
props: true,
meta: { requiresSuperAdmin: true },
},
{
path: 'problem/stuck',
name: 'admin stuck problems',
component: () => import('admin/problem/Stuck.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'problem/trend',
name: 'admin grade trend',
component: () => import('admin/problem/Trend.vue'),
meta: { requiresTeacherAdmin: true },
},
{
// 课堂看板布置的每一节课:改、删、补(数据统计按布置切课)
path: 'lesson',
name: 'admin lesson list',
component: () => import('admin/lesson/list.vue'),
meta: { requiresTeacherAdmin: true },
},
// 题单管理路由
{
path: 'problemset/list',
name: 'admin problemset list',
component: () => import('admin/problemset/list.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'achievement/list',
name: 'admin achievement list',
// 后端 admin 接口是 super_admin_required,这里必须跟着用
// requiresSuperAdmin,否则教师能进页面但每个请求都 403
component: () => import('admin/achievement/list.vue'),
meta: { requiresSuperAdmin: true },
},
{
path: 'problemset/create',
name: 'admin problemset create',
component: () => import('admin/problemset/edit.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'problemset/edit/:problemSetId',
name: 'admin problemset edit',
component: () => import('admin/problemset/edit.vue'),
props: true,
meta: { requiresTeacherAdmin: true },
},
{
path: 'ai/reports',
name: 'admin ai reports',
component: () => import('admin/ai/list.vue'),
meta: { requiresTeacherAdmin: true },
},
{
path: 'learn',
name: 'admin learn analytics',
component: () => import('admin/learn/index.vue'),
meta: { requiresTeacherAdmin: true },
},
],
}