feat(business): update audit, auth, course-plans, announcements, exams

- audit: update retention.ts, types.ts, audit-retention-settings.tsx

- auth: update actions.ts and types.ts

- course-plans: update actions.ts, course-plan-detail.tsx, template-picker-dialog.tsx,

  add lib/track-event.ts

- announcements: update page.tsx, announcement-list.tsx, announcement-pagination.tsx

- exams: update actions.ts
This commit is contained in:
SpecialX
2026-07-04 10:23:03 +08:00
parent b63d116b6c
commit 98429e87eb
13 changed files with 114 additions and 61 deletions

View File

@@ -34,8 +34,9 @@ type Filter = "all" | AnnouncementStatus
* P2-4: 删除了死 prop `detailHrefBuilder`,仅保留 `detailHrefPrefix`Server Component 安全)。
* P1-2: 用户端canManage=false时调用 `getReadStatus` 批量获取已读状态,
* 传入卡片做已读/未读视觉区分。
* P2-6: 新增分页支持。传入 `pagination` prop 时在列表底部渲染 `AnnouncementPagination`
* 通过 `buildPageHref` 回调生成页码 URL与 `?status=` 过滤查询参数合并)。
* P2-6: 新增分页支持。传入 `pagination` prop 时在列表底部渲染 `AnnouncementPagination`
* v3: pagination 不再接收 `buildPageHref` 函数Server Component 不能传函数),
* 改为传 `basePath` + `statusFilter`,由 AnnouncementPagination 自行构建 URL。
*/
export function AnnouncementList({
announcements,
@@ -54,7 +55,8 @@ export function AnnouncementList({
page: number
pageSize: number
total: number
buildPageHref: (page: number) => string
basePath: string
statusFilter?: string
}
}) {
const t = useTranslations("announcements")
@@ -152,7 +154,8 @@ export function AnnouncementList({
page={pagination.page}
pageSize={pagination.pageSize}
total={pagination.total}
buildHref={pagination.buildPageHref}
basePath={pagination.basePath}
statusFilter={pagination.statusFilter}
/>
) : null}
</div>

View File

@@ -19,18 +19,23 @@ import { cn } from "@/shared/lib/utils"
* - a11y上一页/下一页按钮使用 aria-label当前页用 aria-current="page"
* - 边界处理total=0 时不渲染分页;首页时上一页禁用;末页时下一页禁用
* - 计算窗口:当总页数 > 7 时显示首末页 + 当前页 ±1 + 省略号,避免溢出
*
* v3: 不再接收 `buildHref` 函数 propServer Component 不能传函数给 Client Component
* 改为接收 `basePath` + `statusFilter`,在客户端自行构建分页 URL。
*/
export function AnnouncementPagination({
page,
pageSize,
total,
buildHref,
basePath,
statusFilter,
className,
}: {
page: number
pageSize: number
total: number
buildHref: (page: number) => string
basePath: string
statusFilter?: string
className?: string
}) {
const t = useTranslations("announcements")
@@ -38,6 +43,15 @@ export function AnnouncementPagination({
const totalPages = Math.max(1, Math.ceil(total / pageSize))
const currentPage = Math.min(Math.max(1, page), totalPages)
// 客户端构建分页 URL保留 ?status= 过滤参数
const buildHref = (targetPage: number): string => {
const params = new URLSearchParams()
if (statusFilter && statusFilter !== "all") params.set("status", statusFilter)
if (targetPage > 1) params.set("page", String(targetPage))
const qs = params.toString()
return qs ? `${basePath}?${qs}` : basePath
}
const pages = useMemo(() => {
if (totalPages <= 7) {
return Array.from({ length: totalPages }, (_, i) => i + 1)