UsersTable.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import React, { useEffect, useState } from 'react';
  2. import { Button, Form, Label, Pagination, Popup, Table } from 'semantic-ui-react';
  3. import { Link } from 'react-router-dom';
  4. import { API, showError, showSuccess } from '../helpers';
  5. import { ITEMS_PER_PAGE } from '../constants';
  6. import { renderGroup, renderNumber, renderQuota, renderText } from '../helpers/render';
  7. function renderRole(role) {
  8. switch (role) {
  9. case 1:
  10. return <Label>普通用户</Label>;
  11. case 10:
  12. return <Label color='yellow'>管理员</Label>;
  13. case 100:
  14. return <Label color='orange'>超级管理员</Label>;
  15. default:
  16. return <Label color='red'>未知身份</Label>;
  17. }
  18. }
  19. const UsersTable = () => {
  20. const [users, setUsers] = useState([]);
  21. const [loading, setLoading] = useState(true);
  22. const [activePage, setActivePage] = useState(1);
  23. const [searchKeyword, setSearchKeyword] = useState('');
  24. const [searching, setSearching] = useState(false);
  25. const loadUsers = async (startIdx) => {
  26. const res = await API.get(`/api/user/?p=${startIdx}`);
  27. const { success, message, data } = res.data;
  28. if (success) {
  29. if (startIdx === 0) {
  30. setUsers(data);
  31. } else {
  32. let newUsers = users;
  33. newUsers.push(...data);
  34. setUsers(newUsers);
  35. }
  36. } else {
  37. showError(message);
  38. }
  39. setLoading(false);
  40. };
  41. const onPaginationChange = (e, { activePage }) => {
  42. (async () => {
  43. if (activePage === Math.ceil(users.length / ITEMS_PER_PAGE) + 1) {
  44. // In this case we have to load more data and then append them.
  45. await loadUsers(activePage - 1);
  46. }
  47. setActivePage(activePage);
  48. })();
  49. };
  50. useEffect(() => {
  51. loadUsers(0)
  52. .then()
  53. .catch((reason) => {
  54. showError(reason);
  55. });
  56. }, []);
  57. const manageUser = (username, action, idx) => {
  58. (async () => {
  59. const res = await API.post('/api/user/manage', {
  60. username,
  61. action
  62. });
  63. const { success, message } = res.data;
  64. if (success) {
  65. showSuccess('操作成功完成!');
  66. let user = res.data.data;
  67. let newUsers = [...users];
  68. let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
  69. if (action === 'delete') {
  70. newUsers[realIdx].deleted = true;
  71. } else {
  72. newUsers[realIdx].status = user.status;
  73. newUsers[realIdx].role = user.role;
  74. }
  75. setUsers(newUsers);
  76. } else {
  77. showError(message);
  78. }
  79. })();
  80. };
  81. const renderStatus = (status) => {
  82. switch (status) {
  83. case 1:
  84. return <Label basic>已激活</Label>;
  85. case 2:
  86. return (
  87. <Label basic color='red'>
  88. 已封禁
  89. </Label>
  90. );
  91. default:
  92. return (
  93. <Label basic color='grey'>
  94. 未知状态
  95. </Label>
  96. );
  97. }
  98. };
  99. const searchUsers = async () => {
  100. if (searchKeyword === '') {
  101. // if keyword is blank, load files instead.
  102. await loadUsers(0);
  103. setActivePage(1);
  104. return;
  105. }
  106. setSearching(true);
  107. const res = await API.get(`/api/user/search?keyword=${searchKeyword}`);
  108. const { success, message, data } = res.data;
  109. if (success) {
  110. setUsers(data);
  111. setActivePage(1);
  112. } else {
  113. showError(message);
  114. }
  115. setSearching(false);
  116. };
  117. const handleKeywordChange = async (e, { value }) => {
  118. setSearchKeyword(value.trim());
  119. };
  120. const sortUser = (key) => {
  121. if (users.length === 0) return;
  122. setLoading(true);
  123. let sortedUsers = [...users];
  124. sortedUsers.sort((a, b) => {
  125. return ('' + a[key]).localeCompare(b[key]);
  126. });
  127. if (sortedUsers[0].id === users[0].id) {
  128. sortedUsers.reverse();
  129. }
  130. setUsers(sortedUsers);
  131. setLoading(false);
  132. };
  133. return (
  134. <>
  135. <Form onSubmit={searchUsers}>
  136. <Form.Input
  137. icon='search'
  138. fluid
  139. iconPosition='left'
  140. placeholder='搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...'
  141. value={searchKeyword}
  142. loading={searching}
  143. onChange={handleKeywordChange}
  144. />
  145. </Form>
  146. <Table basic compact size='small'>
  147. <Table.Header>
  148. <Table.Row>
  149. <Table.HeaderCell
  150. style={{ cursor: 'pointer' }}
  151. onClick={() => {
  152. sortUser('id');
  153. }}
  154. >
  155. ID
  156. </Table.HeaderCell>
  157. <Table.HeaderCell
  158. style={{ cursor: 'pointer' }}
  159. onClick={() => {
  160. sortUser('username');
  161. }}
  162. >
  163. 用户名
  164. </Table.HeaderCell>
  165. <Table.HeaderCell
  166. style={{ cursor: 'pointer' }}
  167. onClick={() => {
  168. sortUser('group');
  169. }}
  170. >
  171. 分组
  172. </Table.HeaderCell>
  173. <Table.HeaderCell
  174. style={{ cursor: 'pointer' }}
  175. onClick={() => {
  176. sortUser('quota');
  177. }}
  178. >
  179. 统计信息
  180. </Table.HeaderCell>
  181. <Table.HeaderCell
  182. style={{ cursor: 'pointer' }}
  183. onClick={() => {
  184. sortUser('role');
  185. }}
  186. >
  187. 用户角色
  188. </Table.HeaderCell>
  189. <Table.HeaderCell
  190. style={{ cursor: 'pointer' }}
  191. onClick={() => {
  192. sortUser('status');
  193. }}
  194. >
  195. 状态
  196. </Table.HeaderCell>
  197. <Table.HeaderCell>操作</Table.HeaderCell>
  198. </Table.Row>
  199. </Table.Header>
  200. <Table.Body>
  201. {users
  202. .slice(
  203. (activePage - 1) * ITEMS_PER_PAGE,
  204. activePage * ITEMS_PER_PAGE
  205. )
  206. .map((user, idx) => {
  207. if (user.deleted) return <></>;
  208. return (
  209. <Table.Row key={user.id}>
  210. <Table.Cell>{user.id}</Table.Cell>
  211. <Table.Cell>
  212. <Popup
  213. content={user.email ? user.email : '未绑定邮箱地址'}
  214. key={user.username}
  215. header={user.display_name ? user.display_name : user.username}
  216. trigger={<span>{renderText(user.username, 15)}</span>}
  217. hoverable
  218. />
  219. </Table.Cell>
  220. <Table.Cell>{renderGroup(user.group)}</Table.Cell>
  221. {/*<Table.Cell>*/}
  222. {/* {user.email ? <Popup hoverable content={user.email} trigger={<span>{renderText(user.email, 24)}</span>} /> : '无'}*/}
  223. {/*</Table.Cell>*/}
  224. <Table.Cell>
  225. <Popup content='剩余额度' trigger={<Label basic>{renderQuota(user.quota)}</Label>} />
  226. <Popup content='已用额度' trigger={<Label basic>{renderQuota(user.used_quota)}</Label>} />
  227. <Popup content='请求次数' trigger={<Label basic>{renderNumber(user.request_count)}</Label>} />
  228. </Table.Cell>
  229. <Table.Cell>{renderRole(user.role)}</Table.Cell>
  230. <Table.Cell>{renderStatus(user.status)}</Table.Cell>
  231. <Table.Cell>
  232. <div>
  233. <Button
  234. size={'small'}
  235. positive
  236. onClick={() => {
  237. manageUser(user.username, 'promote', idx);
  238. }}
  239. disabled={user.role === 100}
  240. >
  241. 提升
  242. </Button>
  243. <Button
  244. size={'small'}
  245. color={'yellow'}
  246. onClick={() => {
  247. manageUser(user.username, 'demote', idx);
  248. }}
  249. disabled={user.role === 100}
  250. >
  251. 降级
  252. </Button>
  253. <Popup
  254. trigger={
  255. <Button size='small' negative disabled={user.role === 100}>
  256. 删除
  257. </Button>
  258. }
  259. on='click'
  260. flowing
  261. hoverable
  262. >
  263. <Button
  264. negative
  265. onClick={() => {
  266. manageUser(user.username, 'delete', idx);
  267. }}
  268. >
  269. 删除用户 {user.username}
  270. </Button>
  271. </Popup>
  272. <Button
  273. size={'small'}
  274. onClick={() => {
  275. manageUser(
  276. user.username,
  277. user.status === 1 ? 'disable' : 'enable',
  278. idx
  279. );
  280. }}
  281. disabled={user.role === 100}
  282. >
  283. {user.status === 1 ? '禁用' : '启用'}
  284. </Button>
  285. <Button
  286. size={'small'}
  287. as={Link}
  288. to={'/user/edit/' + user.id}
  289. >
  290. 编辑
  291. </Button>
  292. </div>
  293. </Table.Cell>
  294. </Table.Row>
  295. );
  296. })}
  297. </Table.Body>
  298. <Table.Footer>
  299. <Table.Row>
  300. <Table.HeaderCell colSpan='7'>
  301. <Button size='small' as={Link} to='/user/add' loading={loading}>
  302. 添加新的用户
  303. </Button>
  304. <Pagination
  305. floated='right'
  306. activePage={activePage}
  307. onPageChange={onPaginationChange}
  308. size='small'
  309. siblingRange={1}
  310. totalPages={
  311. Math.ceil(users.length / ITEMS_PER_PAGE) +
  312. (users.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
  313. }
  314. />
  315. </Table.HeaderCell>
  316. </Table.Row>
  317. </Table.Footer>
  318. </Table>
  319. </>
  320. );
  321. };
  322. export default UsersTable;