refactor: update agent API schemas, standardize UI text styling, and enhance settlement credit ledger components

This commit is contained in:
2026-06-11 18:02:02 +08:00
parent 44ad51698f
commit 1eb6702c51
54 changed files with 1888 additions and 1103 deletions

View File

@@ -113,6 +113,10 @@ function MaskedValueWithCopy({
type FormState = {
code: string;
name: string;
admin_username: string;
admin_nickname: string;
admin_password: string;
admin_email: string;
currency_code: string;
status: number;
wallet_api_url: string;
@@ -128,6 +132,10 @@ type FormState = {
const EMPTY_FORM: FormState = {
code: "",
name: "",
admin_username: "",
admin_nickname: "",
admin_password: "",
admin_email: "",
currency_code: "NPR",
status: 1,
wallet_api_url: "",
@@ -155,6 +163,10 @@ function rowToForm(row: AdminIntegrationSiteDetail): FormState {
return {
code: row.code,
name: row.name,
admin_username: "",
admin_nickname: "",
admin_password: "",
admin_email: "",
currency_code: row.currency_code,
status: row.status,
wallet_api_url: row.wallet_api_url ?? "",
@@ -189,7 +201,16 @@ function formToPayload(
};
if (includeCode) {
return { code: form.code.trim(), ...base };
return {
code: form.code.trim(),
admin_account: {
username: form.admin_username.trim(),
nickname: form.admin_nickname.trim(),
password: form.admin_password,
email: form.admin_email.trim() || null,
},
...base,
};
}
return base;
@@ -303,11 +324,34 @@ export function IntegrationSitesConsole({
return;
}
if (mode === "create" && form.admin_username.trim() === "") {
toast.error(t("integrationSites.form.adminUsernameRequired"));
return;
}
if (mode === "create" && form.admin_nickname.trim() === "") {
toast.error(t("integrationSites.form.adminNicknameRequired"));
return;
}
if (mode === "create" && form.admin_password.trim().length < 8) {
toast.error(t("integrationSites.form.adminPasswordRequired"));
return;
}
setSaving(true);
try {
if (mode === "create") {
const created = await postAdminIntegrationSite(formToPayload(form, true));
toast.success(t("integrationSites.createSuccess", { code: created.code }));
if (created.admin_user?.username) {
toast.success(
t("integrationSites.adminAccountCreated", {
username: created.admin_user.username,
defaultValue: "已同时创建站点后台账号 {{username}}",
}),
);
}
showSecretsOnce(created);
} else if (editingId !== null) {
await putAdminIntegrationSite(editingId, formToPayload(form, false));
@@ -487,7 +531,6 @@ export function IntegrationSitesConsole({
<TableHead>{t("integrationSites.columns.status")}</TableHead>
<TableHead>{t("integrationSites.columns.lineRoot")}</TableHead>
<TableHead>{t("integrationSites.columns.walletUrl")}</TableHead>
<TableHead>{t("integrationSites.columns.h5Url")}</TableHead>
<TableHead>{t("integrationSites.columns.ssoSecret")}</TableHead>
<TableHead>{t("integrationSites.columns.walletApiKey")}</TableHead>
<TableHead className="sticky right-0 z-20 bg-muted w-14 text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">{t("integrationSites.columns.actions")}</TableHead>
@@ -541,9 +584,6 @@ export function IntegrationSitesConsole({
) : null}
</div>
</TableCell>
<TableCell className="max-w-[12rem] truncate text-xs text-muted-foreground">
{row.lottery_h5_base_url ?? "—"}
</TableCell>
<TableCell>
<MaskedValueWithCopy
configured={row.has_sso_secret}
@@ -641,6 +681,83 @@ export function IntegrationSitesConsole({
onChange={(e) => updateForm("name", e.target.value)}
/>
</div>
{mode === "create" ? (
<>
<div className="rounded-lg border border-border/60 bg-muted/20 p-4">
<div className="mb-3">
<p className="text-sm font-medium text-foreground">
{t("integrationSites.adminAccountSectionTitle", {
defaultValue: "站点后台管理账号",
})}
</p>
<p className="mt-1 text-xs text-muted-foreground">
{t("integrationSites.adminAccountSectionDescription", {
defaultValue: "创建站点时将同步创建一个绑定该站点的后台管理账号。",
})}
</p>
</div>
<div className="grid gap-4">
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="is-admin-username">
{t("integrationSites.fields.adminUsername", { defaultValue: "后台登录名" })}
</Label>
<Input
id="is-admin-username"
value={form.admin_username}
placeholder={t("integrationSites.placeholders.adminUsername", {
defaultValue: "请输入后台登录名",
})}
onChange={(e) => updateForm("admin_username", e.target.value)}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="is-admin-nickname">
{t("integrationSites.fields.adminNickname", { defaultValue: "账号昵称" })}
</Label>
<Input
id="is-admin-nickname"
value={form.admin_nickname}
placeholder={t("integrationSites.placeholders.adminNickname", {
defaultValue: "请输入账号昵称",
})}
onChange={(e) => updateForm("admin_nickname", e.target.value)}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="is-admin-password">
{t("integrationSites.fields.adminPassword", { defaultValue: "初始密码" })}
</Label>
<Input
id="is-admin-password"
type="password"
value={form.admin_password}
placeholder={t("integrationSites.placeholders.adminPassword", {
defaultValue: "至少 8 位",
})}
onChange={(e) => updateForm("admin_password", e.target.value)}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="is-admin-email">
{t("integrationSites.fields.adminEmail", { defaultValue: "邮箱(可选)" })}
</Label>
<Input
id="is-admin-email"
value={form.admin_email}
placeholder={t("integrationSites.placeholders.adminEmail", {
defaultValue: "请输入邮箱",
})}
onChange={(e) => updateForm("admin_email", e.target.value)}
/>
</div>
</div>
</div>
</div>
</>
) : null}
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="is-currency">{t("integrationSites.fields.currency")}</Label>
@@ -673,15 +790,6 @@ export function IntegrationSitesConsole({
onChange={(e) => updateForm("wallet_api_url", e.target.value)}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="is-h5">{t("integrationSites.fields.lotteryH5BaseUrl")}</Label>
<Input
id="is-h5"
value={form.lottery_h5_base_url}
placeholder={t("integrationSites.placeholders.lotteryH5BaseUrl")}
onChange={(e) => updateForm("lottery_h5_base_url", e.target.value)}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="is-origins">{t("integrationSites.fields.iframeOrigins")}</Label>
<Textarea