145 lines
6.6 KiB
JavaScript
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 }));
|
|
}
|