ChannelSentDiff.tsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import { Button, message, Select, Table, Tooltip, Typography } from "antd";
  2. import { Change, diffChars } from "diff";
  3. import { useEffect, useState } from "react";
  4. import { post } from "../../request";
  5. import {
  6. ISentenceDiffData,
  7. ISentenceDiffRequest,
  8. ISentenceDiffResponse,
  9. ISentenceNewMultiResponse,
  10. ISentenceNewRequest,
  11. } from "../api/Corpus";
  12. import { IChannel } from "./Channel";
  13. const { Text } = Typography;
  14. interface IDataType {
  15. key: React.Key;
  16. sentId: string;
  17. pali?: string;
  18. srcContent?: string;
  19. destContent?: string;
  20. }
  21. interface IWidget {
  22. srcChannel?: IChannel;
  23. destChannel?: IChannel;
  24. sentences?: string[];
  25. goPrev?: Function;
  26. onSubmit?: Function;
  27. }
  28. const ChannelSentDiffWidget = ({
  29. srcChannel,
  30. destChannel,
  31. sentences,
  32. goPrev,
  33. onSubmit,
  34. }: IWidget) => {
  35. const [srcApiData, setSrcApiData] = useState<ISentenceDiffData[]>([]);
  36. const [diffData, setDiffData] = useState<IDataType[]>();
  37. const [loading, setLoading] = useState(false);
  38. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>();
  39. const [newRowKeys, setNewRowKeys] = useState<React.Key[]>();
  40. const [emptyRowKeys, setEmptyRowKeys] = useState<React.Key[]>();
  41. useEffect(() => {
  42. if (sentences && srcChannel && destChannel) {
  43. post<ISentenceDiffRequest, ISentenceDiffResponse>(`/v2/sent-in-channel`, {
  44. sentences: sentences,
  45. channels: ["_System_Pali_VRI_", srcChannel.id, destChannel.id],
  46. }).then((json) => {
  47. if (json.ok) {
  48. const apiData = json.data.rows;
  49. setSrcApiData(apiData);
  50. let newRows: string[] = [];
  51. let emptyRows: string[] = [];
  52. const diffList: IDataType[] = sentences?.map((item, index) => {
  53. const id: string[] = item.split("-");
  54. const srcContent = apiData.find(
  55. (element) =>
  56. element.book_id === parseInt(id[0]) &&
  57. element.paragraph === parseInt(id[1]) &&
  58. element.word_start === parseInt(id[2]) &&
  59. element.word_end === parseInt(id[3]) &&
  60. element.channel_uid === srcChannel.id
  61. );
  62. const destContent = apiData.find(
  63. (element) =>
  64. element.book_id === parseInt(id[0]) &&
  65. element.paragraph === parseInt(id[1]) &&
  66. element.word_start === parseInt(id[2]) &&
  67. element.word_end === parseInt(id[3]) &&
  68. element.channel_uid === destChannel.id
  69. );
  70. if (srcContent && destContent) {
  71. const srcDate = new Date(srcContent.updated_at);
  72. const destDate = new Date(destContent.updated_at);
  73. if (srcDate > destDate) {
  74. newRows.push(item);
  75. }
  76. }
  77. if (
  78. typeof destContent === "undefined" ||
  79. destContent.content.trim().length === 0
  80. ) {
  81. emptyRows.push(item);
  82. }
  83. const paliContent = apiData.find(
  84. (element) =>
  85. element.book_id === parseInt(id[0]) &&
  86. element.paragraph === parseInt(id[1]) &&
  87. element.word_start === parseInt(id[2]) &&
  88. element.word_end === parseInt(id[3]) &&
  89. element.channel_uid !== destChannel.id &&
  90. element.channel_uid !== srcChannel.id
  91. );
  92. return {
  93. key: item,
  94. sentId: item,
  95. pali: paliContent?.content,
  96. srcContent: srcContent?.content,
  97. destContent: destContent?.content,
  98. };
  99. });
  100. setDiffData(diffList);
  101. setNewRowKeys(newRows);
  102. setSelectedRowKeys(newRows);
  103. setEmptyRowKeys(emptyRows);
  104. }
  105. });
  106. }
  107. }, [srcChannel, sentences, destChannel]);
  108. return (
  109. <div>
  110. <div style={{ display: "flex", justifyContent: "space-between" }}>
  111. <Button
  112. onClick={() => {
  113. if (typeof goPrev !== "undefined") {
  114. goPrev();
  115. }
  116. }}
  117. >
  118. 上一步
  119. </Button>
  120. <Select
  121. defaultValue={"new"}
  122. style={{ width: 180 }}
  123. onChange={(value: string) => {
  124. switch (value) {
  125. case "new":
  126. setSelectedRowKeys(newRowKeys);
  127. break;
  128. case "all":
  129. setSelectedRowKeys(sentences);
  130. break;
  131. case "empty":
  132. setSelectedRowKeys(emptyRowKeys);
  133. break;
  134. default:
  135. break;
  136. }
  137. }}
  138. options={[
  139. { value: "new", label: "仅复制较新的" },
  140. { value: "empty", label: "仅复制缺失的" },
  141. { value: "all", label: "全部复制" },
  142. ]}
  143. />
  144. <Button
  145. type="primary"
  146. loading={loading}
  147. onClick={() => {
  148. if (typeof srcChannel === "undefined") {
  149. return;
  150. }
  151. if (
  152. typeof selectedRowKeys === "undefined" ||
  153. selectedRowKeys.length === 0
  154. ) {
  155. message.warning("没有被选择的句子");
  156. return;
  157. }
  158. setLoading(true);
  159. let submitData: ISentenceDiffData[] = [];
  160. selectedRowKeys?.forEach((value) => {
  161. const id: string[] = value.toString().split("-");
  162. const srcContent = srcApiData.find(
  163. (element) =>
  164. element.book_id === parseInt(id[0]) &&
  165. element.paragraph === parseInt(id[1]) &&
  166. element.word_start === parseInt(id[2]) &&
  167. element.word_end === parseInt(id[3]) &&
  168. element.channel_uid === srcChannel.id
  169. );
  170. if (srcContent) {
  171. submitData.push(srcContent);
  172. }
  173. });
  174. if (typeof submitData === "undefined") {
  175. return;
  176. }
  177. post<ISentenceNewRequest, ISentenceNewMultiResponse>(
  178. `/v2/sentence`,
  179. {
  180. sentences: submitData,
  181. channel: destChannel?.id,
  182. }
  183. )
  184. .then((json) => {
  185. if (json.ok) {
  186. if (typeof onSubmit !== "undefined") {
  187. onSubmit();
  188. }
  189. } else {
  190. message.error(json.message);
  191. }
  192. })
  193. .catch((e) => {
  194. console.log(e);
  195. })
  196. .finally(() => {
  197. setLoading(false);
  198. });
  199. }}
  200. >
  201. 开始复制
  202. </Button>
  203. </div>
  204. <div style={{ height: 400, overflowY: "scroll" }}>
  205. <Table
  206. pagination={false}
  207. rowSelection={{
  208. type: "checkbox",
  209. selectedRowKeys: selectedRowKeys,
  210. onChange: (
  211. selectedRowKeys: React.Key[],
  212. selectedRows: IDataType[]
  213. ) => {
  214. console.log(
  215. `selectedRowKeys: ${selectedRowKeys}`,
  216. "selectedRows: ",
  217. selectedRows
  218. );
  219. setSelectedRowKeys(selectedRowKeys);
  220. },
  221. getCheckboxProps: (record: IDataType) => ({
  222. name: record.pali,
  223. }),
  224. }}
  225. columns={[
  226. {
  227. title: "pali",
  228. width: "33%",
  229. dataIndex: "pali",
  230. },
  231. {
  232. title: (
  233. <>
  234. {`原文-`}
  235. <Text strong>{srcChannel?.name}</Text>
  236. </>
  237. ),
  238. width: "33%",
  239. dataIndex: "srcContent",
  240. },
  241. {
  242. title: (
  243. <>
  244. {`复制到-`}
  245. <Text strong>{destChannel?.name}</Text>
  246. </>
  247. ),
  248. width: "33%",
  249. dataIndex: "destContent",
  250. render: (value, record, index) => {
  251. const diff: Change[] = diffChars(
  252. record.destContent ? record.destContent : "",
  253. record.srcContent ? record.srcContent : ""
  254. );
  255. const diffResult = diff.map((item, id) => {
  256. return (
  257. <Text
  258. key={id}
  259. type={
  260. item.added
  261. ? "success"
  262. : item.removed
  263. ? "danger"
  264. : "secondary"
  265. }
  266. delete={item.removed ? true : undefined}
  267. >
  268. {item.value}
  269. </Text>
  270. );
  271. });
  272. return (
  273. <Tooltip title={record.destContent}>{diffResult}</Tooltip>
  274. );
  275. },
  276. },
  277. ]}
  278. dataSource={diffData}
  279. />
  280. </div>
  281. </div>
  282. );
  283. };
  284. export default ChannelSentDiffWidget;