官方凭证档案优化

This commit is contained in:
2026-08-11 11:58:29 +08:00 Unverified
parent 4005b78d2e
commit be21861a7c
2 changed files with 41 additions and 10 deletions
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
@@ -32,15 +33,24 @@ public sealed class OfficialDocumentsController(
OfficialDocumentType? type,
OfficialDocumentStatus? status,
Guid? studentId,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
int page = 1,
int pageSize = 20)
{
if (page < 1 || pageSize is < 1 or > 100)
return ValidationProblem("页码必须大于 0,且每页条数应在 1 至 100 之间。");
var source = AccessibleDocuments().AsNoTracking();
if (type.HasValue) source = source.Where(x => x.Type == type);
if (status.HasValue) source = source.Where(x => x.Status == status);
if (studentId.HasValue) source = source.Where(x => x.StudentId == studentId);
return Ok(await source
var total = await source.CountAsync(cancellationToken);
var items = await source
.OrderByDescending(x => x.IssuedAt)
.ThenByDescending(x => x.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new
{
x.Id,
@@ -62,7 +72,8 @@ public sealed class OfficialDocumentsController(
DownloadCount = x.Downloads.Count,
LastDownloadedAt = x.Downloads.Max(download => (DateTime?)download.CreatedAt)
})
.ToListAsync(cancellationToken));
.ToListAsync(cancellationToken);
return Ok(new PagedResult<object>(items, total, page, pageSize));
}
[HttpGet("students/options")]
+27 -7
View File
@@ -51,6 +51,9 @@ const historyLoading = ref(false)
const downloadHistory = ref<any[]>([])
const selectedDocument = ref<OfficialDocumentRow>()
const filters = reactive<{ type?: DocumentType; status?: DocumentStatus }>({})
const page = ref(1)
const total = ref(0)
const pageSize = 20
const issueForm = reactive<{ studentId: string; type: DocumentType; purpose: string }>({
studentId: '',
type: 'Transcript',
@@ -67,11 +70,19 @@ const statusLabels: Record<DocumentStatus, string> = {
Superseded: '已重签',
}
async function load() {
async function load(resetPage = false) {
if (resetPage) page.value = 1
loading.value = true
try {
const { data } = await http.get('/official-documents', { params: filters })
documents.value = data
const { data } = await http.get('/official-documents', {
params: { ...filters, page: page.value, pageSize },
})
documents.value = data.items
total.value = data.total
if (documents.value.length === 0 && page.value > 1) {
page.value--
await load()
}
} catch (error) {
ElMessage.error(apiErrorMessage(error))
} finally {
@@ -224,14 +235,14 @@ onMounted(load)
</section>
<section class="document-toolbar">
<el-select v-model="filters.type" clearable placeholder="全部凭证类型" @change="load">
<el-select v-model="filters.type" clearable placeholder="全部凭证类型" @change="load(true)">
<el-option v-for="(label, value) in typeLabels" :key="value" :label="label" :value="value" />
</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> {{ documents.length }} 份凭证</span>
<el-button :icon="Refresh" @click="() => load()">刷新</el-button>
<span> {{ total }} 份凭证</span>
</section>
<section class="document-list" v-loading="loading">
@@ -274,6 +285,15 @@ onMounted(load)
v-if="!documents.length"
:description="isManager ? '暂无符合条件的官方凭证' : '暂无电子凭证,可点击上方按钮在线申请'"
/>
<el-pagination
v-if="total > pageSize"
v-model:current-page="page"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next"
class="document-pagination"
@current-change="() => load()"
/>
</section>
<el-dialog v-model="issueDialog" :title="isManager ? '签发官方凭证' : '申请电子凭证'" width="620px">