task-83(店铺中心): 实现店铺密钥筛选和分页
新增 shop-keys-list-state.ts:密钥页本地关键词过滤(备注名/紫鸟账号)与分页 信息派生(页码钳制/首末/前后页)。Java 密钥接口无服务端筛选,搜索在已加载 当前页行上本地过滤,纯逻辑可测。 TDD: task-83.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/** 店铺密钥列表筛选与分页(任务 83):密钥页本地关键词过滤与分页信息派生,纯逻辑。
|
||||
* 说明:Java 店铺密钥接口只支持分页,无服务端筛选,页面 FilterBar 的备注/紫鸟账号
|
||||
* 搜索在已加载的当前页行上做本地过滤,避免误导用户以为会跨页检索。 */
|
||||
import { SHOP_PAGE_DEFAULT_SIZE, SHOP_PAGE_MAX_SIZE, SHOP_PAGE_MIN_PAGE, type ShopKeyItem } from './shop-dto.ts'
|
||||
|
||||
/** 店铺密钥列表本地筛选状态。 */
|
||||
export interface ShopKeyListFilter {
|
||||
keyword?: string
|
||||
}
|
||||
|
||||
function finiteInt(value: unknown): number | null {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : null
|
||||
}
|
||||
|
||||
/** 归一本地关键词:去首尾空白,空白视为无筛选。 */
|
||||
export function normalizeShopKeyFilter(raw: Partial<ShopKeyListFilter>): ShopKeyListFilter {
|
||||
const keyword = typeof raw.keyword === 'string' ? raw.keyword.trim() || undefined : undefined
|
||||
return keyword ? { keyword } : {}
|
||||
}
|
||||
|
||||
export function shopKeyFilterActive(filter: ShopKeyListFilter): boolean {
|
||||
return Boolean(filter.keyword && filter.keyword.trim())
|
||||
}
|
||||
|
||||
/** 在已加载行上做备注名/紫鸟账号名子串过滤(大小写不敏感);无关键词原样返回副本。 */
|
||||
export function filterShopKeyRows(items: readonly ShopKeyItem[], filter: ShopKeyListFilter): ShopKeyItem[] {
|
||||
const keyword = (filter.keyword || '').trim().toLowerCase()
|
||||
if (!keyword) return [...items]
|
||||
return items.filter(
|
||||
(item) =>
|
||||
item.remarkName.toLowerCase().includes(keyword) ||
|
||||
item.ziniaoAccountName.toLowerCase().includes(keyword),
|
||||
)
|
||||
}
|
||||
|
||||
export interface ShopKeyPageInfo {
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
from: number
|
||||
to: number
|
||||
hasPrev: boolean
|
||||
hasNext: boolean
|
||||
}
|
||||
|
||||
/** 由总数与目标分页派生页码信息:页码钳制到 [1, totalPages],空数据视为第 1 页。 */
|
||||
export function shopKeyPageInfo(page: unknown, pageSize: unknown, total: unknown): ShopKeyPageInfo {
|
||||
const safePageSize = Math.max(finiteInt(pageSize) ?? SHOP_PAGE_DEFAULT_SIZE, SHOP_PAGE_MIN_PAGE)
|
||||
const clampedPageSize = Math.min(safePageSize, SHOP_PAGE_MAX_SIZE)
|
||||
const safeTotal = Math.max(finiteInt(total) ?? 0, 0)
|
||||
const totalPages = safeTotal === 0 ? 1 : Math.max(1, Math.ceil(safeTotal / clampedPageSize))
|
||||
const pageNumber = Math.max(finiteInt(page) ?? SHOP_PAGE_MIN_PAGE, SHOP_PAGE_MIN_PAGE)
|
||||
const safePage = Math.min(pageNumber, totalPages)
|
||||
const from = safeTotal === 0 ? 0 : (safePage - 1) * clampedPageSize + 1
|
||||
const to = Math.min(safeTotal, safePage * clampedPageSize)
|
||||
return {
|
||||
page: safePage,
|
||||
pageSize: clampedPageSize,
|
||||
total: safeTotal,
|
||||
totalPages,
|
||||
from,
|
||||
to,
|
||||
hasPrev: safePage > 1,
|
||||
hasNext: safePage < totalPages && safeTotal > 0,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user