/** 店铺数据任务列表分页与选择派生(任务 111/112):分页信息 + 可批量选择的结果行,纯逻辑。 */ import { SHOP_DATA_DEFAULT_PAGE_SIZE, SHOP_DATA_MAX_PAGE_SIZE, SHOP_DATA_PAGE_MIN } from './shop-data-filter.ts' import type { ShopDataTaskGroup } from './shop-data-model.ts' export interface ShopDataPageInfo { page: number pageSize: number total: number totalPages: number from: number to: number hasPrev: boolean hasNext: boolean } function finiteInt(value: unknown): number | null { return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : null } /** 由总数与目标分页派生页码信息;空数据视为第 1 页。 */ export function shopDataPageInfo(page: unknown, pageSize: unknown, total: unknown): ShopDataPageInfo { const rawSize = finiteInt(pageSize) const size = rawSize === null || rawSize < 1 ? SHOP_DATA_DEFAULT_PAGE_SIZE : Math.min(Math.max(rawSize, 10), SHOP_DATA_MAX_PAGE_SIZE) const safeTotal = Math.max(finiteInt(total) ?? 0, 0) const totalPages = safeTotal === 0 ? 1 : Math.max(1, Math.ceil(safeTotal / size)) const requested = Math.max(finiteInt(page) ?? SHOP_DATA_PAGE_MIN, SHOP_DATA_PAGE_MIN) const safePage = Math.min(requested, totalPages) const from = safeTotal === 0 ? 0 : (safePage - 1) * size + 1 const to = Math.min(safeTotal, safePage * size) return { page: safePage, pageSize: size, total: safeTotal, totalPages, from, to, hasPrev: safePage > 1, hasNext: safePage < totalPages && safeTotal > 0, } } /** 展平本页所有可选中的结果行(result_id),供批量下载/勾选;缺行忽略。 */ export function selectableResultRows(groups: readonly ShopDataTaskGroup[]): string[] { const ids: string[] = [] for (const group of groups) { for (const row of group.results) { if (row.resultId) ids.push(row.resultId) } } return ids }