247 lines
9.8 KiB
JavaScript

import PasswordOutlined from "@mui/icons-material/PasswordOutlined";
import PersonAddAltOutlined from "@mui/icons-material/PersonAddAltOutlined";
import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined";
import AdminPanelSettingsOutlined from "@mui/icons-material/AdminPanelSettingsOutlined";
import { ExternalAdminPermissionDrawer } from "@/features/external-admin-users/components/ExternalAdminPermissionDrawer.jsx";
import { ExternalAdminUserFormDialog } from "@/features/external-admin-users/components/ExternalAdminUserFormDialog.jsx";
import { ExternalAdminUserPasswordDialog } from "@/features/external-admin-users/components/ExternalAdminUserPasswordDialog.jsx";
import styles from "@/features/external-admin-users/external-admin-users.module.css";
import { useExternalAdminUsersPage } from "@/features/external-admin-users/hooks/useExternalAdminUsersPage.js";
import {
AdminActionIconButton,
AdminFilterResetButton,
AdminFilterSelect,
AdminListBody,
AdminListPage,
AdminListToolbar,
AdminRowActions,
AdminSearchBox,
} from "@/shared/ui/AdminListLayout.jsx";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx";
import { formatMillis } from "@/shared/utils/time.js";
const statusOptions = [
["", "全部状态"],
["active", "启用"],
["disabled", "停用"],
];
const columns = [
{
key: "linkedUser",
label: "绑定 App 用户",
render: (item) => (
<AdminUserIdentity
openInAppUserDetail
rows={[item.linkedUser?.displayUserId, item.linkedUser?.userId]}
user={item.linkedUser}
/>
),
width: "minmax(250px, 1.2fr)",
},
{
key: "username",
label: "外管账号",
render: (item) => (
<div className={styles.accountCell}>
<span className={styles.accountName}>{item.username || "-"}</span>
<span className={styles.accountMeta}>ID {item.id || "-"}</span>
</div>
),
width: "minmax(190px, 0.95fr)",
},
{
key: "status",
label: "状态",
render: (item, _index, context) => {
const page = context?.page;
// 外管状态、密码和权限都是账号级敏感写操作;同页串行化可避免多个请求互相覆盖全局 loading 状态。
const accountActionInFlight = Boolean(page?.loadingAction);
return (
<AdminSwitch
checked={item.status === "active"}
checkedLabel="启用"
disabled={!page?.abilities.canStatus || accountActionInFlight}
label={item.status === "active" ? "停用外管用户" : "启用外管用户"}
uncheckedLabel="停用"
onChange={() => page.toggleStatus(item)}
/>
);
},
width: "minmax(120px, 0.65fr)",
},
{
key: "permissions",
label: "外管权限",
render: (item) => `${item.permissions?.length || 0}`,
width: "minmax(120px, 0.65fr)",
},
{
key: "createdByAdminId",
label: "创建人",
render: (item) => item.createdByAdminId || "-",
width: "minmax(130px, 0.7fr)",
},
{
key: "lastLoginAtMs",
label: "最近登录",
render: (item) => formatMillis(item.lastLoginAtMs),
width: "minmax(170px, 0.85fr)",
},
{
key: "createdAtMs",
label: "创建时间",
render: (item) => formatMillis(item.createdAtMs),
width: "minmax(170px, 0.85fr)",
},
{
key: "actions",
label: "操作",
render: (item, _index, context) => {
const page = context?.page;
return (
<AdminRowActions>
<AdminActionIconButton
disabled={!page?.abilities.canManagePermissions || Boolean(page.loadingAction)}
label="配置权限"
onClick={() => page.openPermissions(item)}
>
<AdminPanelSettingsOutlined fontSize="small" />
</AdminActionIconButton>
<AdminActionIconButton
disabled={!page?.abilities.canResetPassword || Boolean(page.loadingAction)}
label="重置密码"
onClick={() => page.openPassword(item)}
>
<PasswordOutlined fontSize="small" />
</AdminActionIconButton>
</AdminRowActions>
);
},
resizable: false,
width: "132px",
},
];
export function ExternalAdminUsersPage() {
const page = useExternalAdminUsersPage();
const items = page.data.items || [];
const total = Number(page.data.total || 0);
return (
<AdminListPage>
<AdminListToolbar
actions={
<>
<AdminActionIconButton
component="a"
href="/external-admin/"
label="打开外管后台"
rel="noopener noreferrer"
target="_blank"
>
<OpenInNewOutlined fontSize="small" />
</AdminActionIconButton>
{page.abilities.canCreate ? (
<AdminActionIconButton
disabled={Boolean(page.loadingAction)}
label="创建外管用户"
primary
onClick={page.openCreate}
>
<PersonAddAltOutlined fontSize="small" />
</AdminActionIconButton>
) : null}
</>
}
filters={
<>
<AdminSearchBox
label="搜索"
placeholder="外管账号、短 ID"
value={page.keyword}
onChange={page.setKeyword}
/>
<AdminFilterSelect
label="状态"
options={statusOptions}
value={page.status}
onChange={page.setStatus}
/>
<AdminFilterResetButton
disabled={!page.keyword && !page.status}
onClick={page.resetFilters}
/>
</>
}
/>
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
<AdminListBody>
<DataTable
columns={columns}
context={{ page }}
items={items}
minWidth="1350px"
pagination={
total > 0
? {
onPageChange: page.setPage,
page: page.page,
pageSize: page.data.pageSize || page.pageSize,
total,
}
: undefined
}
rowKey={(item) => item.id || `${item.appCode}-${item.username}`}
/>
</AdminListBody>
</DataState>
<ExternalAdminUserFormDialog
abilities={page.abilities}
catalog={page.permissionCatalog.items || []}
catalogError={page.catalogError}
catalogLoading={page.catalogLoading}
form={page.createForm}
loading={page.loadingAction === "create"}
lookupLoading={page.lookupLoading}
open={page.createOpen}
targetUser={page.targetUser}
onClose={page.closeCreate}
onDisplayUserIdChange={page.setDisplayUserId}
onFormChange={page.setCreateForm}
onLookup={page.lookupTarget}
onReloadCatalog={page.reloadPermissionCatalog}
onSubmit={page.submitCreate}
/>
<ExternalAdminPermissionDrawer
abilities={page.abilities}
catalog={page.permissionCatalog.items || []}
catalogError={page.catalogError}
catalogLoading={page.catalogLoading}
error={page.permissionError}
loading={page.loadingAction.startsWith("permissions:")}
permissions={page.permissionForm.permissions}
permissionsLoading={page.permissionLoading}
user={page.permissionUser}
onChange={page.setPermissions}
onClose={page.closePermissions}
onReloadCatalog={page.reloadPermissionCatalog}
onRetry={page.retryPermissions}
onSubmit={page.submitPermissions}
/>
<ExternalAdminUserPasswordDialog
abilities={page.abilities}
form={page.passwordForm}
loading={page.loadingAction.startsWith("password:")}
user={page.passwordUser}
onClose={page.closePassword}
onFormChange={page.setPasswordForm}
onSubmit={page.submitPassword}
/>
</AdminListPage>
);
}