hyapp-admin-platform/src/features/external-admin-users/components/ExternalAdminPermissionSelector.jsx

145 lines
6.6 KiB
JavaScript

import Checkbox from "@mui/material/Checkbox";
import CircularProgress from "@mui/material/CircularProgress";
import { useMemo } from "react";
import { Button } from "@/shared/ui/Button.jsx";
import styles from "@/features/external-admin-users/external-admin-users.module.css";
import {
updatePermissionGroup,
updatePermissionSelection,
} from "@/features/external-admin-users/permissionSelection.js";
export { updatePermissionSelection } from "@/features/external-admin-users/permissionSelection.js";
export function ExternalAdminPermissionSelector({
catalog = [],
disabled = false,
error = "",
loading = false,
onChange,
onRetry,
value = [],
}) {
const groups = useMemo(() => groupCatalog(catalog), [catalog]);
const knownCodes = useMemo(() => catalog.map((permission) => permission.code), [catalog]);
const selected = useMemo(() => new Set(value), [value]);
const selectedCount = knownCodes.filter((code) => selected.has(code)).length;
if (loading) {
return (
<div className={styles.permissionState} role="status">
<CircularProgress size={22} />
<span>加载权限目录</span>
</div>
);
}
if (error) {
return (
<div className={styles.permissionState} role="alert">
<span>{error}</span>
{onRetry ? <Button onClick={onRetry}>重新加载</Button> : null}
</div>
);
}
return (
<div className={styles.permissionSelector}>
<div className={styles.permissionToolbar}>
<strong>
已选 {selectedCount} / {knownCodes.length}
</strong>
<div className={styles.permissionToolbarActions}>
<Button
disabled={disabled || selectedCount === knownCodes.length}
onClick={() => onChange?.(knownCodes)}
>
全选
</Button>
<Button disabled={disabled || selectedCount === 0} onClick={() => onChange?.([])}>
清空
</Button>
</div>
</div>
{groups.length ? (
<div className={styles.permissionGroups}>
{groups.map((group) => {
const groupCodes = group.items.map((permission) => permission.code);
const groupSelectedCount = groupCodes.filter((code) => selected.has(code)).length;
return (
<section className={styles.permissionGroup} key={group.name}>
<header className={styles.permissionGroupHeader}>
<strong>{group.name}</strong>
<label>
<Checkbox
checked={groupSelectedCount === groupCodes.length}
disabled={disabled}
indeterminate={groupSelectedCount > 0 && groupSelectedCount < groupCodes.length}
slotProps={{ input: { "aria-label": `${group.name}全选` } }}
size="small"
onChange={(event) =>
onChange?.(
updatePermissionGroup(
catalog,
value,
groupCodes,
event.target.checked,
),
)
}
/>
<span>
{groupSelectedCount}/{groupCodes.length}
</span>
</label>
</header>
<div className={styles.permissionOptions}>
{group.items.map((permission) => {
return (
<label className={styles.permissionOption} key={permission.code}>
<Checkbox
checked={selected.has(permission.code)}
disabled={disabled}
slotProps={{ input: { "aria-label": permission.label } }}
size="small"
onChange={(event) =>
onChange?.(
updatePermissionSelection(
catalog,
value,
permission.code,
event.target.checked,
),
)
}
/>
<span>
<strong>{permission.label}</strong>
</span>
</label>
);
})}
</div>
</section>
);
})}
</div>
) : (
<div className={styles.permissionState}>当前无可配置权限</div>
)}
</div>
);
}
function groupCatalog(catalog) {
const groups = new Map();
catalog.forEach((permission) => {
const groupName = permission.group || "其他";
if (!groups.has(groupName)) {
groups.set(groupName, []);
}
groups.get(groupName).push(permission);
});
return [...groups].map(([name, items]) => ({ items, name }));
}