feat: enhance ticket order detail and status display

- Updated ticket status display logic to handle "failed" status and improve "settled_win" condition.
- Refactored TicketOrderDetailScreen to utilize search parameters for dynamic navigation and grouping of tickets.
- Enhanced TicketOrdersListScreen to group ticket items and improve rendering of order details.
- Added new translations for order-related terms in multiple languages to support enhanced user experience.
This commit is contained in:
2026-05-25 16:02:23 +08:00
parent ca3a1db770
commit 3bcbf7d256
10 changed files with 446 additions and 30 deletions

View File

@@ -0,0 +1,131 @@
import type { TicketItemListRow } from "@/types/api/ticket-items";
export type TicketItemGroup = {
key: string;
order_no: string | null;
draw_no: string | null;
placed_at: string | null;
currency_code: string | null;
status: string;
items: TicketItemListRow[];
total_bet_amount: number;
actual_deduct_amount: number;
win_amount: number;
jackpot_win_amount: number;
};
export const ORDER_GROUP_STORAGE_PREFIX = "lottery:order-group:v1:";
/** 分组键:有 order_no 则按订单;否则 draw_no + placed_at + currency + status 兜底 */
export function getTicketItemGroupKey(row: TicketItemListRow): string {
const orderNo = (row.order_no ?? "").trim();
if (orderNo) {
return `order:${orderNo}`;
}
const drawNo = row.draw_no ?? "";
const placedAt = row.placed_at ?? "";
const currency = row.currency_code ?? "";
const status = row.status ?? "";
return `fallback:${drawNo}|${placedAt}|${currency}|${status}`;
}
export function sortTicketGroupItems(items: TicketItemListRow[]): TicketItemListRow[] {
return [...items].sort((a, b) => {
const byPlay = a.play_code.localeCompare(b.play_code);
if (byPlay !== 0) return byPlay;
return (a.original_number ?? "").localeCompare(b.original_number ?? "");
});
}
/** 保持 API 返回顺序下,按分组键首次出现顺序输出合并组 */
export function groupTicketItems(items: TicketItemListRow[]): TicketItemGroup[] {
const map = new Map<string, TicketItemGroup>();
const order: string[] = [];
for (const row of items) {
const key = getTicketItemGroupKey(row);
let group = map.get(key);
if (!group) {
const orderNo = (row.order_no ?? "").trim();
group = {
key,
order_no: orderNo || null,
draw_no: row.draw_no ?? null,
placed_at: row.placed_at ?? null,
currency_code: row.currency_code ?? null,
status: row.status,
items: [],
total_bet_amount: 0,
actual_deduct_amount: 0,
win_amount: 0,
jackpot_win_amount: 0,
};
map.set(key, group);
order.push(key);
}
group.items.push(row);
group.total_bet_amount += row.total_bet_amount;
group.actual_deduct_amount += row.actual_deduct_amount;
group.win_amount += row.win_amount;
group.jackpot_win_amount += row.jackpot_win_amount;
}
return order.map((key) => {
const group = map.get(key)!;
return { ...group, items: sortTicketGroupItems(group.items) };
});
}
export function persistOrderGroup(group: TicketItemGroup): void {
try {
sessionStorage.setItem(
ORDER_GROUP_STORAGE_PREFIX + group.key,
JSON.stringify(group),
);
} catch {
/* quota / private mode */
}
}
export function loadPersistedOrderGroup(groupKey: string): TicketItemGroup | null {
try {
const raw = sessionStorage.getItem(ORDER_GROUP_STORAGE_PREFIX + groupKey);
if (!raw) return null;
const parsed = JSON.parse(raw) as TicketItemGroup;
if (!parsed?.key || !Array.isArray(parsed.items)) return null;
return { ...parsed, items: sortTicketGroupItems(parsed.items) };
} catch {
return null;
}
}
export function orderGroupPath(groupKey: string, ticketNos?: string[]): string {
const base = `/orders/group/${encodeURIComponent(groupKey)}`;
if (!ticketNos?.length) return base;
return `${base}?tickets=${encodeURIComponent(ticketNos.join(","))}`;
}
export function orderGroupHref(group: TicketItemGroup): string {
if (group.items.length === 1) {
return `/orders/${encodeURIComponent(group.items[0].ticket_no)}`;
}
return orderGroupPath(
group.key,
group.items.map((i) => i.ticket_no),
);
}
/** 注项详情;来自订单组时带上 fromGroup便于返回订单详情 */
export function ticketDetailHref(
ticketNo: string,
fromGroup?: TicketItemGroup | null,
): string {
const base = `/orders/${encodeURIComponent(ticketNo)}`;
const key = (fromGroup?.key ?? "").trim();
if (!key) return base;
const params = new URLSearchParams({ fromGroup: key });
if (fromGroup && fromGroup.items.length > 1) {
params.set("tickets", fromGroup.items.map((i) => i.ticket_no).join(","));
}
return `${base}?${params.toString()}`;
}