ChannelPickerTable.tsx 11 KB

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