这是基于vue-vben-admin 模板适用于abp Vnext的前端管理项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

139 lines
4.2 KiB

import type React from "react";
import { useMemo } from "react";
import { Checkbox, TreeSelect, Spin } from "antd";
import { useTranslation } from "react-i18next";
import { useQuery } from "@tanstack/react-query";
import { getListApi as getPermissionsApi } from "@/api/management/permissions/definitions";
import { getListApi as getGroupsApi } from "@/api/management/permissions/groups";
import { listToTree } from "@/utils/tree";
import { localizationSerializer } from "@/utils/abp/localization-serializer";
import { useLocalizer } from "@/hooks/abp/use-localization";
interface ValueType {
permissions: string[];
requiresAll: boolean;
}
interface PermissionStateCheckProps {
value?: ValueType;
onChange?: (value: ValueType) => void;
}
const PermissionStateCheck: React.FC<PermissionStateCheckProps> = ({
value = { permissions: [], requiresAll: false },
onChange,
}) => {
const { t: $t } = useTranslation();
const { deserialize } = localizationSerializer();
const { Lr } = useLocalizer();
// 1. Fetch Data
const { data: permissionData, isLoading } = useQuery({
queryKey: ["permissionStateCheckData"],
queryFn: async () => {
const [groupsRes, permissionsRes] = await Promise.all([getGroupsApi(), getPermissionsApi()]);
// Prepare localized permissions list
const permissions = permissionsRes.items.map((p) => {
const d = deserialize(p.displayName);
return {
...p,
displayName: Lr(d.resourceName, d.name), // Localized Name
name: p.name,
parentName: p.parentName,
groupName: p.groupName,
};
});
// Prepare localized groups
const groups = groupsRes.items.map((g) => {
const d = deserialize(g.displayName);
return {
...g,
displayName: Lr(d.resourceName, d.name),
name: g.name,
};
});
return { groups, permissions };
},
staleTime: Number.POSITIVE_INFINITY,
});
// 2. Construct Tree Data
const treeData = useMemo(() => {
if (!permissionData) return [];
const { groups, permissions } = permissionData;
return groups.map((group) => {
// Find permissions belonging to this group
const groupPermissions = permissions.filter((p) => p.groupName === group.name);
// Build hierarchy for permissions (parent/child)
// Standard listToTree usage: (list, config)
const children = listToTree(groupPermissions, { id: "name", pid: "parentName" });
// Return Group Node
// Note: We use 'displayName' and 'name' which match the DTOs,
// and map them using the 'fieldNames' prop on TreeSelect below.
return {
displayName: group.displayName,
name: group.name,
// UI props to make group unselectable/uncheckable
checkable: false,
selectable: false,
disableCheckbox: true,
children: children,
};
});
}, [permissionData]);
// 3. Map selected string[] to { label, value } objects for TreeSelect (Strict Mode)
const treeValue = useMemo(() => {
if (!permissionData || !value.permissions) return [];
return value.permissions.map((name) => {
const permission = permissionData.permissions.find((p) => p.name === name);
return {
label: permission?.displayName || name,
value: name,
};
});
}, [permissionData, value.permissions]);
// 4. Handle Change
const triggerChange = (changedValue: Partial<ValueType>) => {
onChange?.({ ...value, ...changedValue });
};
const onTreeChange = (labeledValues: { label: React.ReactNode; value: string }[]) => {
const names = labeledValues.map((item) => item.value);
triggerChange({ permissions: names });
};
if (isLoading) return <Spin className="my-2" />;
return (
<div className="flex flex-col gap-2 w-full">
<Checkbox checked={value.requiresAll} onChange={(e) => triggerChange({ requiresAll: e.target.checked })}>
{$t("component.simple_state_checking.requirePermissions.requiresAll")}
</Checkbox>
<TreeSelect
treeData={treeData}
value={treeValue}
onChange={onTreeChange}
// Map DTO fields to AntD TreeSelect expected fields
fieldNames={{ label: "displayName", value: "name", children: "children" }}
allowClear
treeCheckable
treeCheckStrictly
showCheckedStrategy={TreeSelect.SHOW_ALL}
placeholder={$t("ui.placeholder.select")}
style={{ width: "100%" }}
treeDefaultExpandAll
/>
</div>
);
};
export default PermissionStateCheck;