index.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { MEMBER_TYPE_COLORS, MEMBER_TYPE_LABELS, STATUS_DISABLED, STATUS_ENABLED } from '@/defines';
  2. import { BindRolesDrawer } from '@/pages/Admin/_shared/BindRolesDrawer';
  3. import { UserPermDrawer } from '@/pages/Admin/_shared/UserPermDrawer';
  4. import {
  5. useProductRolesBase,
  6. useUserProductRoles,
  7. } from '@/pages/Admin/_shared/useUserProductRoles';
  8. import { fetchUpdateUserStatus, fetchUserList } from '@/services/user';
  9. import { unixTimeFormat } from '@/utils/timeUtils';
  10. import { StopOutlined } from '@ant-design/icons';
  11. import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
  12. import { useIntl } from '@umijs/max';
  13. import { Button, Popconfirm, Tag, message } from 'antd';
  14. import { useMemo, useRef, useState } from 'react';
  15. import { UserForm } from './components/Form';
  16. export default function UserPage() {
  17. const intl = useIntl();
  18. const actionRef = useRef<ActionType>();
  19. const [pageUsers, setPageUsers] = useState<API.UserItem[]>([]);
  20. const [rolesDrawer, setRolesDrawer] = useState<{ open: boolean; userId: number }>({
  21. open: false,
  22. userId: 0,
  23. });
  24. const [permDrawer, setPermDrawer] = useState<{ open: boolean; userId: number }>({
  25. open: false,
  26. userId: 0,
  27. });
  28. const [refreshKey, setRefreshKey] = useState(0);
  29. const userIds = useMemo(() => pageUsers.map((u) => u.id), [pageUsers]);
  30. const productRolesBase = useProductRolesBase();
  31. const { userProductInfo } = useUserProductRoles(userIds, productRolesBase, refreshKey);
  32. const handleToggleStatus = async (r: API.UserItem) => {
  33. const res = await fetchUpdateUserStatus({
  34. id: r.id,
  35. status: r.status === STATUS_ENABLED ? STATUS_DISABLED : STATUS_ENABLED,
  36. });
  37. if (!res.success) return;
  38. message.success('操作成功');
  39. actionRef.current?.reload();
  40. };
  41. const columns: ProColumns<API.UserItem>[] = [
  42. { title: '用户名', dataIndex: 'username', width: 200 },
  43. { title: '昵称', dataIndex: 'nickname', width: 200 },
  44. {
  45. title: '状态',
  46. dataIndex: 'status',
  47. width: 80,
  48. render: (_, r) => (
  49. <Tag color={r.status === STATUS_ENABLED ? 'success' : 'error'}>
  50. {r.status === STATUS_ENABLED ? '启用' : '禁用'}
  51. </Tag>
  52. ),
  53. },
  54. {
  55. title: '产品角色',
  56. key: 'productRoles',
  57. render: (_, r) => {
  58. const info = userProductInfo.get(r.id) ?? [];
  59. if (info.length === 0)
  60. return <span className="text-(--ant-color-text-quaternary) text-xs">无</span>;
  61. return (
  62. <div className="flex flex-col gap-1">
  63. {info.map((p) => (
  64. <div
  65. key={p.productName}
  66. className="flex flex-row gap-1 rounded border border-dashed border-(--ant-color-border) bg-(--ant-color-fill-quaternary) px-2 py-2"
  67. >
  68. <Tag color="purple" className="mr-0 self-center">
  69. {p.productName}
  70. {p.productStatus !== STATUS_ENABLED && (
  71. <StopOutlined className="ml-1 text-(--ant-color-error)!" />
  72. )}
  73. </Tag>
  74. <Tag
  75. color={MEMBER_TYPE_COLORS[p.memberType ?? ''] ?? 'default'}
  76. className="mr-0 self-center"
  77. >
  78. {MEMBER_TYPE_LABELS[p.memberType ?? ''] ?? p.memberType}
  79. </Tag>
  80. <div className="flex flex-row flex-wrap gap-1 items-center">
  81. {p.roleNames.length > 0 ? (
  82. p.roleNames.map((name) => (
  83. <Tag key={name} className="mr-0">
  84. {name}
  85. </Tag>
  86. ))
  87. ) : (
  88. <span className="text-(--ant-color-text-quaternary) text-xs whitespace-nowrap">
  89. 无角色
  90. </span>
  91. )}
  92. </div>
  93. </div>
  94. ))}
  95. </div>
  96. );
  97. },
  98. },
  99. {
  100. title: '创建时间',
  101. dataIndex: 'createTime',
  102. width: 180,
  103. render: (_, r) => unixTimeFormat(r.createTime),
  104. },
  105. {
  106. title: '操作',
  107. valueType: 'option',
  108. width: 260,
  109. render: (_, r) => [
  110. <UserForm
  111. key="edit"
  112. mode="edit"
  113. initialValues={r}
  114. trigger={<a>编辑</a>}
  115. onSuccess={() => actionRef.current?.reload()}
  116. />,
  117. <UserForm
  118. key="copy"
  119. mode="copy"
  120. initialValues={r}
  121. trigger={<a>复制</a>}
  122. onSuccess={() => actionRef.current?.reload()}
  123. />,
  124. <a key="roles" onClick={() => setRolesDrawer({ open: true, userId: r.id })}>
  125. 分配角色
  126. </a>,
  127. <a key="perms" onClick={() => setPermDrawer({ open: true, userId: r.id })}>
  128. 设置权限
  129. </a>,
  130. <Popconfirm
  131. key="status"
  132. title={
  133. r.status === STATUS_ENABLED
  134. ? `确认冻结「${r.username}」?`
  135. : `确认解冻「${r.username}」?`
  136. }
  137. onConfirm={() => handleToggleStatus(r)}
  138. >
  139. <a className={r.status === STATUS_ENABLED ? 'text-(--ant-color-error)' : ''}>
  140. {r.status === STATUS_ENABLED ? '冻结' : '解冻'}
  141. </a>
  142. </Popconfirm>,
  143. ],
  144. },
  145. ];
  146. return (
  147. <PageContainer title={intl.formatMessage({ id: 'admin.user.title' })}>
  148. <ProTable<API.UserItem>
  149. actionRef={actionRef}
  150. columns={columns}
  151. rowKey="id"
  152. search={false}
  153. request={fetchUserList}
  154. onDataSourceChange={setPageUsers}
  155. scroll={{ x: 1220 }}
  156. tableLayout="fixed"
  157. pagination={{
  158. defaultPageSize: 20,
  159. pageSizeOptions: [10, 20, 50, 100],
  160. showSizeChanger: true,
  161. }}
  162. toolBarRender={() => [
  163. <UserForm
  164. key="create"
  165. mode="add"
  166. trigger={<Button type="primary">新建用户</Button>}
  167. onSuccess={() => actionRef.current?.reload()}
  168. />,
  169. ]}
  170. />
  171. <BindRolesDrawer
  172. {...rolesDrawer}
  173. onClose={() => setRolesDrawer((p) => ({ ...p, open: false }))}
  174. onSuccess={() => setRefreshKey((k) => k + 1)}
  175. />
  176. <UserPermDrawer
  177. {...permDrawer}
  178. onClose={() => setPermDrawer((p) => ({ ...p, open: false }))}
  179. />
  180. </PageContainer>
  181. );
  182. }