feat(integration): 新增 site_code 支持并增强玩家与注单管理界面

在后台玩家与注单相关 API 中新增 site_code 参数,支持按站点筛选数据。
更新 PlayersConsole 与 PlayerTicketsConsole UI 组件,新增站点选择筛选功能。
增强国际化支持,在英文与中文语言包中新增站点相关文案。
优化配置中心页面,新增跳转至集成站点管理的入口,提升后台导航体验。
This commit is contained in:
2026-05-27 13:36:44 +08:00
parent e87229c1b7
commit 5eabbcf0ee
17 changed files with 1126 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import { useExportLabels } from "@/hooks/use-export-labels";
import { useTranslation } from "react-i18next";
import { getAdminTicketItems } from "@/api/admin-tickets";
import { useAdminSiteCodeOptions } from "@/hooks/use-admin-site-code-options";
import { AdminDateRangeField } from "@/components/admin/admin-date-range-field";
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
@@ -19,6 +20,13 @@ import {
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Table,
TableBody,
@@ -48,6 +56,7 @@ const TICKET_STATUS_OPTIONS = [
] as const;
type TicketFilters = {
siteCode: string;
playerQuery: string;
drawNo: string;
numberKeyword: string;
@@ -57,6 +66,7 @@ type TicketFilters = {
};
const emptyTicketFilters: TicketFilters = {
siteCode: "",
playerQuery: "",
drawNo: "",
numberKeyword: "",
@@ -90,6 +100,7 @@ function ticketStatusSummary(statuses: string[], t: TicketTranslateFn): string {
export function PlayerTicketsConsole(): React.ReactElement {
const { t } = useTranslation(["tickets", "common"]);
const { sites: siteOptions, canChooseSite } = useAdminSiteCodeOptions();
const playCodeLabel = useAdminPlayCodeLabel();
const exportLabels = useExportLabels("tickets");
const formatTs = useAdminDateTimeFormatter();
@@ -118,6 +129,7 @@ export function PlayerTicketsConsole(): React.ReactElement {
page,
per_page: perPage,
...query,
site_code: applied.siteCode.trim() || undefined,
draw_no: applied.drawNo.trim() || undefined,
status: applied.statuses.length > 0 ? applied.statuses : undefined,
number: applied.numberKeyword.trim() || undefined,
@@ -143,6 +155,7 @@ export function PlayerTicketsConsole(): React.ReactElement {
setErr(null);
setApplied({
...draft,
siteCode: draft.siteCode.trim(),
playerQuery: draft.playerQuery.trim(),
drawNo: draft.drawNo.trim(),
numberKeyword: draft.numberKeyword.trim(),
@@ -173,6 +186,32 @@ export function PlayerTicketsConsole(): React.ReactElement {
</CardHeader>
<CardContent className="admin-list-content">
<div className="admin-list-toolbar">
{canChooseSite ? (
<div className="admin-list-field">
<Label className="sm:shrink-0">{t("filterSite")}</Label>
<Select
value={draft.siteCode || "__all__"}
onValueChange={(v) =>
setDraft((current) => ({
...current,
siteCode: v === "__all__" ? "" : v,
}))
}
>
<SelectTrigger className="w-full sm:w-[12rem]">
<SelectValue placeholder={t("filterAllSites")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="__all__">{t("filterAllSites")}</SelectItem>
{siteOptions.map((site) => (
<SelectItem key={site.code} value={site.code}>
{site.code} {site.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : null}
<div className="admin-list-field min-w-[12rem] flex-1 sm:max-w-md">
<Label htmlFor="pt-player" className="sm:shrink-0">
{t("playerId")}