feat: 增加管理端多语言与多模块界面国际化支持
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import {
|
||||
@@ -40,6 +41,7 @@ import { useAdminProfile } from "@/stores/admin-session";
|
||||
import type { AdminPermissionCatalogData, AdminUserPermissionRow } from "@/types/api/index";
|
||||
|
||||
export function AdminUsersConsole(): React.ReactElement {
|
||||
const { t } = useTranslation(["adminUsers", "common"]);
|
||||
const profile = useAdminProfile();
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(25);
|
||||
@@ -59,7 +61,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [savingRoles, setSavingRoles] = useState(false);
|
||||
const [permissionOpen, setPermissionOpen] = useState(false);
|
||||
/** `false` = 折叠;缺省为展开 */
|
||||
/** `false` = collapsed; default expanded */
|
||||
const [directMenuExpanded, setDirectMenuExpanded] = useState<Record<string, boolean>>({});
|
||||
|
||||
const [accountOpen, setAccountOpen] = useState(false);
|
||||
@@ -129,16 +131,16 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
async function submitAccount(): Promise<void> {
|
||||
const nick = formNickname.trim();
|
||||
if (nick === "") {
|
||||
toast.error("请填写昵称");
|
||||
toast.error(t("nicknameRequired"));
|
||||
return;
|
||||
}
|
||||
if (accountMode === "edit" && formPassword !== "" && formPassword.length < 8) {
|
||||
toast.error("新密码至少 8 位");
|
||||
toast.error(t("newPasswordMin"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountMode === "create" && formCreateRoles.length === 0) {
|
||||
toast.error("请至少选择一个角色");
|
||||
toast.error(t("roleRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,11 +149,11 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
if (accountMode === "create") {
|
||||
const u = formUsername.trim();
|
||||
if (u === "") {
|
||||
toast.error("请填写登录账号");
|
||||
toast.error(t("usernameRequired"));
|
||||
return;
|
||||
}
|
||||
if (formPassword.length < 8) {
|
||||
toast.error("密码至少 8 位");
|
||||
toast.error(t("passwordMin"));
|
||||
return;
|
||||
}
|
||||
const created = await postAdminUser({
|
||||
@@ -164,7 +166,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
});
|
||||
setItems((prev) => [created, ...prev]);
|
||||
setTotal((t) => t + 1);
|
||||
toast.success(`已创建管理员 ${created.username}`);
|
||||
toast.success(t("createSuccess", { name: created.username }));
|
||||
handleAccountDialogOpenChange(false);
|
||||
} else {
|
||||
const id = editingAccountId;
|
||||
@@ -186,11 +188,11 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
}
|
||||
const updated = await putAdminUser(id, body);
|
||||
setItems((prev) => prev.map((row) => (row.id === updated.id ? updated : row)));
|
||||
toast.success(`已更新 ${updated.username}`);
|
||||
toast.success(t("updateSuccess", { name: updated.username }));
|
||||
handleAccountDialogOpenChange(false);
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : "保存账号失败";
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : t("saveAccountFailed");
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setAccountSaving(false);
|
||||
@@ -206,10 +208,10 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
await deleteAdminUser(deleteTarget.id);
|
||||
setItems((prev) => prev.filter((r) => r.id !== deleteTarget.id));
|
||||
setTotal((t) => Math.max(0, t - 1));
|
||||
toast.success(`已删除 ${deleteTarget.username}`);
|
||||
toast.success(t("deleteSuccess", { name: deleteTarget.username }));
|
||||
setDeleteTarget(null);
|
||||
} catch (e) {
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : "删除失败";
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : t("deleteFailed");
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setDeleteBusy(false);
|
||||
@@ -229,10 +231,10 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
}
|
||||
const flat = catalog?.permissions ?? [];
|
||||
if (flat.length > 0) {
|
||||
return [{ key: "all", label: "全部权限", permissions: flat }];
|
||||
return [{ key: "all", label: t("allPermissions"), permissions: flat }];
|
||||
}
|
||||
return [];
|
||||
}, [catalog]);
|
||||
}, [catalog, t]);
|
||||
|
||||
function isDirectGroupOpen(key: string): boolean {
|
||||
return directMenuExpanded[key] !== false;
|
||||
@@ -271,7 +273,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
setTotal(listData.meta.total);
|
||||
setLastPage(Math.max(1, listData.meta.last_page));
|
||||
} catch (e) {
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : "加载管理员列表失败";
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : t("loadFailed");
|
||||
setErr(msg);
|
||||
setItems([]);
|
||||
setTotal(0);
|
||||
@@ -279,7 +281,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [page, perPage, query]);
|
||||
}, [page, perPage, query, t]);
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
@@ -324,9 +326,9 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
: row,
|
||||
),
|
||||
);
|
||||
toast.success(`已更新 ${result.username} 的角色`);
|
||||
toast.success(t("saveRoleSuccess", { name: result.username }));
|
||||
} catch (e) {
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : "保存角色失败";
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : t("saveRoleFailed");
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setSavingRoles(false);
|
||||
@@ -354,9 +356,9 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
: row,
|
||||
),
|
||||
);
|
||||
toast.success(`已更新 ${result.username} 的权限`);
|
||||
toast.success(t("savePermissionSuccess", { name: result.username }));
|
||||
} catch (e) {
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : "保存权限失败";
|
||||
const msg = e instanceof LotteryApiBizError ? e.message : t("savePermissionFailed");
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
@@ -368,15 +370,15 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row flex-wrap items-end justify-between gap-4">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
||||
<CardTitle>管理员用户列表</CardTitle>
|
||||
<CardTitle>{t("listTitle")}</CardTitle>
|
||||
<Button type="button" size="sm" onClick={() => openCreateAccount()}>
|
||||
新建管理员
|
||||
{t("createAdmin")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex w-full max-w-lg gap-2">
|
||||
<Input
|
||||
value={keyword}
|
||||
placeholder="按用户名 / 昵称 / 邮箱搜索"
|
||||
placeholder={t("searchPlaceholder")}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
@@ -392,37 +394,37 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
setQuery(keyword.trim());
|
||||
}}
|
||||
>
|
||||
搜索
|
||||
{t("actions.search", { ns: "common" })}
|
||||
</Button>
|
||||
<Button type="button" variant="secondary" onClick={() => void load()}>
|
||||
刷新
|
||||
{t("actions.refresh", { ns: "common" })}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{err ? <p className="text-sm text-red-600 dark:text-red-400">{err}</p> : null}
|
||||
{loading && items.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">加载中…</p>
|
||||
<p className="text-sm text-muted-foreground">{t("states.loading", { ns: "common" })}</p>
|
||||
) : null}
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-16">ID</TableHead>
|
||||
<TableHead>账号</TableHead>
|
||||
<TableHead>昵称</TableHead>
|
||||
<TableHead className="w-20 whitespace-nowrap">状态</TableHead>
|
||||
<TableHead>角色</TableHead>
|
||||
<TableHead>直接权限</TableHead>
|
||||
<TableHead>有效权限</TableHead>
|
||||
<TableHead className="min-w-[11rem]">操作</TableHead>
|
||||
<TableHead>{t("table.account")}</TableHead>
|
||||
<TableHead>{t("table.nickname")}</TableHead>
|
||||
<TableHead className="w-20 whitespace-nowrap">{t("table.status")}</TableHead>
|
||||
<TableHead>{t("table.roles")}</TableHead>
|
||||
<TableHead>{t("table.direct")}</TableHead>
|
||||
<TableHead>{t("table.effective")}</TableHead>
|
||||
<TableHead className="min-w-[11rem]">{t("table.actions")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{items.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-muted-foreground">
|
||||
暂无数据
|
||||
{t("states.noData", { ns: "common" })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
@@ -439,18 +441,18 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<TableCell>
|
||||
{row.status === 0 ? (
|
||||
<Badge variant="secondary" className="font-normal">
|
||||
启用
|
||||
{t("status.enabled")}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant="outline" className="border-amber-600/50 text-amber-800 dark:text-amber-400">
|
||||
禁用
|
||||
{t("status.disabled")}
|
||||
</Badge>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{row.roles.length === 0 ? (
|
||||
<span className="text-xs text-muted-foreground">无</span>
|
||||
<span className="text-xs text-muted-foreground">{t("common.none")}</span>
|
||||
) : (
|
||||
row.roles.map((slug) => (
|
||||
<Badge key={slug} variant="secondary">
|
||||
@@ -472,7 +474,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
openPermissionEditor(row);
|
||||
}}
|
||||
>
|
||||
权限
|
||||
{t("actions.permissions")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -482,7 +484,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
}
|
||||
onClick={() => openEditAccount(row)}
|
||||
>
|
||||
编辑
|
||||
{t("actions.edit")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -490,11 +492,13 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
variant="destructive"
|
||||
disabled={profile?.id === row.id}
|
||||
title={
|
||||
profile?.id === row.id ? "不能删除当前登录账号" : "删除该管理员"
|
||||
profile?.id === row.id
|
||||
? t("delete.currentUserBlocked")
|
||||
: t("delete.rowActionTitle")
|
||||
}
|
||||
onClick={() => setDeleteTarget(row)}
|
||||
>
|
||||
删除
|
||||
{t("actions.delete")}
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -526,7 +530,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
className="flex h-[min(88vh,800px)] max-h-[90vh] w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden p-0 sm:h-[min(85vh,780px)] sm:max-w-3xl"
|
||||
>
|
||||
<DialogHeader className="shrink-0 space-y-1 border-b px-4 py-3 pr-12">
|
||||
<DialogTitle>管理员权限</DialogTitle>
|
||||
<DialogTitle>{t("permissionDialog.title")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{selectedUser ? (
|
||||
<>
|
||||
@@ -541,9 +545,9 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<div className="space-y-6 pb-1">
|
||||
<section className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium leading-none">角色</h3>
|
||||
<h3 className="text-sm font-medium leading-none">{t("permissionDialog.rolesTitle")}</h3>
|
||||
<p className="mt-1.5 text-xs text-muted-foreground">
|
||||
保存至默认站点,与「直接权限」叠加为有效权限。
|
||||
{t("permissionDialog.rolesDescription")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-3 rounded-md border p-3 sm:grid-cols-2">
|
||||
@@ -559,7 +563,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<span className="block leading-none font-medium">{r.name}</span>
|
||||
<span className="text-xs text-muted-foreground">{r.slug}</span>
|
||||
<span className="block text-xs text-muted-foreground/90">
|
||||
含 {r.permission_slugs.length} 项功能权限
|
||||
{t("permissionDialog.rolePermissionCount", { count: r.permission_slugs.length })}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
@@ -570,15 +574,15 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
|
||||
<section className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium leading-none">直接权限</h3>
|
||||
<h3 className="text-sm font-medium leading-none">{t("permissionDialog.directTitle")}</h3>
|
||||
<p className="mt-1.5 text-xs text-muted-foreground">
|
||||
按菜单/业务域展开,勾选具体的 prd.*;多数情况只调角色即可。
|
||||
{t("permissionDialog.directDescription")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-md border bg-muted/20 p-2.5 text-xs text-muted-foreground">
|
||||
当前勾选的角色:
|
||||
{t("permissionDialog.selectedRoles")}
|
||||
{draftRoles.length === 0 ? (
|
||||
<span className="ml-1 text-foreground/80">无</span>
|
||||
<span className="ml-1 text-foreground/80">{t("common.none")}</span>
|
||||
) : (
|
||||
<span className="ml-1 inline-flex flex-wrap gap-1 align-middle">
|
||||
{draftRoles.map((slug) => (
|
||||
@@ -653,7 +657,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
handlePermissionDialogOpenChange(false);
|
||||
}}
|
||||
>
|
||||
关闭
|
||||
{t("actions.close", { ns: "common" })}
|
||||
</Button>
|
||||
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-nowrap sm:justify-end">
|
||||
<Button
|
||||
@@ -663,7 +667,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
disabled={!selectedUser || savingRoles}
|
||||
onClick={() => void saveRoles()}
|
||||
>
|
||||
{savingRoles ? "保存中…" : "保存角色"}
|
||||
{savingRoles ? t("saving") : t("permissionDialog.saveRoles")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -671,7 +675,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
disabled={!selectedUser || saving}
|
||||
onClick={() => void savePermissions()}
|
||||
>
|
||||
{saving ? "保存中…" : "保存直接权限"}
|
||||
{saving ? t("saving") : t("permissionDialog.saveDirect")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -681,63 +685,69 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<Dialog open={accountOpen} onOpenChange={handleAccountDialogOpenChange}>
|
||||
<DialogContent showCloseButton className="max-h-[90vh] max-w-lg gap-4 overflow-y-auto sm:max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{accountMode === "create" ? "新建管理员" : "编辑账号"}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{accountMode === "create" ? t("accountDialog.createTitle") : t("accountDialog.editTitle")}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{accountMode === "create"
|
||||
? "须为账号指定至少一个默认站点角色。登录账号仅可使用字母、数字、点、下划线与连字符,保存后为小写。"
|
||||
: "登录账号不可修改。留空密码表示不修改。"}
|
||||
? t("accountDialog.createDescription")
|
||||
: t("accountDialog.editDescription")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium leading-none">登录账号</div>
|
||||
<div className="text-sm font-medium leading-none">{t("accountDialog.username")}</div>
|
||||
<Input
|
||||
value={formUsername}
|
||||
disabled={accountMode === "edit"}
|
||||
placeholder="例如 ops_admin"
|
||||
placeholder={t("accountDialog.usernamePlaceholder")}
|
||||
autoComplete="off"
|
||||
onChange={(e) => setFormUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium leading-none">昵称</div>
|
||||
<div className="text-sm font-medium leading-none">{t("accountDialog.nickname")}</div>
|
||||
<Input
|
||||
value={formNickname}
|
||||
placeholder="显示名称"
|
||||
placeholder={t("accountDialog.nicknamePlaceholder")}
|
||||
onChange={(e) => setFormNickname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium leading-none">邮箱(可选)</div>
|
||||
<div className="text-sm font-medium leading-none">{t("accountDialog.emailOptional")}</div>
|
||||
<Input
|
||||
type="email"
|
||||
value={formEmail}
|
||||
placeholder="留空则不填"
|
||||
placeholder={t("accountDialog.emailPlaceholder")}
|
||||
onChange={(e) => setFormEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium leading-none">
|
||||
密码{accountMode === "edit" ? "(可选)" : ""}
|
||||
{accountMode === "edit" ? t("accountDialog.passwordOptional") : t("accountDialog.password")}
|
||||
</div>
|
||||
<Input
|
||||
type="password"
|
||||
value={formPassword}
|
||||
placeholder={accountMode === "create" ? "至少 8 位" : "不修改请留空"}
|
||||
placeholder={
|
||||
accountMode === "create"
|
||||
? t("accountDialog.passwordPlaceholderCreate")
|
||||
: t("accountDialog.passwordPlaceholderEdit")
|
||||
}
|
||||
autoComplete="new-password"
|
||||
onChange={(e) => setFormPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{accountMode === "create" ? (
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-medium leading-none">角色(默认站点,至少一项)</div>
|
||||
<div className="text-sm font-medium leading-none">{t("accountDialog.rolesRequired")}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
创建后即可在「权限」中继续调整角色或直接授权。
|
||||
{t("accountDialog.rolesDescription")}
|
||||
</p>
|
||||
<div className="max-h-52 space-y-2 overflow-y-auto rounded-md border p-2.5 sm:grid sm:max-h-56 sm:grid-cols-2 sm:gap-2 sm:space-y-0">
|
||||
{(catalog?.roles ?? []).length === 0 ? (
|
||||
<p className="col-span-full text-xs text-muted-foreground">
|
||||
暂无角色数据,请等待列表加载完成后重试。
|
||||
{t("accountDialog.noRoles")}
|
||||
</p>
|
||||
) : (
|
||||
(catalog?.roles ?? []).map((r) => {
|
||||
@@ -760,14 +770,14 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
</div>
|
||||
) : null}
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium leading-none">状态</div>
|
||||
<div className="text-sm font-medium leading-none">{t("table.status")}</div>
|
||||
<select
|
||||
className={selectClassName}
|
||||
value={formStatus}
|
||||
onChange={(e) => setFormStatus(Number(e.target.value))}
|
||||
>
|
||||
<option value={0}>启用</option>
|
||||
<option value={1}>禁用</option>
|
||||
<option value={0}>{t("status.enabled")}</option>
|
||||
<option value={1}>{t("status.disabled")}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -777,10 +787,10 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
variant="outline"
|
||||
onClick={() => handleAccountDialogOpenChange(false)}
|
||||
>
|
||||
取消
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
<Button type="button" disabled={accountSaving} onClick={() => void submitAccount()}>
|
||||
{accountSaving ? "保存中…" : "保存"}
|
||||
{accountSaving ? t("saving") : t("actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
@@ -789,14 +799,10 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
<Dialog open={deleteTarget !== null} onOpenChange={(open) => !open && setDeleteTarget(null)}>
|
||||
<DialogContent showCloseButton className="max-w-md gap-4">
|
||||
<DialogHeader>
|
||||
<DialogTitle>确认删除</DialogTitle>
|
||||
<DialogTitle>{t("delete.confirmTitle")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{deleteTarget ? (
|
||||
<>
|
||||
确定删除管理员{" "}
|
||||
<span className="font-medium text-foreground">{deleteTarget.username}</span>
|
||||
?此操作不可撤销。
|
||||
</>
|
||||
<>{t("delete.confirmDescription", { name: deleteTarget.username })}</>
|
||||
) : null}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -807,7 +813,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
disabled={deleteBusy}
|
||||
onClick={() => setDeleteTarget(null)}
|
||||
>
|
||||
取消
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -815,7 +821,7 @@ export function AdminUsersConsole(): React.ReactElement {
|
||||
disabled={deleteBusy}
|
||||
onClick={() => void confirmDelete()}
|
||||
>
|
||||
{deleteBusy ? "删除中…" : "删除"}
|
||||
{deleteBusy ? t("deleting") : t("actions.delete")}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
Reference in New Issue
Block a user