教学点名:教学班、考勤表均改为服务端分页;支持课程/任务关键词、学期、考勤表名称/备注、状态、签到方式筛选。
成绩管理:确认原本已是服务端分页,且已有学期、状态、课程关键词与学生关键词筛选,无需重复改造。 新增服务端分页与筛选测试。
This commit is contained in:
@@ -53,6 +53,16 @@ const isNativeApp = Capacitor.isNativePlatform()
|
||||
const now = ref(Date.now())
|
||||
const fileInput = ref<HTMLInputElement>()
|
||||
const termId = ref<string>()
|
||||
const taskKeyword = ref('')
|
||||
const taskPage = ref(1)
|
||||
const taskPageSize = 12
|
||||
const taskTotal = ref(0)
|
||||
const sheetKeyword = ref('')
|
||||
const sheetStatus = ref<string>()
|
||||
const sheetMethod = ref<string>()
|
||||
const sheetPage = ref(1)
|
||||
const sheetPageSize = 12
|
||||
const sheetTotal = ref(0)
|
||||
const activeMode = ref<'rollcall' | 'statistics'>('rollcall')
|
||||
const studentKeyword = ref('')
|
||||
const classFilter = ref('')
|
||||
@@ -108,18 +118,27 @@ const filteredStudents = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
async function loadTasks() {
|
||||
async function loadTasks(resetPage = false) {
|
||||
loading.value = true
|
||||
try {
|
||||
tasks.value = (await http.get('/attendance/my-tasks', {
|
||||
params: { academicTermId: termId.value || undefined },
|
||||
if (resetPage) taskPage.value = 1
|
||||
const response = (await http.get('/attendance/my-tasks', {
|
||||
params: {
|
||||
academicTermId: termId.value || undefined,
|
||||
keyword: taskKeyword.value.trim() || undefined,
|
||||
page: taskPage.value,
|
||||
pageSize: taskPageSize,
|
||||
},
|
||||
})).data
|
||||
tasks.value = response.items
|
||||
taskTotal.value = response.total
|
||||
const preferred = tasks.value.find((item: any) => item.id === selectedTask.value?.id)
|
||||
?? tasks.value[0]
|
||||
if (preferred) await selectTask(preferred)
|
||||
else {
|
||||
selectedTask.value = null
|
||||
sheets.value = []
|
||||
sheetTotal.value = 0
|
||||
statistics.value = null
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -137,13 +156,37 @@ async function selectTask(task: any) {
|
||||
studentKeyword.value = ''
|
||||
classFilter.value = ''
|
||||
attentionFilter.value = ''
|
||||
sheetPage.value = 1
|
||||
sheetKeyword.value = ''
|
||||
sheetStatus.value = undefined
|
||||
sheetMethod.value = undefined
|
||||
await loadSheets()
|
||||
if (activeMode.value === 'statistics') await loadStatistics()
|
||||
}
|
||||
|
||||
async function loadSheets(resetPage = false) {
|
||||
if (!selectedTask.value) return
|
||||
try {
|
||||
sheets.value = (await http.get('/attendance/sheets', {
|
||||
params: { teachingTaskId: task.id },
|
||||
if (resetPage) sheetPage.value = 1
|
||||
const response = (await http.get('/attendance/sheets', {
|
||||
params: {
|
||||
teachingTaskId: selectedTask.value.id,
|
||||
keyword: sheetKeyword.value.trim() || undefined,
|
||||
status: sheetStatus.value,
|
||||
checkInMethod: sheetMethod.value,
|
||||
page: sheetPage.value,
|
||||
pageSize: sheetPageSize,
|
||||
},
|
||||
})).data
|
||||
if (activeMode.value === 'statistics') await loadStatistics()
|
||||
sheets.value = response.items
|
||||
sheetTotal.value = response.total
|
||||
if (!sheets.value.some((sheet: any) => sheet.id === selectedSheet.value?.id)) {
|
||||
selectedSheet.value = null
|
||||
sheetDetail.value = null
|
||||
}
|
||||
} catch (error) {
|
||||
sheets.value = []
|
||||
sheetTotal.value = 0
|
||||
ElMessage.error(apiErrorMessage(error))
|
||||
}
|
||||
}
|
||||
@@ -680,14 +723,22 @@ onUnmounted(() => {
|
||||
<h2>教学点名</h2>
|
||||
<p>完成课堂点名,并持续查看本课程每位学生的出勤表现。</p>
|
||||
</div>
|
||||
<el-button :icon="Refresh" @click="loadTasks">刷新</el-button>
|
||||
<el-button :icon="Refresh" @click="() => loadTasks()">刷新</el-button>
|
||||
</section>
|
||||
|
||||
<section class="attendance-toolbar">
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="loadTasks">
|
||||
<el-input
|
||||
v-model="taskKeyword"
|
||||
clearable
|
||||
placeholder="课程、任务号或教学班"
|
||||
@keyup.enter="loadTasks(true)"
|
||||
@clear="loadTasks(true)"
|
||||
/>
|
||||
<el-select v-model="termId" clearable placeholder="全部学期" @change="loadTasks(true)">
|
||||
<el-option v-for="term in terms" :key="term.id" :label="academicTermLabel(term)" :value="term.id" :class="academicTermOptionClass(term)" />
|
||||
</el-select>
|
||||
<span>共 {{ tasks.length }} 个教学班</span>
|
||||
<el-button :icon="Search" @click="loadTasks(true)">查询</el-button>
|
||||
<span>共 {{ taskTotal }} 个教学班</span>
|
||||
<el-radio-group v-model="activeMode" class="mode-switch" size="small">
|
||||
<el-radio-button value="rollcall">点名记录</el-radio-button>
|
||||
<el-radio-button value="statistics">课程统计</el-radio-button>
|
||||
@@ -712,6 +763,16 @@ onUnmounted(() => {
|
||||
<small>{{ task.termName }} · {{ task.studentCount }} 名学生 · {{ task.sheetCount }} 张考勤表</small>
|
||||
</button>
|
||||
<el-empty v-if="!tasks.length" description="没有可点名的教学班" />
|
||||
<el-pagination
|
||||
v-if="taskTotal > taskPageSize"
|
||||
small
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:current-page="taskPage"
|
||||
:page-size="taskPageSize"
|
||||
:total="taskTotal"
|
||||
@current-change="(page: number) => { taskPage = page; loadTasks() }"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<template v-if="activeMode === 'rollcall'">
|
||||
@@ -727,6 +788,25 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<el-button type="primary" :icon="Plus" @click="openCreate">新建考勤表</el-button>
|
||||
</header>
|
||||
<div class="attendance-sheet-filters">
|
||||
<el-input
|
||||
v-model="sheetKeyword"
|
||||
clearable
|
||||
placeholder="考勤表名称或备注"
|
||||
@keyup.enter="loadSheets(true)"
|
||||
@clear="loadSheets(true)"
|
||||
/>
|
||||
<el-select v-model="sheetStatus" clearable placeholder="全部状态" @change="loadSheets(true)">
|
||||
<el-option label="草稿" value="Draft" />
|
||||
<el-option label="已提交" value="Submitted" />
|
||||
</el-select>
|
||||
<el-select v-model="sheetMethod" clearable placeholder="全部方式" @change="loadSheets(true)">
|
||||
<el-option label="教师点名" value="Manual" />
|
||||
<el-option label="扫码签到" value="QrCode" />
|
||||
<el-option label="定位签到" value="Location" />
|
||||
</el-select>
|
||||
<el-button :icon="Search" @click="loadSheets(true)">筛选</el-button>
|
||||
</div>
|
||||
<div class="attendance-sheet-list">
|
||||
<button
|
||||
v-for="sheet in sheets"
|
||||
@@ -766,6 +846,16 @@ onUnmounted(() => {
|
||||
</button>
|
||||
<el-empty v-if="!sheets.length" description="尚未创建考勤表,点击右上方按钮新建" />
|
||||
</div>
|
||||
<el-pagination
|
||||
v-if="sheetTotal > sheetPageSize"
|
||||
small
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:current-page="sheetPage"
|
||||
:page-size="sheetPageSize"
|
||||
:total="sheetTotal"
|
||||
@current-change="(page: number) => { sheetPage = page; loadSheets() }"
|
||||
/>
|
||||
</template>
|
||||
</main>
|
||||
|
||||
@@ -1192,7 +1282,8 @@ onUnmounted(() => {
|
||||
border: 1px solid var(--line);
|
||||
background: #fbfcfd;
|
||||
}
|
||||
.attendance-toolbar .el-select { width: 260px; }
|
||||
.attendance-toolbar .el-input,
|
||||
.attendance-toolbar .el-select { width: 220px; }
|
||||
.attendance-toolbar > span { color: var(--muted); font-size: 12px; }
|
||||
.mode-switch { margin-left: auto; }
|
||||
.attendance-workspace {
|
||||
@@ -1256,6 +1347,21 @@ onUnmounted(() => {
|
||||
display: block;
|
||||
}
|
||||
.attendance-sheet-list { flex: 1; overflow-y: auto; }
|
||||
.attendance-sheet-filters {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: #fbfcfd;
|
||||
}
|
||||
.attendance-sheet-filters .el-input { flex: 1; min-width: 140px; }
|
||||
.attendance-sheet-filters .el-select { width: 115px; }
|
||||
.attendance-task-list :deep(.el-pagination),
|
||||
.attendance-detail :deep(.el-pagination) {
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.attendance-sheet-list > button {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
@@ -1708,7 +1814,11 @@ onUnmounted(() => {
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.attendance-toolbar { align-items: stretch; flex-wrap: wrap; }
|
||||
.attendance-toolbar .el-input,
|
||||
.attendance-toolbar .el-select { width: 100%; }
|
||||
.attendance-sheet-filters { flex-wrap: wrap; }
|
||||
.attendance-sheet-filters .el-input,
|
||||
.attendance-sheet-filters .el-select { width: 100%; flex-basis: 100%; }
|
||||
.attendance-toolbar > span { align-self: center; }
|
||||
.mode-switch { margin-left: auto; }
|
||||
.statistics-head { align-items: flex-start; flex-direction: column; }
|
||||
|
||||
Reference in New Issue
Block a user