ChannelPickerTable.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. import { useEffect, useRef, useState } from "react";
  2. import { useIntl } from "react-intl";
  3. import { ActionType, ProList } from "@ant-design/pro-components";
  4. import { Button } from "antd";
  5. import { Badge, Dropdown, Space, Table, Typography } from "antd";
  6. import {
  7. GlobalOutlined,
  8. EditOutlined,
  9. MoreOutlined,
  10. CopyOutlined,
  11. } from "@ant-design/icons";
  12. import { IApiResponseChannelList, IFinal, TChannelType } from "../api/Channel";
  13. import { get, post } from "../../request";
  14. import { LockIcon } from "../../assets/icon";
  15. import StudioName, { IStudio } from "../auth/StudioName";
  16. import ProgressSvg from "./ProgressSvg";
  17. import { IChannel } from "./Channel";
  18. import { ArticleType } from "../article/Article";
  19. import CopyToModal from "./CopyToModal";
  20. import { useAppSelector } from "../../hooks";
  21. import { currentUser as _currentUser } from "../../reducers/current-user";
  22. import { sentenceList } from "../../reducers/sentence";
  23. const { Link } = Typography;
  24. interface IProgressRequest {
  25. sentence: string[];
  26. }
  27. export interface IItem {
  28. id: number;
  29. uid: string;
  30. title: string;
  31. summary: string;
  32. type: TChannelType;
  33. studio: IStudio;
  34. shareType: string;
  35. role?: string;
  36. publicity: number;
  37. createdAt: number;
  38. final?: IFinal[];
  39. progress: number;
  40. }
  41. interface IWidget {
  42. type?: ArticleType | "editable";
  43. articleId?: string;
  44. multiSelect?: boolean /*是否支持多选*/;
  45. selectedKeys?: string[];
  46. reload?: boolean;
  47. onSelect?: Function;
  48. }
  49. const Widget = ({
  50. type,
  51. articleId,
  52. multiSelect = true,
  53. selectedKeys = [],
  54. onSelect,
  55. reload = false,
  56. }: IWidget) => {
  57. const intl = useIntl();
  58. const [selectedRowKeys, setSelectedRowKeys] =
  59. useState<React.Key[]>(selectedKeys);
  60. const [showCheckBox, setShowCheckBox] = useState<boolean>(false);
  61. const user = useAppSelector(_currentUser);
  62. const ref = useRef<ActionType>();
  63. const sentences = useAppSelector(sentenceList);
  64. useEffect(() => {
  65. if (reload) {
  66. ref.current?.reload();
  67. }
  68. }, [reload]);
  69. return (
  70. <>
  71. <ProList<IItem>
  72. actionRef={ref}
  73. rowSelection={
  74. showCheckBox
  75. ? {
  76. // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
  77. // 注释该行则默认不显示下拉选项
  78. alwaysShowAlert: true,
  79. selectedRowKeys: selectedRowKeys,
  80. onChange: (selectedRowKeys: React.Key[]) => {
  81. setSelectedRowKeys(selectedRowKeys);
  82. },
  83. selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
  84. }
  85. : undefined
  86. }
  87. tableAlertRender={
  88. showCheckBox
  89. ? ({ selectedRowKeys, selectedRows, onCleanSelected }) => {
  90. console.log(selectedRowKeys);
  91. return (
  92. <Space>
  93. {intl.formatMessage({ id: "buttons.selected" })}
  94. <Badge color="geekblue" count={selectedRowKeys.length} />
  95. <Link onClick={onCleanSelected}>
  96. {intl.formatMessage({ id: "buttons.empty" })}
  97. </Link>
  98. </Space>
  99. );
  100. }
  101. : undefined
  102. }
  103. tableAlertOptionRender={
  104. showCheckBox
  105. ? ({ selectedRowKeys, selectedRows, onCleanSelected }) => {
  106. return (
  107. <Space>
  108. <Link
  109. onClick={() => {
  110. console.log("select", selectedRowKeys);
  111. if (typeof onSelect !== "undefined") {
  112. onSelect(
  113. selectedRows.map((item) => {
  114. return {
  115. id: item.uid,
  116. name: item.title,
  117. };
  118. })
  119. );
  120. setShowCheckBox(false);
  121. ref.current?.reload();
  122. }
  123. }}
  124. >
  125. {intl.formatMessage({
  126. id: "buttons.ok",
  127. })}
  128. </Link>
  129. <Link
  130. type="danger"
  131. onClick={() => {
  132. setShowCheckBox(false);
  133. }}
  134. >
  135. {intl.formatMessage({
  136. id: "buttons.cancel",
  137. })}
  138. </Link>
  139. </Space>
  140. );
  141. }
  142. : undefined
  143. }
  144. request={async (params = {}, sorter, filter) => {
  145. // TODO
  146. console.log(params, sorter, filter);
  147. let url: string = "";
  148. if (typeof articleId !== "undefined") {
  149. const id = articleId.split("_");
  150. const [book, para] = id[0].split("-");
  151. url = `/v2/channel-progress?view=user-in-chapter&book=${book}&para=${para}&progress=sent`;
  152. }
  153. const res = await post<IProgressRequest, IApiResponseChannelList>(
  154. url,
  155. {
  156. sentence: sentences,
  157. }
  158. );
  159. console.log("data", res.data.rows);
  160. const items: IItem[] = res.data.rows.map((item, id) => {
  161. const date = new Date(item.created_at);
  162. let all: number = 0;
  163. let finished: number = 0;
  164. item.final?.forEach((value) => {
  165. all += value[0];
  166. finished += value[1] ? value[0] : 0;
  167. });
  168. const progress = finished / all;
  169. return {
  170. id: id,
  171. uid: item.uid,
  172. title: item.name,
  173. summary: item.summary,
  174. studio: item.studio,
  175. shareType: "my",
  176. role: item.role,
  177. type: item.type,
  178. publicity: item.status,
  179. createdAt: date.getTime(),
  180. final: item.final,
  181. progress: progress,
  182. };
  183. });
  184. //当前被选择的
  185. const currChannel = items.filter((value) =>
  186. selectedRowKeys.includes(value.uid)
  187. );
  188. let show = selectedRowKeys;
  189. //有进度的
  190. const progressing = items.filter(
  191. (value) => value.progress > 0 && !show.includes(value.uid)
  192. );
  193. show = [...show, ...progressing.map((item) => item.uid)];
  194. //我自己的
  195. const myChannel = items.filter(
  196. (value) => value.role === "owner" && !show.includes(value.uid)
  197. );
  198. show = [...show, ...myChannel.map((item) => item.uid)];
  199. //其他的
  200. const others = items.filter(
  201. (value) => !show.includes(value.uid) && value.role !== "member"
  202. );
  203. console.log("user:", user);
  204. setSelectedRowKeys(selectedRowKeys);
  205. return {
  206. total: res.data.count,
  207. succcess: true,
  208. data: [...currChannel, ...progressing, ...myChannel, ...others],
  209. };
  210. }}
  211. rowKey="uid"
  212. bordered
  213. options={false}
  214. search={{
  215. filterType: "light",
  216. }}
  217. toolBarRender={() => [
  218. <Button
  219. onClick={() => {
  220. ref.current?.reload();
  221. }}
  222. >
  223. reload
  224. </Button>,
  225. multiSelect ? (
  226. <Button
  227. onClick={() => {
  228. setShowCheckBox(true);
  229. console.log("user:", user);
  230. }}
  231. >
  232. 选择
  233. </Button>
  234. ) : undefined,
  235. ]}
  236. metas={{
  237. title: {
  238. render(dom, entity, index, action, schema) {
  239. let pIcon = <></>;
  240. switch (entity.publicity) {
  241. case 10:
  242. pIcon = <LockIcon />;
  243. break;
  244. case 30:
  245. pIcon = <GlobalOutlined />;
  246. break;
  247. }
  248. return (
  249. <div
  250. key={index}
  251. style={{
  252. width: "100%",
  253. borderRadius: 5,
  254. padding: "0 5px",
  255. background:
  256. selectedKeys.includes(entity.uid) && !showCheckBox
  257. ? "linear-gradient(to right,#006112,rgba(0,0,0,0))"
  258. : undefined,
  259. }}
  260. >
  261. <div
  262. key="info"
  263. style={{ overflowX: "clip", display: "flex" }}
  264. >
  265. <Space>
  266. {pIcon}
  267. {entity.role !== "member" ? <EditOutlined /> : undefined}
  268. </Space>
  269. <Button
  270. type="link"
  271. onClick={() => {
  272. if (typeof onSelect !== "undefined") {
  273. const e: IChannel = {
  274. name: entity.title,
  275. id: entity.uid,
  276. };
  277. onSelect([e]);
  278. }
  279. }}
  280. >
  281. <Space>
  282. <StudioName data={entity.studio} showName={false} />
  283. {entity.title}
  284. </Space>
  285. </Button>
  286. </div>
  287. <div key="progress">
  288. <ProgressSvg data={entity.final} width={200} />
  289. </div>
  290. </div>
  291. );
  292. },
  293. search: false,
  294. },
  295. actions: {
  296. render: (dom, entity, index, action, schema) => {
  297. return (
  298. <Dropdown
  299. key={index}
  300. trigger={["click"]}
  301. menu={{
  302. items: [
  303. {
  304. key: "copy_to",
  305. label: (
  306. <CopyToModal
  307. trigger={intl.formatMessage({
  308. id: "buttons.copy.to",
  309. })}
  310. channel={{
  311. id: entity.uid,
  312. name: entity.title,
  313. type: entity.type,
  314. }}
  315. />
  316. ),
  317. icon: <CopyOutlined />,
  318. },
  319. ],
  320. onClick: (e) => {
  321. console.log("click ", e);
  322. switch (e.key) {
  323. case "copy_to":
  324. break;
  325. default:
  326. break;
  327. }
  328. },
  329. }}
  330. placement="bottomRight"
  331. >
  332. <Button
  333. type="link"
  334. size="small"
  335. icon={<MoreOutlined />}
  336. ></Button>
  337. </Dropdown>
  338. );
  339. },
  340. },
  341. status: {
  342. // 自己扩展的字段,主要用于筛选,不在列表中显示
  343. title: "版本筛选",
  344. valueType: "select",
  345. valueEnum: {
  346. all: { text: "全部", status: "Default" },
  347. my: {
  348. text: "我的",
  349. },
  350. closed: {
  351. text: "协作",
  352. },
  353. processing: {
  354. text: "社区公开",
  355. },
  356. },
  357. },
  358. }}
  359. />
  360. </>
  361. );
  362. };
  363. export default Widget;