调整数据授权管理的前端优化

main
kangwenjing 2026-07-01 17:48:21 +08:00
parent aa632ec6ec
commit 09098a180c
2 changed files with 108 additions and 5 deletions

View File

@ -1,5 +1,6 @@
.user-data-scope-page {
min-height: 100%;
overflow: hidden;
}
.user-data-scope-overview {
@ -38,7 +39,45 @@
}
.user-data-scope-table-card {
flex: 1;
min-height: 0;
overflow: hidden;
.ant-card-body {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
.user-data-scope-table {
min-height: 0;
display: flex;
flex: 1;
flex-direction: column;
.ant-spin-nested-loading,
.ant-spin-container {
min-height: 0;
display: flex;
flex: 1;
flex-direction: column;
}
.ant-table {
min-height: 0;
flex: 1;
}
.ant-table-container {
min-height: 0;
}
.ant-table-pagination {
flex-shrink: 0;
margin: 12px 16px !important;
}
}
@media (max-width: 991px) {

View File

@ -4,10 +4,10 @@ import dayjs from "dayjs";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DeleteOutlined, EditOutlined, PlusOutlined, SearchOutlined, TeamOutlined } from "@ant-design/icons";
import PageHeader from "@/components/shared/PageHeader";
import { deleteUserDataScopeGrant, listTenants, listUserDataScopeGrants, listUserDataScopeUsers, saveUserDataScopeAssignment } from "@/api";
import { deleteUserDataScopeGrant, fetchUsersByRoleId, listRoles, listTenants, listUserDataScopeGrants, listUserDataScopeUsers, saveUserDataScopeAssignment } from "@/api";
import { usePermission } from "@/hooks/usePermission";
import { getStandardPagination } from "@/utils/pagination";
import type { SysTenant, UserDataScopeGrant, UserDataScopeUser } from "@/types";
import type { SysRole, SysTenant, UserDataScopeGrant, UserDataScopeUser } from "@/types";
import "./index.less";
const { Text } = Typography;
@ -141,7 +141,9 @@ export default function UserDataScopePage() {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [roleUserLoading, setRoleUserLoading] = useState(false);
const [users, setUsers] = useState<UserDataScopeUser[]>([]);
const [roles, setRoles] = useState<SysRole[]>([]);
const [grants, setGrants] = useState<UserDataScopeGrant[]>([]);
const [tenants, setTenants] = useState<SysTenant[]>([]);
const [drawerOpen, setDrawerOpen] = useState(false);
@ -170,6 +172,15 @@ export default function UserDataScopePage() {
setUsers(list || []);
}, [selectedTenantId]);
const loadRoles = useCallback(async () => {
if (!selectedTenantId) {
setRoles([]);
return;
}
const list = await listRoles(selectedTenantId);
setRoles(list || []);
}, [selectedTenantId]);
const loadGrants = useCallback(async () => {
if (!selectedTenantId) {
setGrants([]);
@ -182,11 +193,11 @@ export default function UserDataScopePage() {
const loadPageData = useCallback(async () => {
setLoading(true);
try {
await Promise.all([loadTenants(), loadUsers(), loadGrants()]);
await Promise.all([loadTenants(), loadUsers(), loadRoles(), loadGrants()]);
} finally {
setLoading(false);
}
}, [loadTenants, loadUsers, loadGrants]);
}, [loadTenants, loadUsers, loadRoles, loadGrants]);
useEffect(() => {
loadPageData();
@ -244,12 +255,28 @@ export default function UserDataScopePage() {
[selectedDrawerViewerId, userOptions]
);
const roleOptions = useMemo(() => roles.map((role) => {
const label = role.roleName || role.roleCode || String(role.roleId);
return {
label: (
<Space direction="vertical" size={0}>
<span>{label}</span>
{role.roleCode && role.roleCode !== label && <Text type="secondary" style={{ fontSize: 12 }}>{role.roleCode}</Text>}
</Space>
),
displayLabel: label,
value: role.roleId,
searchText: `${role.roleName || ""} ${role.roleCode || ""}`
};
}), [roles]);
const openCreate = () => {
setEditingGrantGroup(null);
form.resetFields();
form.setFieldsValue({
resourceTypes: ["OPPORTUNITY"],
enabled: true,
roleIds: [],
ownerUserIds: []
});
setDrawerOpen(true);
@ -261,6 +288,7 @@ export default function UserDataScopePage() {
form.setFieldsValue({
viewerUserId: record.viewerUserId,
resourceTypes: record.resourceTypes,
roleIds: [],
ownerUserIds: record.ownerUsers.map((owner) => owner.userId),
enabled: record.enabled,
expireAt: record.expireAt ? dayjs(record.expireAt) : undefined,
@ -274,6 +302,27 @@ export default function UserDataScopePage() {
setEditingGrantGroup(null);
};
const handleRoleSelectChange = async (roleIds: number[]) => {
form.setFieldValue("roleIds", roleIds);
if (!roleIds.length) {
return;
}
const selectedViewerUserId = form.getFieldValue("viewerUserId");
const selectedOwnerIds = new Set<number>(form.getFieldValue("ownerUserIds") || []);
setRoleUserLoading(true);
try {
const roleUsers = await Promise.all(roleIds.map((roleId) => fetchUsersByRoleId(roleId)));
roleUsers.flat().forEach((user) => {
if (user?.userId && user.userId !== selectedViewerUserId) {
selectedOwnerIds.add(user.userId);
}
});
form.setFieldValue("ownerUserIds", Array.from(selectedOwnerIds));
} finally {
setRoleUserLoading(false);
}
};
const handleSave = async () => {
if (!selectedTenantId) {
message.warning("请先选择租户");
@ -469,12 +518,13 @@ export default function UserDataScopePage() {
<Card className="app-page__content-card user-data-scope-table-card" styles={{ body: { padding: 0 } }}>
<Table
className="user-data-scope-table"
rowKey="id"
loading={loading}
columns={columns}
dataSource={pagedGrantGroups}
size="middle"
scroll={{ y: "calc(100vh - 390px)", x: 980 }}
scroll={{ y: "max(260px, calc(100vh - 470px))", x: 980 }}
pagination={getStandardPagination(
filteredGrantGroups.length,
query.current,
@ -506,6 +556,20 @@ export default function UserDataScopePage() {
<Form.Item label="资源范围" name="resourceTypes" rules={[{ required: true, message: "请选择资源范围" }]}>
<Select mode="multiple" allowClear options={resourceOptions} maxTagCount="responsive" />
</Form.Item>
<Form.Item label="按角色添加" name="roleIds">
<Select
mode="multiple"
showSearch
allowClear
placeholder="选择角色后自动加入该角色下人员"
options={roleOptions}
loading={roleUserLoading}
optionFilterProp="searchText"
optionLabelProp="displayLabel"
maxTagCount="responsive"
onChange={handleRoleSelectChange}
/>
</Form.Item>
<Form.Item label="可见人员" name="ownerUserIds" rules={[{ required: true, message: "请选择可见人员" }]}>
<Select
mode="multiple"