课程库:保留并验证现有 Excel 导入,支持模板下载、按编码新增/更新、整批校验及事务回滚。
授课资格:学院可直接选择本院教师、学期和课程并分配资格,服务端强制学院数据范围。[后端接口 (line 178)](/E:/jiaowu/src/Jiaowu.Api/Controllers/TeacherCourseApplicationsController.cs:178) · [页面 (line 151)](/E:/jiaowu/web/src/views/TeachingPreferencesView.vue:151) 学生培养方案:新增“我的培养方案”,展示学分进度及已完成、在读、重修中、未通过、待完成、未修读课程,并支持搜索筛选。[学生接口 (line 15)](/E:/jiaowu/src/Jiaowu.Api/Controllers/StudentCurriculumController.cs:15) · [学生页面 (line 88)](/E:/jiaowu/web/src/views/StudentCurriculumView.vue:88)
This commit is contained in:
1 parent
0d34936d38
commit
9a3afa3df5
9 files changed
+1119
-8
No files matched your search
@@ -5,15 +5,22 @@ import http, { apiErrorMessage } from '../api/http'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
|
||||
const auth = useAuthStore()
|
||||
const isTeacher = computed(() => auth.user?.roles.includes('Teacher') ?? false)
|
||||
const isReviewer = computed(() =>
|
||||
auth.user?.roles.some((role) =>
|
||||
['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)) ?? false)
|
||||
const isTeacher = computed(() =>
|
||||
(auth.user?.roles.includes('Teacher') ?? false) && !isReviewer.value)
|
||||
const loading = ref(false)
|
||||
const rows = ref<any[]>([])
|
||||
const terms = ref<any[]>([])
|
||||
const courses = ref<any[]>([])
|
||||
const teachers = ref<any[]>([])
|
||||
const submitDialog = ref(false)
|
||||
const reviewDialog = ref(false)
|
||||
const assignmentDialog = ref(false)
|
||||
const form = reactive<Record<string, any>>({})
|
||||
const reviewForm = reactive<Record<string, any>>({})
|
||||
const assignmentForm = reactive<Record<string, any>>({})
|
||||
const filters = reactive({
|
||||
academicTermId: undefined as string | undefined,
|
||||
status: isTeacher.value ? undefined : 'Pending' as string | undefined,
|
||||
@@ -108,14 +115,49 @@ async function review() {
|
||||
}
|
||||
}
|
||||
|
||||
function openAssignment() {
|
||||
Object.assign(assignmentForm, {
|
||||
academicTermId: filters.academicTermId ??
|
||||
terms.value.find((item) => item.isCurrent)?.id,
|
||||
teacherId: undefined,
|
||||
courseId: undefined,
|
||||
comment: '',
|
||||
})
|
||||
assignmentDialog.value = true
|
||||
}
|
||||
|
||||
async function assignTeacher() {
|
||||
if (!assignmentForm.academicTermId ||
|
||||
!assignmentForm.teacherId ||
|
||||
!assignmentForm.courseId) {
|
||||
ElMessage.warning('请选择学期、教师和课程。')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await http.post('/teacher-course-applications/assign', assignmentForm)
|
||||
assignmentDialog.value = false
|
||||
ElMessage.success('授课资格已直接分配')
|
||||
await load()
|
||||
} catch (error) {
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const requests: Promise<any>[] = [http.get('/base-data/terms')]
|
||||
if (isTeacher.value) {
|
||||
requests.push(http.get('/teacher-course-applications/course-options'))
|
||||
} else {
|
||||
requests.push(http.get('/teacher-course-applications/assignment-options'))
|
||||
}
|
||||
const [termRes, courseRes] = await Promise.all(requests)
|
||||
const [termRes, optionRes] = await Promise.all(requests)
|
||||
terms.value = termRes.data
|
||||
courses.value = courseRes?.data ?? []
|
||||
if (isTeacher.value) {
|
||||
courses.value = optionRes?.data ?? []
|
||||
} else {
|
||||
courses.value = optionRes?.data?.courses ?? []
|
||||
teachers.value = optionRes?.data?.teachers ?? []
|
||||
}
|
||||
filters.academicTermId = terms.value.find((item) => item.isCurrent)?.id
|
||||
await load()
|
||||
})
|
||||
@@ -128,17 +170,22 @@ onMounted(async () => {
|
||||
<span class="section-kicker">TEACHING ELIGIBILITY</span>
|
||||
<h2>{{ isTeacher ? '授课科目申报' : '授课资格审核' }}</h2>
|
||||
<p v-if="isTeacher">在排课前申报本学期愿意承担的课程,学院审核通过后才能分配公共课教学任务。</p>
|
||||
<p v-else>审核教师的授课科目申报;通过后,该教师进入相应学期和课程的可分配教师池。</p>
|
||||
<p v-else>审核教师申报,或由学院直接指定教师;通过后进入相应学期和课程的可分配教师池。</p>
|
||||
</div>
|
||||
<div class="page-actions">
|
||||
<el-button v-if="isTeacher" type="primary" :icon="Plus" @click="openSubmit">
|
||||
申报课程
|
||||
</el-button>
|
||||
<el-button v-else type="primary" :icon="Plus" @click="openAssignment">
|
||||
直接分配教师
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button v-if="isTeacher" type="primary" :icon="Plus" @click="openSubmit">
|
||||
申报课程
|
||||
</el-button>
|
||||
</section>
|
||||
|
||||
<section class="eligibility-flow">
|
||||
<div class="active"><span>教师</span><b>选择学期与科目</b></div>
|
||||
<i>→</i>
|
||||
<div><span>学院</span><b>审核授课资格</b></div>
|
||||
<div><span>学院</span><b>审核或直接分配</b></div>
|
||||
<i>→</i>
|
||||
<div><span>教务</span><b>合班并分配教师</b></div>
|
||||
<i>→</i>
|
||||
@@ -228,5 +275,82 @@ onMounted(async () => {
|
||||
</el-form>
|
||||
<template #footer><el-button @click="reviewDialog = false">取消</el-button><el-button type="primary" @click="review">确认审核</el-button></template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="assignmentDialog" title="直接分配授课教师" width="640px">
|
||||
<el-alert
|
||||
title="无需教师先申报,保存后立即取得所选学期与课程的授课资格。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-form label-position="top" style="margin-top: 18px">
|
||||
<el-form-item label="学期" required>
|
||||
<el-select v-model="assignmentForm.academicTermId" style="width: 100%">
|
||||
<el-option v-for="item in terms" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div class="form-grid">
|
||||
<el-form-item label="教师" required>
|
||||
<el-select
|
||||
v-model="assignmentForm.teacherId"
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="按工号或姓名选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in teachers"
|
||||
:key="item.id"
|
||||
:label="`${item.teacherNumber} · ${item.name}`"
|
||||
:value="item.id"
|
||||
>
|
||||
<span>{{ item.teacherNumber }} · {{ item.name }}</span>
|
||||
<small class="option-hint">{{ item.collegeName }}{{ item.title ? ` · ${item.title}` : '' }}</small>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="课程" required>
|
||||
<el-select
|
||||
v-model="assignmentForm.courseId"
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="按编码或名称选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in courses"
|
||||
:key="item.id"
|
||||
:label="`${item.code} · ${item.name}`"
|
||||
:value="item.id"
|
||||
>
|
||||
<span>{{ item.code }} · {{ item.name }}</span>
|
||||
<small class="option-hint">{{ item.collegeName }} · {{ item.credits }} 学分</small>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="分配说明">
|
||||
<el-input
|
||||
v-model="assignmentForm.comment"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="可填写教研室安排、专业方向等依据"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="assignmentDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="assignTeacher">确认分配</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.option-hint {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
color: #8a93a5;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user