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

@@ -50,6 +50,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
import { useAdminSiteCodeOptions } from "@/hooks/use-admin-site-code-options";
import { formatAdminMinorUnits } from "@/lib/money";
import { LotteryApiBizError } from "@/types/api/errors";
import type { AdminPlayerRow, AdminPlayerWalletRow } from "@/types/api/admin-player";
@@ -87,8 +88,11 @@ export function PlayersConsole(): React.ReactElement {
const canFreezePlayers = adminHasAnyPermission(profile?.permissions, [PRD_PLAYER_FREEZE_MANAGE]);
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const { sites: siteOptions, canChooseSite } = useAdminSiteCodeOptions();
const [keyword, setKeyword] = useState("");
const [query, setQuery] = useState("");
const [siteCode, setSiteCode] = useState("");
const [appliedSiteCode, setAppliedSiteCode] = useState("");
const [items, setItems] = useState<AdminPlayerRow[]>([]);
const [total, setTotal] = useState(0);
@@ -124,6 +128,7 @@ export function PlayersConsole(): React.ReactElement {
page,
per_page: perPage,
keyword: query.trim() || undefined,
site_code: appliedSiteCode.trim() || undefined,
});
setItems(data.items);
setTotal(data.meta.total);
@@ -137,7 +142,7 @@ export function PlayersConsole(): React.ReactElement {
} finally {
setLoading(false);
}
}, [page, perPage, query, t]);
}, [page, perPage, query, appliedSiteCode, t]);
useEffect(() => {
queueMicrotask(() => {
@@ -297,6 +302,24 @@ export function PlayersConsole(): React.ReactElement {
) : null}
</div>
<div className="admin-list-toolbar">
{canChooseSite ? (
<div className="admin-list-field">
<Label className="sm:w-20 sm:shrink-0">{t("filterSite")}</Label>
<Select value={siteCode || "__all__"} onValueChange={(v) => setSiteCode(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 xl:min-w-0">
<Label htmlFor="player-search" className="sm:w-20 sm:shrink-0">
{t("search")}
@@ -311,6 +334,7 @@ export function PlayersConsole(): React.ReactElement {
if (e.key === "Enter") {
setPage(1);
setQuery(keyword.trim());
setAppliedSiteCode(siteCode.trim());
}
}}
/>
@@ -326,6 +350,7 @@ export function PlayersConsole(): React.ReactElement {
onClick={() => {
setPage(1);
setQuery(keyword.trim());
setAppliedSiteCode(siteCode.trim());
}}
>
{t("search")}