优化服务器端分页

This commit is contained in:
2026-08-11 11:52:43 +08:00 Unverified
parent 83103eebb8
commit 5d7322507b
6 changed files with 225 additions and 50 deletions
+42 -10
View File
@@ -27,6 +27,11 @@ const sessionTask = ref<any>(null)
const sessionLoading = ref(false)
const termId = ref<string>()
const tab = ref(isManager.value ? 'reviews' : 'mine')
const minePage = ref(1)
const mineTotal = ref(0)
const reviewPage = ref(1)
const reviewTotal = ref(0)
const pageSize = 20
const form = reactive({
teachingTaskId: undefined as string | undefined,
@@ -79,21 +84,30 @@ function sessionLabel(session: any) {
return `${session.week} 周 · ${session.date}${weekdayLabel(session.dayOfWeek)} · 第 ${session.startPeriod}${end}${room}`
}
async function load() {
async function load(resetPages = false) {
if (resetPages) {
minePage.value = 1
reviewPage.value = 1
}
loading.value = true
reviewLoading.value = isManager.value
try {
if (isTeacher.value || (!isManager.value)) {
adjustments.value = (await http.get('/course-adjustments/mine', {
params: { academicTermId: termId.value },
})).data
const response = await http.get('/course-adjustments/mine', {
params: { academicTermId: termId.value, page: minePage.value, pageSize },
})
adjustments.value = response.data.items
mineTotal.value = response.data.total
}
if (isManager.value) {
pendingReviews.value = (await http.get('/course-adjustments/pending-reviews', {
params: { academicTermId: termId.value },
})).data
const response = await http.get('/course-adjustments/pending-reviews', {
params: { academicTermId: termId.value, page: reviewPage.value, pageSize },
})
pendingReviews.value = response.data.items
reviewTotal.value = response.data.total
}
} catch (e) { ElMessage.error(apiErrorMessage(e)) }
finally { loading.value = false }
finally { loading.value = false; reviewLoading.value = false }
}
function openCreate() {
@@ -255,12 +269,12 @@ function showSub(type: string) { return type === 'Substitute' }
</div>
<div style="display:flex;gap:8px;align-items:center">
<el-button v-if="isTeacher" type="primary" :icon="Plus" @click="openCreate">提交申请</el-button>
<el-button :icon="Refresh" @click="load">刷新</el-button>
<el-button :icon="Refresh" @click="() => load()">刷新</el-button>
</div>
</section>
<section class="adj-toolbar">
<el-select v-model="termId" clearable placeholder="全部学期" @change="load(); loadTaskOptions()">
<el-select v-model="termId" clearable placeholder="全部学期" @change="() => { load(true); loadTaskOptions() }">
<el-option v-for="t in terms" :key="t.id" :label="academicTermLabel(t)" :value="t.id" :class="academicTermOptionClass(t)" />
</el-select>
<el-segmented v-model="tab" :options="[
@@ -307,6 +321,15 @@ function showSub(type: string) { return type === 'Substitute' }
</footer>
</article>
<el-empty v-if="!adjustments.length" description="暂无调停课申请" />
<el-pagination
v-if="mineTotal > pageSize"
v-model:current-page="minePage"
:page-size="pageSize"
:total="mineTotal"
layout="total, prev, pager, next"
class="table-pagination"
@current-change="() => load()"
/>
</section>
<!-- Pending reviews -->
@@ -345,6 +368,15 @@ function showSub(type: string) { return type === 'Substitute' }
</footer>
</article>
<el-empty v-if="!pendingReviews.length" description="暂无待审核申请" />
<el-pagination
v-if="reviewTotal > pageSize"
v-model:current-page="reviewPage"
:page-size="pageSize"
:total="reviewTotal"
layout="total, prev, pager, next"
class="table-pagination"
@current-change="() => load()"
/>
</section>
<!-- Create dialog -->
+32 -12
View File
@@ -23,6 +23,11 @@ const selected = ref<any | null>(null)
const reviewApproved = ref(true)
const applyForm = reactive({ type: '', reason: '' })
const reviewForm = reactive({ comment: '' })
const page = ref(1)
const total = ref(0)
const actionableTotal = ref(0)
const finishedTotal = ref(0)
const pageSize = 20
const typeLabels: Record<string, string> = {
Suspension: '休学',
@@ -48,10 +53,6 @@ const steps = [
{ label: '学院审核', state: 'CounselorApproved' },
{ label: '校级审批', state: 'CollegeApproved' },
]
const currentQueue = computed(() => changes.value.filter(canReview))
const finishedCount = computed(() =>
changes.value.filter((x) => ['Approved', 'Rejected', 'Cancelled'].includes(x.state)).length)
function dateText(value?: string) {
if (!value) return '—'
return new Intl.DateTimeFormat('zh-CN', {
@@ -90,14 +91,24 @@ function stateTagType(state: ChangeState) {
return 'warning'
}
async function load() {
async function load(resetPage = false) {
if (resetPage) page.value = 1
loading.value = true
try {
const requests = [http.get('/student-status-changes')]
const requests = [http.get('/student-status-changes', {
params: { page: page.value, pageSize },
})]
if (isStudent.value) requests.push(http.get('/student-status-changes/options'))
const [changeResponse, optionResponse] = await Promise.all(requests)
changes.value = changeResponse.data
changes.value = changeResponse.data.items
total.value = changeResponse.data.total
actionableTotal.value = changeResponse.data.actionableTotal
finishedTotal.value = changeResponse.data.finishedTotal
if (optionResponse) options.value = optionResponse.data
if (changes.value.length === 0 && page.value > 1) {
page.value--
await load()
}
} catch (error) {
ElMessage.error(apiErrorMessage(error))
} finally {
@@ -118,7 +129,7 @@ async function submitApply() {
await http.post('/student-status-changes', applyForm)
applyDialog.value = false
ElMessage.success('申请已提交,等待辅导员审核。')
await load()
await load(true)
} catch (error) {
ElMessage.error(apiErrorMessage(error))
}
@@ -177,7 +188,7 @@ onMounted(load)
:disabled="options?.hasPending || !options?.types?.length"
@click="openApply"
>发起申请</el-button>
<el-button v-else :icon="RefreshRight" @click="load">刷新队列</el-button>
<el-button v-else :icon="RefreshRight" @click="() => load()">刷新队列</el-button>
</section>
<section v-if="isStudent && options" class="status-identity">
@@ -194,9 +205,9 @@ onMounted(load)
</section>
<section v-if="!isStudent" class="status-review-summary">
<div><span>当前待我审核</span><b>{{ currentQueue.length }}</b><small></small></div>
<div><span>辖区申请总数</span><b>{{ changes.length }}</b><small></small></div>
<div><span>已结束</span><b>{{ finishedCount }}</b><small></small></div>
<div><span>当前待我审核</span><b>{{ actionableTotal }}</b><small></small></div>
<div><span>辖区申请总数</span><b>{{ total }}</b><small></small></div>
<div><span>已结束</span><b>{{ finishedTotal }}</b><small></small></div>
<p>系统仅开放当前审核层级的操作所有越级请求都会由服务端拒绝</p>
</section>
@@ -243,6 +254,15 @@ onMounted(load)
</footer>
</article>
<el-empty v-if="!changes.length && !loading" :description="isStudent ? '尚未提交学籍异动申请' : '当前辖区暂无学籍异动申请'" />
<el-pagination
v-if="total > pageSize"
v-model:current-page="page"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next"
class="table-pagination"
@current-change="() => load()"
/>
</section>
<el-dialog v-model="applyDialog" title="发起学籍异动申请" width="620px">
+27 -7
View File
@@ -19,6 +19,9 @@ const teachers = ref<any[]>([])
const submitDialog = ref(false)
const reviewDialog = ref(false)
const assignmentDialog = ref(false)
const page = ref(1)
const total = ref(0)
const pageSize = 20
const form = reactive<Record<string, any>>({})
const reviewForm = reactive<Record<string, any>>({})
const assignmentForm = reactive<Record<string, any>>({})
@@ -39,14 +42,22 @@ const statusTypes: Record<string, 'success' | 'warning' | 'danger' | 'info'> = {
Withdrawn: 'info',
}
async function load() {
async function load(resetPage = false) {
if (resetPage) page.value = 1
loading.value = true
try {
const endpoint = isTeacher.value
? '/teacher-course-applications/mine'
: '/teacher-course-applications/reviews'
const { data } = await http.get(endpoint, { params: filters })
rows.value = data
const { data } = await http.get(endpoint, {
params: { ...filters, page: page.value, pageSize },
})
rows.value = data.items
total.value = data.total
if (rows.value.length === 0 && page.value > 1) {
page.value--
await load()
}
} catch (error) {
ElMessage.error(apiErrorMessage(error))
} finally {
@@ -195,14 +206,14 @@ onMounted(async () => {
<section class="data-card">
<div class="filter-bar">
<el-select v-model="filters.academicTermId" clearable placeholder="全部学期" @change="load">
<el-select v-model="filters.academicTermId" clearable placeholder="全部学期" @change="load(true)">
<el-option v-for="item in terms" :key="item.id" :label="academicTermLabel(item)" :value="item.id" :class="academicTermOptionClass(item)" />
</el-select>
<el-select v-model="filters.status" clearable placeholder="全部状态" @change="load">
<el-select v-model="filters.status" clearable placeholder="全部状态" @change="load(true)">
<el-option v-for="(label, value) in statusLabels" :key="value" :label="label" :value="value" />
</el-select>
<el-button :icon="Refresh" @click="load">刷新</el-button>
<span> {{ rows.length }} </span>
<el-button :icon="Refresh" @click="() => load()">刷新</el-button>
<span> {{ total }} </span>
</div>
<el-table v-loading="loading" :data="rows" class="data-table">
@@ -235,6 +246,15 @@ onMounted(async () => {
</el-table-column>
<template #empty><el-empty :description="isTeacher ? '尚未申报授课科目' : '没有待处理的授课申报'" /></template>
</el-table>
<el-pagination
v-if="total > pageSize"
v-model:current-page="page"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next"
class="table-pagination"
@current-change="() => load()"
/>
</section>
<el-dialog v-model="submitDialog" title="申报授课科目" width="600px">