CourseMemberList.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. import { useIntl } from "react-intl";
  2. import { Space, Button, Dropdown, Table, Modal } from "antd";
  3. import { ActionType, ProTable } from "@ant-design/pro-components";
  4. import {
  5. DeleteOutlined,
  6. BarChartOutlined,
  7. ExclamationCircleFilled,
  8. } from "@ant-design/icons";
  9. import { delete_, get, put } from "../../request";
  10. import { ICourseMember } from "./CourseMember";
  11. import AddMember from "./AddMember";
  12. import { useRef, useState } from "react";
  13. import {
  14. ICourseMemberData,
  15. ICourseMemberDeleteResponse,
  16. ICourseMemberListResponse,
  17. ICourseMemberResponse,
  18. TCourseMemberStatus,
  19. } from "../api/Course";
  20. import { ItemType } from "antd/lib/menu/hooks/useItems";
  21. const { confirm } = Modal;
  22. interface IWidget {
  23. studioName?: string;
  24. courseId?: string;
  25. }
  26. const Widget = ({ studioName, courseId }: IWidget) => {
  27. const intl = useIntl(); //i18n
  28. const [canDelete, setCanDelete] = useState(false);
  29. const ref = useRef<ActionType>();
  30. const ChangeStatus = (
  31. id: string,
  32. name: string,
  33. status: TCourseMemberStatus
  34. ) => {
  35. confirm({
  36. title: (
  37. <div>
  38. <div>
  39. {intl.formatMessage({
  40. id: `course.member.status.${status}.message`,
  41. })}
  42. </div>
  43. <div>{name}</div>
  44. </div>
  45. ),
  46. icon: <ExclamationCircleFilled />,
  47. onOk() {
  48. return put<ICourseMemberData, ICourseMemberResponse>(
  49. "/v2/course-member/" + id,
  50. {
  51. course_id: "",
  52. user_id: "",
  53. status: status,
  54. }
  55. )
  56. .then((json) => {
  57. if (json.ok) {
  58. console.log("delete ok");
  59. ref.current?.reload();
  60. }
  61. })
  62. .catch(() => console.log("Oops errors!"));
  63. },
  64. });
  65. };
  66. return (
  67. <>
  68. <ProTable<ICourseMember>
  69. actionRef={ref}
  70. columns={[
  71. {
  72. title: intl.formatMessage({
  73. id: "dict.fields.sn.label",
  74. }),
  75. dataIndex: "sn",
  76. key: "sn",
  77. width: 50,
  78. search: false,
  79. },
  80. {
  81. title: intl.formatMessage({
  82. id: "forms.fields.name.label",
  83. }),
  84. dataIndex: "name",
  85. key: "name",
  86. },
  87. {
  88. title: intl.formatMessage({
  89. id: "forms.fields.role.label",
  90. }),
  91. dataIndex: "role",
  92. key: "role",
  93. width: 100,
  94. search: false,
  95. filters: true,
  96. onFilter: true,
  97. valueEnum: {
  98. all: {
  99. text: intl.formatMessage({
  100. id: "tables.publicity.all",
  101. }),
  102. status: "Default",
  103. },
  104. student: {
  105. text: intl.formatMessage({
  106. id: "auth.role.student",
  107. }),
  108. status: "Default",
  109. },
  110. assistant: {
  111. text: intl.formatMessage({
  112. id: "auth.role.assistant",
  113. }),
  114. status: "Success",
  115. },
  116. },
  117. },
  118. {
  119. title: intl.formatMessage({
  120. id: "forms.fields.status.label",
  121. }),
  122. dataIndex: "status",
  123. key: "status",
  124. width: 100,
  125. search: false,
  126. filters: true,
  127. onFilter: true,
  128. valueEnum: {
  129. all: {
  130. text: intl.formatMessage({
  131. id: "tables.publicity.all",
  132. }),
  133. status: "Default",
  134. },
  135. progressing: {
  136. text: intl.formatMessage({
  137. id: "course.member.status.progressing.label",
  138. }),
  139. status: "Processing",
  140. },
  141. accepted: {
  142. text: intl.formatMessage({
  143. id: "course.member.status.accepted.label",
  144. }),
  145. status: "success",
  146. },
  147. rejected: {
  148. text: intl.formatMessage({
  149. id: "course.member.status.rejected.label",
  150. }),
  151. status: "warning",
  152. },
  153. left: {
  154. text: intl.formatMessage({
  155. id: "course.member.status.left.label",
  156. }),
  157. status: "warning",
  158. },
  159. blocked: {
  160. text: intl.formatMessage({
  161. id: "course.member.status.blocked.label",
  162. }),
  163. status: "warning",
  164. },
  165. },
  166. },
  167. {
  168. title: intl.formatMessage({
  169. id: "course.exp.start.label",
  170. }),
  171. dataIndex: "startExp",
  172. key: "startExp",
  173. },
  174. {
  175. title: intl.formatMessage({
  176. id: "course.exp.current.label",
  177. }),
  178. dataIndex: "currentExp",
  179. key: "currentExp",
  180. },
  181. {
  182. title: intl.formatMessage({
  183. id: "course.exp.end.label",
  184. }),
  185. dataIndex: "endExp",
  186. key: "endExp",
  187. },
  188. {
  189. title: intl.formatMessage({ id: "buttons.option" }),
  190. key: "option",
  191. width: 120,
  192. valueType: "option",
  193. render: (text, row, index, action) => {
  194. let items: ItemType[] = [];
  195. switch (row.status) {
  196. case "accepted":
  197. items = [
  198. {
  199. key: "exp",
  200. label: "经验值",
  201. icon: <BarChartOutlined />,
  202. },
  203. {
  204. key: "delete",
  205. label: "删除",
  206. icon: <DeleteOutlined />,
  207. },
  208. ];
  209. break;
  210. case "progressing":
  211. items = [
  212. {
  213. key: "accept",
  214. label: "接受",
  215. icon: <BarChartOutlined />,
  216. },
  217. {
  218. key: "reject",
  219. label: "拒绝",
  220. icon: <DeleteOutlined />,
  221. },
  222. ];
  223. break;
  224. default:
  225. break;
  226. }
  227. return [
  228. canDelete ? (
  229. <Dropdown.Button
  230. key={index}
  231. type="link"
  232. menu={{
  233. items,
  234. onClick: (e) => {
  235. console.log("click", e);
  236. switch (e.key) {
  237. case "exp":
  238. break;
  239. case "delete":
  240. confirm({
  241. title: `删除此成员吗?`,
  242. icon: <ExclamationCircleFilled />,
  243. content: "此操作不能恢复",
  244. okType: "danger",
  245. onOk() {
  246. return delete_<ICourseMemberDeleteResponse>(
  247. "/v2/course-member/" + row.id
  248. )
  249. .then((json) => {
  250. if (json.ok) {
  251. console.log("delete ok");
  252. ref.current?.reload();
  253. }
  254. })
  255. .catch(() => console.log("Oops errors!"));
  256. },
  257. });
  258. break;
  259. case "accept":
  260. if (row.id && row.name) {
  261. ChangeStatus(row.id, row.name, "accepted");
  262. }
  263. break;
  264. case "reject":
  265. if (row.id && row.name) {
  266. ChangeStatus(row.id, row.name, "rejected");
  267. }
  268. break;
  269. default:
  270. break;
  271. }
  272. },
  273. }}
  274. >
  275. <></>
  276. </Dropdown.Button>
  277. ) : (
  278. <></>
  279. ),
  280. ];
  281. },
  282. },
  283. ]}
  284. rowSelection={{
  285. // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
  286. // 注释该行则默认不显示下拉选项
  287. selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
  288. }}
  289. tableAlertRender={({
  290. selectedRowKeys,
  291. selectedRows,
  292. onCleanSelected,
  293. }) => (
  294. <Space size={24}>
  295. <span>
  296. {intl.formatMessage({ id: "buttons.selected" })}
  297. {selectedRowKeys.length}
  298. <Button
  299. type="link"
  300. style={{ marginInlineStart: 8 }}
  301. onClick={onCleanSelected}
  302. >
  303. {intl.formatMessage({
  304. id: "buttons.unselect",
  305. })}
  306. </Button>
  307. </span>
  308. </Space>
  309. )}
  310. tableAlertOptionRender={() => {
  311. return (
  312. <Space size={16}>
  313. <Button type="link">
  314. {intl.formatMessage({
  315. id: "buttons.delete.all",
  316. })}
  317. </Button>
  318. </Space>
  319. );
  320. }}
  321. request={async (params = {}, sorter, filter) => {
  322. console.log(params, sorter, filter);
  323. let url = `/v2/course-member?view=course&id=${courseId}`;
  324. const offset =
  325. ((params.current ? params.current : 1) - 1) *
  326. (params.pageSize ? params.pageSize : 20);
  327. url += `&limit=${params.pageSize}&offset=${offset}`;
  328. if (typeof params.keyword !== "undefined") {
  329. url += "&search=" + (params.keyword ? params.keyword : "");
  330. }
  331. const res = await get<ICourseMemberListResponse>(url);
  332. if (res.ok) {
  333. console.log(res.data);
  334. switch (res.data.role) {
  335. case "owner":
  336. case "manager":
  337. case "assistant":
  338. setCanDelete(true);
  339. break;
  340. }
  341. const items: ICourseMember[] = res.data.rows.map((item, id) => {
  342. let member: ICourseMember = {
  343. sn: id + 1,
  344. id: item.id,
  345. userId: item.user_id,
  346. name: item.user?.nickName,
  347. role: item.role,
  348. status: item.status,
  349. tag: [],
  350. image: "",
  351. };
  352. member.tag.push({
  353. title: intl.formatMessage({
  354. id: "forms.fields." + item.role + ".label",
  355. }),
  356. color: "default",
  357. });
  358. return member;
  359. });
  360. console.log(items);
  361. return {
  362. total: res.data.count,
  363. succcess: true,
  364. data: items,
  365. };
  366. } else {
  367. console.error(res.message);
  368. return {
  369. total: 0,
  370. succcess: false,
  371. data: [],
  372. };
  373. }
  374. }}
  375. rowKey="id"
  376. bordered
  377. pagination={{
  378. showQuickJumper: true,
  379. showSizeChanger: true,
  380. }}
  381. search={false}
  382. options={{
  383. search: true,
  384. }}
  385. toolBarRender={() => [
  386. <AddMember
  387. courseId={courseId}
  388. onCreated={() => {
  389. ref.current?.reload();
  390. }}
  391. />,
  392. ]}
  393. />
  394. </>
  395. );
  396. };
  397. export default Widget;