diff --git a/apps/admin/src/views/Contents.vue b/apps/admin/src/views/Contents.vue index 2b8d266..b64dd17 100644 --- a/apps/admin/src/views/Contents.vue +++ b/apps/admin/src/views/Contents.vue @@ -130,6 +130,9 @@ const filterStatus = ref(''); const loading = ref(false); const saving = ref(false); const items = ref([]); +const total = ref(0); +const page = ref(1); +const pageSize = ref(10); const tableRef = ref(); const selectedRows = ref([]); @@ -206,9 +209,12 @@ async function load() { params: { type: activeType.value, status: filterStatus.value || undefined, + page: page.value, + pageSize: pageSize.value, }, }); - items.value = data.data ?? []; + items.value = data.data.items ?? []; + total.value = data.data.total ?? 0; selectedRows.value = []; tableRef.value?.clearSelection(); } catch (e: unknown) { @@ -219,7 +225,19 @@ async function load() { } } +function onPageChange(p: number) { + page.value = p; + load(); +} + +function onSizeChange(size: number) { + pageSize.value = size; + page.value = 1; + load(); +} + watch([activeType, filterStatus], () => { + page.value = 1; selectedRows.value = []; tableRef.value?.clearSelection(); void load(); @@ -290,7 +308,7 @@ function batchDelete() { function resetForm() { form.value = { - sortOrder: items.value.length + 1, + sortOrder: total.value + 1, status: 'DRAFT', linkType: '', linkTarget: '', @@ -562,6 +580,18 @@ void load(); +
+ +
diff --git a/apps/api/src/applications/admin/admin.controller.ts b/apps/api/src/applications/admin/admin.controller.ts index d04ed9e..20622fe 100644 --- a/apps/api/src/applications/admin/admin.controller.ts +++ b/apps/api/src/applications/admin/admin.controller.ts @@ -2249,9 +2249,13 @@ export class AdminController { async listContents( @Query('type') type?: string, @Query('status') status?: string, + @Query('page') page?: string, + @Query('pageSize') pageSize?: string, ) { - const items = await this.content.listForAdmin(type, status); - return jsonResponse(items); + const p = Math.max(1, page ? parseInt(page, 10) || 1 : 1); + const size = Math.min(Math.max(1, pageSize ? parseInt(pageSize, 10) : 20), 100); + const result = await this.content.listForAdmin(type, status, p, size); + return jsonResponse(result); } @Get('contents/:id') diff --git a/apps/api/src/domains/operations/content/content.service.ts b/apps/api/src/domains/operations/content/content.service.ts index db59554..eb15a9c 100644 --- a/apps/api/src/domains/operations/content/content.service.ts +++ b/apps/api/src/domains/operations/content/content.service.ts @@ -255,7 +255,12 @@ export class ContentService { }); } - async listForAdmin(contentType?: string, status?: string) { + async listForAdmin( + contentType?: string, + status?: string, + page = 1, + pageSize = 20, + ) { const typeWhere = contentType === ANNOUNCEMENT_ADMIN_TYPE ? { contentType: { in: [...ANNOUNCEMENT_TYPES] } } @@ -263,15 +268,28 @@ export class ContentService { ? { contentType } : {}; - const items = await this.prisma.content.findMany({ - where: { - ...typeWhere, - ...(status ? { status } : {}), - }, - include: { translations: true }, - orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }], - }); - return items.map((item) => this.mapAdminItem(item)); + const where = { + ...typeWhere, + ...(status ? { status } : {}), + }; + + const [items, total] = await Promise.all([ + this.prisma.content.findMany({ + where, + include: { translations: true }, + orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }], + skip: (page - 1) * pageSize, + take: pageSize, + }), + this.prisma.content.count({ where }), + ]); + + return { + items: items.map((item) => this.mapAdminItem(item)), + total, + page, + pageSize, + }; } async getForAdmin(id: bigint) { @@ -442,6 +460,7 @@ export class ContentService { /** @deprecated use listForAdmin */ async listAll(contentType?: string) { - return this.listForAdmin(contentType); + const result = await this.listForAdmin(contentType); + return result.items; } }