deepseek
前台 — 路由 (router/index.ts) - my-timetable 路由的角色从 ['Student'] 扩展为 ['Student', 'Teacher'] - 新增 teacher-timetable/:teacherId 路由,用于查看指定教师的课表 前台 — 课表视图 (TimetableView.vue) - 新增 isTeacherView 和 isTeacher 计算属性,区分教师个人课表和查看他人课表 - 教师个人课表:标题显示"我的授课课表",描述改为"展示本人本学期所有授课安排" - 查看他人课表:标题显示"教师课表" - 两种模式均隐藏资源筛选面板,只保留学期选择 - 加载逻辑适配教师模式 API 前台 — 教师档案页 (PersonnelView.vue) - 教师行操作列新增 "课表" 按钮(带日历图标),仅在职教师可点击 - 点击后跳转到该教师的授课课表页面 前台 — 侧边导航 (AdminLayout.vue) - 教师角色可见"我的授课课表"菜单项,与学生的"我的课表"并列
This commit is contained in:
1 parent
a081273ac6
commit
7800951aee
5 files changed
+171
-37
No files matched your search
@@ -91,6 +91,45 @@ public sealed class TimetablesController(
|
|||||||
CancellationToken cancellationToken) =>
|
CancellationToken cancellationToken) =>
|
||||||
BuildTimetableAsync(classId, academicTermId, null, cancellationToken);
|
BuildTimetableAsync(classId, academicTermId, null, cancellationToken);
|
||||||
|
|
||||||
|
[HttpGet("teachers/{teacherId:guid}")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> GetTeacherTimetable(
|
||||||
|
Guid teacherId,
|
||||||
|
Guid? academicTermId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var result = await timetableDataService.BuildAsync(
|
||||||
|
TimetableResourceType.Teacher,
|
||||||
|
teacherId,
|
||||||
|
academicTermId,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
return result is null ? NotFound() : Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("teachers/{teacherId:guid}/export.xlsx")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> ExportTeacherTimetable(
|
||||||
|
Guid teacherId,
|
||||||
|
Guid? academicTermId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var result = await timetableDataService.BuildAsync(
|
||||||
|
TimetableResourceType.Teacher,
|
||||||
|
teacherId,
|
||||||
|
academicTermId,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
if (result is null) return NotFound();
|
||||||
|
return ExcelFile(result);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("classes/{classId:guid}/export.xlsx")]
|
[HttpGet("classes/{classId:guid}/export.xlsx")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<ActionResult> ExportClassTimetable(
|
public async Task<ActionResult> ExportClassTimetable(
|
||||||
@@ -112,13 +151,14 @@ public sealed class TimetablesController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("mine")]
|
[HttpGet("mine")]
|
||||||
[Authorize(Roles = SystemRoles.Student)]
|
[Authorize(Roles = SystemRoles.Student + "," + SystemRoles.Teacher)]
|
||||||
public async Task<ActionResult> GetMyTimetable(
|
public async Task<ActionResult> GetMyTimetable(
|
||||||
Guid? academicTermId,
|
Guid? academicTermId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!Guid.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out var userId))
|
if (!Guid.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out var userId))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
var student = await db.Students.AsNoTracking()
|
var student = await db.Students.AsNoTracking()
|
||||||
.Where(x => x.UserId == userId)
|
.Where(x => x.UserId == userId)
|
||||||
.Select(x => new
|
.Select(x => new
|
||||||
@@ -129,30 +169,51 @@ public sealed class TimetablesController(
|
|||||||
x.Name
|
x.Name
|
||||||
})
|
})
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
if (student is null)
|
if (student is not null)
|
||||||
return Conflict(new ProblemDetails
|
{
|
||||||
{
|
return await BuildTimetableAsync(
|
||||||
Title = "学生档案未关联",
|
student.AdministrativeClassId,
|
||||||
Detail = "当前登录账号没有关联学生档案,请联系教务管理员。",
|
academicTermId,
|
||||||
Status = StatusCodes.Status409Conflict
|
student.Id,
|
||||||
});
|
cancellationToken,
|
||||||
|
new TimetableStudentDto(student.StudentNumber, student.Name));
|
||||||
|
}
|
||||||
|
|
||||||
return await BuildTimetableAsync(
|
var teacher = await db.Teachers.AsNoTracking()
|
||||||
student.AdministrativeClassId,
|
.Where(x => x.UserId == userId && x.Status == TeacherStatus.Active)
|
||||||
academicTermId,
|
.Select(x => new { x.Id })
|
||||||
student.Id,
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
cancellationToken,
|
if (teacher is not null)
|
||||||
new TimetableStudentDto(student.StudentNumber, student.Name));
|
{
|
||||||
|
var result = await timetableDataService.BuildAsync(
|
||||||
|
TimetableResourceType.Teacher,
|
||||||
|
teacher.Id,
|
||||||
|
academicTermId,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
return result is null ? NotFound() : Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Conflict(new ProblemDetails
|
||||||
|
{
|
||||||
|
Title = "档案未关联",
|
||||||
|
Detail = "当前登录账号没有关联教师或学生档案,请联系教务管理员。",
|
||||||
|
Status = StatusCodes.Status409Conflict
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("mine/export.xlsx")]
|
[HttpGet("mine/export.xlsx")]
|
||||||
[Authorize(Roles = SystemRoles.Student)]
|
[Authorize(Roles = SystemRoles.Student + "," + SystemRoles.Teacher)]
|
||||||
public async Task<ActionResult> ExportMyTimetable(
|
public async Task<ActionResult> ExportMyTimetable(
|
||||||
Guid? academicTermId,
|
Guid? academicTermId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!Guid.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out var userId))
|
if (!Guid.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out var userId))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
var student = await db.Students.AsNoTracking()
|
var student = await db.Students.AsNoTracking()
|
||||||
.Where(x => x.UserId == userId)
|
.Where(x => x.UserId == userId)
|
||||||
.Select(x => new
|
.Select(x => new
|
||||||
@@ -163,18 +224,41 @@ public sealed class TimetablesController(
|
|||||||
x.Name
|
x.Name
|
||||||
})
|
})
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
if (student is null) return NotFound();
|
if (student is not null)
|
||||||
var result = await timetableDataService.BuildAsync(
|
{
|
||||||
TimetableResourceType.Class,
|
var result = await timetableDataService.BuildAsync(
|
||||||
student.AdministrativeClassId,
|
TimetableResourceType.Class,
|
||||||
academicTermId,
|
student.AdministrativeClassId,
|
||||||
null,
|
academicTermId,
|
||||||
false,
|
null,
|
||||||
student.Id,
|
false,
|
||||||
new TimetableStudentDto(student.StudentNumber, student.Name),
|
student.Id,
|
||||||
cancellationToken);
|
new TimetableStudentDto(student.StudentNumber, student.Name),
|
||||||
if (result is null) return NotFound();
|
cancellationToken);
|
||||||
return ExcelFile(result);
|
if (result is null) return NotFound();
|
||||||
|
return ExcelFile(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
var teacher = await db.Teachers.AsNoTracking()
|
||||||
|
.Where(x => x.UserId == userId && x.Status == TeacherStatus.Active)
|
||||||
|
.Select(x => new { x.Id })
|
||||||
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
if (teacher is not null)
|
||||||
|
{
|
||||||
|
var result = await timetableDataService.BuildAsync(
|
||||||
|
TimetableResourceType.Teacher,
|
||||||
|
teacher.Id,
|
||||||
|
academicTermId,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationToken);
|
||||||
|
if (result is null) return NotFound();
|
||||||
|
return ExcelFile(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ActionResult> BuildTimetableAsync(
|
private async Task<ActionResult> BuildTimetableAsync(
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const navigationGroups = computed<NavigationGroup[]>(() => [
|
|||||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
|
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
|
||||||
{ path: '/schedules', label: '排课与课表' },
|
{ path: '/schedules', label: '排课与课表' },
|
||||||
),
|
),
|
||||||
...whenVisible(isStudent.value, { path: '/my-timetable', label: '我的课表' }),
|
...whenVisible(isStudent.value || isTeacher.value, { path: '/my-timetable', label: isTeacher.value ? '我的授课课表' : '我的课表' }),
|
||||||
...whenVisible(isStudent.value, { path: '/free-classrooms', label: '空闲教室' }),
|
...whenVisible(isStudent.value, { path: '/free-classrooms', label: '空闲教室' }),
|
||||||
...whenVisible(!isStudent.value, {
|
...whenVisible(!isStudent.value, {
|
||||||
path: '/class-timetable',
|
path: '/class-timetable',
|
||||||
|
|||||||
@@ -141,7 +141,13 @@ const router = createRouter({
|
|||||||
path: 'my-timetable',
|
path: 'my-timetable',
|
||||||
name: 'my-timetable',
|
name: 'my-timetable',
|
||||||
component: () => import('../views/TimetableView.vue'),
|
component: () => import('../views/TimetableView.vue'),
|
||||||
meta: { roles: ['Student'], mine: true },
|
meta: { roles: ['Student', 'Teacher'], mine: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'teacher-timetable/:teacherId',
|
||||||
|
name: 'teacher-timetable',
|
||||||
|
component: () => import('../views/TimetableView.vue'),
|
||||||
|
meta: { teacherView: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'free-classrooms',
|
path: 'free-classrooms',
|
||||||
|
|||||||
Loaded 3 of 5 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user