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:
131
src/features/orders/group-ticket-items.ts
Normal file
131
src/features/orders/group-ticket-items.ts
Normal 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()}`;
|
||||
}
|
||||
Reference in New Issue
Block a user