UserPermDrawer.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { PermTree } from '@/pages/Admin/_shared/PermTree';
  2. import { calcUserPermDelta } from '@/pages/Admin/_shared/PermTree/lib/permUtils';
  3. import { fetchMemberUserProducts } from '@/services/member';
  4. import { fetchPermList } from '@/services/perm';
  5. import { fetchRoleDetail } from '@/services/role';
  6. import { fetchGetUserPerms, fetchSetUserPerms, fetchUserDetail } from '@/services/user';
  7. import { Button, Drawer, Modal, Select, Spin, message } from 'antd';
  8. import { useEffect, useState } from 'react';
  9. interface UserPermDrawerProps {
  10. userId: number;
  11. open: boolean;
  12. onClose: () => void;
  13. }
  14. export const UserPermDrawer = ({ userId, open, onClose }: UserPermDrawerProps) => {
  15. const [products, setProducts] = useState<API.UserProductItem[]>([]);
  16. const [productCode, setProductCode] = useState<string | undefined>();
  17. const [loading, setLoading] = useState(false);
  18. const [saving, setSaving] = useState(false);
  19. const [allPerms, setAllPerms] = useState<API.PermItem[]>([]);
  20. const [checkedIds, setCheckedIds] = useState<number[]>([]);
  21. const [initialCheckedIds, setInitialCheckedIds] = useState<number[]>([]);
  22. const [roleInheritedIds, setRoleInheritedIds] = useState<number[]>([]);
  23. useEffect(() => {
  24. if (!open) return;
  25. setProductCode(undefined);
  26. setAllPerms([]);
  27. setCheckedIds([]);
  28. setInitialCheckedIds([]);
  29. setRoleInheritedIds([]);
  30. fetchMemberUserProducts({ userId }).then((res) => {
  31. const list = res.data?.list ?? [];
  32. setProducts(list);
  33. if (list.length > 0) setProductCode(list[0].productCode);
  34. });
  35. }, [open]);
  36. useEffect(() => {
  37. if (!open || !productCode) return;
  38. setLoading(true);
  39. const load = async () => {
  40. const [userRes, permRes, userPermsRes] = await Promise.all([
  41. fetchUserDetail({ id: userId }),
  42. fetchPermList({ productCode, pageSize: 9999 }),
  43. fetchGetUserPerms({ userId }),
  44. ]);
  45. const perms = permRes.data ?? [];
  46. setAllPerms(perms);
  47. const permIdSet = new Set(perms.map((p: API.PermItem) => p.id));
  48. const userPerms = userPermsRes.data?.perms ?? [];
  49. const allowIds = new Set(
  50. userPerms
  51. .filter((p) => p.effect === 'ALLOW')
  52. .map((p) => p.permId)
  53. .filter((id) => permIdSet.has(id)),
  54. );
  55. const denyIds = new Set(userPerms.filter((p) => p.effect === 'DENY').map((p) => p.permId));
  56. const roleIds = userRes.data?.roleIds ?? [];
  57. const roleDetails = await Promise.all(roleIds.map((id) => fetchRoleDetail({ id })));
  58. const inherited = new Set<number>();
  59. roleDetails.forEach((r) => (r.data?.permIds ?? []).forEach((id) => inherited.add(id)));
  60. setRoleInheritedIds([...inherited]);
  61. const effective = [...inherited, ...allowIds].filter((id) => !denyIds.has(id));
  62. setCheckedIds([...new Set(effective)]);
  63. setInitialCheckedIds([...new Set(effective)]);
  64. };
  65. load().finally(() => setLoading(false));
  66. }, [open, userId, productCode]);
  67. const isDirty =
  68. checkedIds.length !== initialCheckedIds.length ||
  69. checkedIds.some((id) => !initialCheckedIds.includes(id));
  70. const handleProductChange = (code: string) => {
  71. if (productCode && isDirty) {
  72. Modal.confirm({
  73. title: '未保存的修改',
  74. content: '当前产品的权限设置已修改但未保存,切换将丢失修改。确认切换?',
  75. okText: '确认切换',
  76. cancelText: '取消',
  77. onOk: () => setProductCode(code),
  78. });
  79. return;
  80. }
  81. setProductCode(code);
  82. };
  83. const handleSave = async () => {
  84. setSaving(true);
  85. try {
  86. const perms = calcUserPermDelta(checkedIds, roleInheritedIds);
  87. await fetchSetUserPerms({ userId, perms });
  88. message.success('保存成功');
  89. onClose();
  90. } finally {
  91. setSaving(false);
  92. }
  93. };
  94. return (
  95. <Drawer
  96. title="设置权限"
  97. open={open}
  98. onClose={onClose}
  99. width={560}
  100. footer={
  101. <div className="flex justify-end gap-2">
  102. <Button onClick={onClose}>取消</Button>
  103. <Button
  104. type="primary"
  105. loading={saving}
  106. onClick={handleSave}
  107. disabled={!productCode || !isDirty}
  108. >
  109. 确定
  110. </Button>
  111. </div>
  112. }
  113. >
  114. {products.length === 0 ? (
  115. <span className="text-gray-400 text-sm">用户不属于任何产品,无法设置权限</span>
  116. ) : (
  117. <>
  118. <div className="mb-4">
  119. <Select
  120. placeholder="请选择产品"
  121. options={products.map((p) => ({ label: p.productName, value: p.productCode }))}
  122. value={productCode}
  123. onChange={handleProductChange}
  124. className="w-full"
  125. />
  126. </div>
  127. {productCode && (
  128. <Spin spinning={loading}>
  129. <PermTree
  130. mode="edit"
  131. allPerms={allPerms}
  132. checkedPermIds={checkedIds}
  133. onChange={setCheckedIds}
  134. />
  135. </Spin>
  136. )}
  137. </>
  138. )}
  139. </Drawer>
  140. );
  141. };