WbwSent.tsx 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. import { Button, Dropdown, message, Progress, Space, Tree } from "antd";
  2. import { useEffect, useState } from "react";
  3. import { MoreOutlined, ExclamationCircleOutlined } from "@ant-design/icons";
  4. import { useAppSelector } from "../../hooks";
  5. import { mode as _mode } from "../../reducers/article-mode";
  6. import { delete_, get, post } from "../../request";
  7. import { ArticleMode } from "../article/Article";
  8. import WbwWord, {
  9. IWbw,
  10. IWbwFields,
  11. TWbwDisplayMode,
  12. WbwElement,
  13. WbwStatus,
  14. } from "./Wbw/WbwWord";
  15. import { TChannelType } from "../api/Channel";
  16. import { IDictRequest } from "../api/Dict";
  17. import { useIntl } from "react-intl";
  18. import { add } from "../../reducers/sent-word";
  19. import store from "../../store";
  20. import { settingInfo } from "../../reducers/setting";
  21. import { GetUserSetting } from "../auth/setting/default";
  22. import { getGrammar } from "../../reducers/term-vocabulary";
  23. import modal from "antd/lib/modal";
  24. import { UserWbwPost } from "../dict/MyCreate";
  25. import { currentUser } from "../../reducers/current-user";
  26. import Studio, { IStudio } from "../auth/Studio";
  27. import { IChannel } from "../channel/Channel";
  28. import TimeShow from "../general/TimeShow";
  29. import moment from "moment";
  30. import { courseInfo } from "../../reducers/current-course";
  31. import { ISentenceWbwListResponse } from "../api/Corpus";
  32. import { IDeleteResponse } from "../api/Article";
  33. export const getWbwProgress = (data: IWbw[], answer?: IWbw[]) => {
  34. //计算完成度
  35. //祛除标点符号
  36. const allWord = data.filter(
  37. (value) =>
  38. value.real.value &&
  39. value.real.value?.length > 0 &&
  40. value.type?.value !== ".ctl."
  41. );
  42. let final: IWbw[];
  43. if (answer) {
  44. final = allWord.filter((value: IWbw, index: number, array: IWbw[]) => {
  45. const sn = value.sn.join();
  46. const currAnswer = answer.find((value1) => value1.sn.join() === sn);
  47. if (currAnswer) {
  48. if (currAnswer.meaning?.value) {
  49. if (!value.meaning?.value) {
  50. return false;
  51. }
  52. if (value.meaning?.value?.trim().length === 0) {
  53. return false;
  54. }
  55. }
  56. if (currAnswer.factors?.value) {
  57. if (!value.factors?.value) {
  58. return false;
  59. }
  60. if (value.factors?.value?.trim().length === 0) {
  61. return false;
  62. }
  63. }
  64. if (currAnswer.factorMeaning?.value) {
  65. if (!value.factorMeaning?.value) {
  66. return false;
  67. }
  68. if (value.factorMeaning?.value?.trim().length === 0) {
  69. return false;
  70. }
  71. }
  72. if (currAnswer.case?.value) {
  73. if (!value.case?.value) {
  74. return false;
  75. }
  76. if (value.case?.value?.trim().length === 0) {
  77. return false;
  78. }
  79. }
  80. if (currAnswer.parent?.value) {
  81. if (!value.parent?.value) {
  82. return false;
  83. }
  84. if (value.parent?.value?.trim().length === 0) {
  85. return false;
  86. }
  87. }
  88. return true;
  89. }
  90. });
  91. } else {
  92. final = allWord.filter(
  93. (value) =>
  94. value.meaning?.value &&
  95. value.factors?.value &&
  96. value.factorMeaning?.value &&
  97. value.case?.value
  98. );
  99. }
  100. console.debug("wbw progress", allWord, final);
  101. let finalLen: number = 0;
  102. final.forEach((value) => {
  103. if (value.real.value) {
  104. finalLen += value.real.value?.length;
  105. }
  106. });
  107. let allLen: number = 0;
  108. allWord.forEach((value) => {
  109. if (value.real.value) {
  110. allLen += value.real.value?.length;
  111. }
  112. });
  113. const progress = Math.round((finalLen * 100) / allLen);
  114. return progress;
  115. };
  116. export const paraMark = (wbwData: IWbw[]): IWbw[] => {
  117. //处理段落标记,支持点击段落引用弹窗
  118. let start = false;
  119. let bookCode = "";
  120. let count = 0;
  121. let bookCodeStack: string[] = [];
  122. wbwData.forEach((value: IWbw, index: number, array: IWbw[]) => {
  123. if (value.word.value === "(") {
  124. start = true;
  125. bookCode = "";
  126. bookCodeStack = [];
  127. return;
  128. }
  129. if (start) {
  130. if (!isNaN(Number(value.word.value.replaceAll("-", "")))) {
  131. console.debug("para mark", "number", value.word.value);
  132. if (bookCode === "" && bookCodeStack.length > 0) {
  133. //继承之前的
  134. let bookCodeList = bookCodeStack;
  135. bookCode = bookCodeList[0];
  136. }
  137. const dot = bookCode.lastIndexOf(".");
  138. let bookName = "";
  139. let paraNum = "";
  140. if (dot === -1) {
  141. bookName = bookCode;
  142. paraNum = value.word.value;
  143. } else {
  144. bookName = bookCode.substring(0, dot + 1);
  145. paraNum = bookCode.substring(dot + 1) + value.word.value;
  146. }
  147. bookName = bookName.substring(0, 64).toLowerCase();
  148. if (!bookCodeStack.includes(bookName)) {
  149. bookCodeStack.push(bookName);
  150. }
  151. if (bookName !== "") {
  152. array[index].bookName = bookName;
  153. count++;
  154. }
  155. } else if (value.word.value === ";") {
  156. bookCode = "";
  157. return;
  158. } else if (value.word.value === ")") {
  159. start = false;
  160. return;
  161. }
  162. bookCode += value.word.value;
  163. }
  164. });
  165. if (count > 0) {
  166. console.debug("para mark", count, wbwData);
  167. }
  168. return wbwData;
  169. };
  170. interface IMagicDictRequest {
  171. book: number;
  172. para: number;
  173. word_start: number;
  174. word_end: number;
  175. data: IWbw[];
  176. channel_id: string;
  177. lang?: string[];
  178. }
  179. interface IMagicDictResponse {
  180. ok: boolean;
  181. message: string;
  182. data: IWbw[];
  183. }
  184. interface IWbwXml {
  185. id: string;
  186. pali: WbwElement<string>;
  187. real?: WbwElement<string | null>;
  188. type?: WbwElement<string | null>;
  189. gramma?: WbwElement<string | null>;
  190. mean?: WbwElement<string | null>;
  191. org?: WbwElement<string | null>;
  192. om?: WbwElement<string | null>;
  193. case?: WbwElement<string | null>;
  194. parent?: WbwElement<string | null>;
  195. pg?: WbwElement<string | null>;
  196. parent2?: WbwElement<string | null>;
  197. rela?: WbwElement<string | null>;
  198. lock?: boolean;
  199. bmt?: WbwElement<string | null>;
  200. bmc?: WbwElement<number | null>;
  201. cf: number;
  202. }
  203. interface IWbwUpdateResponse {
  204. ok: boolean;
  205. message: string;
  206. data: { rows: IWbw[]; count: number };
  207. }
  208. interface IWbwWord {
  209. words: IWbwXml[];
  210. sn: number;
  211. }
  212. interface IWbwRequest {
  213. book: number;
  214. para: number;
  215. sn: number;
  216. channel_id: string;
  217. data: IWbwWord[];
  218. }
  219. interface IWidget {
  220. data: IWbw[];
  221. answer?: IWbw[];
  222. book: number;
  223. para: number;
  224. wordStart: number;
  225. wordEnd: number;
  226. channel?: IChannel;
  227. channelId: string;
  228. channelType?: TChannelType;
  229. channelLang?: string;
  230. display?: TWbwDisplayMode;
  231. fields?: IWbwFields;
  232. layoutDirection?: "h" | "v";
  233. refreshable?: boolean;
  234. mode?: ArticleMode;
  235. wbwProgress?: boolean;
  236. studio?: IStudio;
  237. readonly?: boolean;
  238. onMagicDictDone?: Function;
  239. onChange?: Function;
  240. }
  241. export const WbwSentCtl = ({
  242. data,
  243. answer,
  244. channel,
  245. channelId,
  246. channelType,
  247. channelLang,
  248. book,
  249. para,
  250. wordStart,
  251. wordEnd,
  252. display = "block",
  253. fields,
  254. layoutDirection = "h",
  255. mode,
  256. refreshable = false,
  257. wbwProgress = false,
  258. readonly = false,
  259. studio,
  260. onChange,
  261. onMagicDictDone,
  262. }: IWidget) => {
  263. const intl = useIntl();
  264. const [wordData, setWordData] = useState<IWbw[]>(paraMark(data));
  265. const [wbwMode, setWbwMode] = useState(display);
  266. const [fieldDisplay, setFieldDisplay] = useState(fields);
  267. const [displayMode, setDisplayMode] = useState<ArticleMode>();
  268. const [loading, setLoading] = useState(false);
  269. const [showProgress, setShowProgress] = useState(false);
  270. const [check, setCheck] = useState(answer ? true : false);
  271. const [courseAnswer, setCourseAnswer] = useState<IWbw[]>();
  272. const user = useAppSelector(currentUser);
  273. const course = useAppSelector(courseInfo);
  274. console.debug("wbw sent lang", channelLang);
  275. const loadAnswer = () => {
  276. if (courseAnswer) {
  277. return;
  278. }
  279. let url = `/v2/wbw-sentence?view=course-answer`;
  280. url += `&book=${book}&para=${para}&wordStart=${wordStart}&wordEnd=${wordEnd}`;
  281. if (course) {
  282. url += `&course=${course.courseId}`;
  283. setLoading(true);
  284. console.info("wbw sentence api request", url);
  285. get<ISentenceWbwListResponse>(url)
  286. .then((json) => {
  287. console.info("wbw sentence api response", json);
  288. if (json.ok) {
  289. console.debug("wbw sentence course", course);
  290. if (json.data.rows.length > 0 && json.data.rows[0].origin) {
  291. let response = json.data.rows[0].origin[0];
  292. setCourseAnswer(
  293. response ? JSON.parse(response.content ?? "") : undefined
  294. );
  295. }
  296. }
  297. })
  298. .finally(() => setLoading(false));
  299. } else {
  300. console.debug("并非课程");
  301. }
  302. };
  303. useEffect(() => setShowProgress(wbwProgress), [wbwProgress]);
  304. const settings = useAppSelector(settingInfo);
  305. const sysGrammar = useAppSelector(getGrammar)?.filter(
  306. (value) => value.tag === ":collocation:"
  307. );
  308. //计算完成度
  309. const progress = getWbwProgress(wordData, answer);
  310. const newMode = useAppSelector(_mode);
  311. const update = (data: IWbw[]) => {
  312. console.debug("wbw update");
  313. setWordData(paraMark(data));
  314. if (typeof onChange !== "undefined") {
  315. onChange(data);
  316. }
  317. };
  318. useEffect(() => {
  319. if (refreshable) {
  320. setWordData(paraMark(data));
  321. }
  322. }, [data, refreshable]);
  323. useEffect(() => {
  324. //发布句子里面的单词的变更。给术语输入菜单用。
  325. let words = new Map<string, number>();
  326. wordData
  327. .filter(
  328. (value) =>
  329. value.type?.value !== null &&
  330. value.type?.value !== ".ctl." &&
  331. value.real.value &&
  332. value.real.value.length > 0
  333. )
  334. .forEach((value) => {
  335. words.set(value.real.value ? value.real.value : "", 1);
  336. if (value.parent?.value) {
  337. words.set(value.parent.value, 1);
  338. }
  339. });
  340. let pubWords: string[] = [];
  341. words.forEach((value, key) => pubWords.push(key));
  342. const sentId = `${book}-${para}-${wordStart}-${wordEnd}`;
  343. store.dispatch(add({ sentId: sentId, words: pubWords }));
  344. }, [book, para, wordData, wordEnd, wordStart]);
  345. useEffect(() => {
  346. console.log("mode", mode);
  347. let currMode: ArticleMode | undefined;
  348. if (typeof mode !== "undefined") {
  349. currMode = mode;
  350. } else if (typeof newMode !== "undefined") {
  351. if (typeof newMode.id === "undefined") {
  352. currMode = newMode.mode;
  353. } else {
  354. const sentId = newMode.id.split("-");
  355. if (sentId.length === 2) {
  356. if (book === parseInt(sentId[0]) && para === parseInt(sentId[1])) {
  357. currMode = newMode.mode;
  358. }
  359. }
  360. }
  361. } else {
  362. currMode = undefined;
  363. }
  364. setDisplayMode(currMode);
  365. switch (currMode) {
  366. case "edit":
  367. if (typeof display === "undefined") {
  368. setWbwMode("block");
  369. }
  370. if (typeof fields === "undefined") {
  371. setFieldDisplay({
  372. meaning: true,
  373. factors: false,
  374. factorMeaning: false,
  375. case: false,
  376. });
  377. }
  378. break;
  379. case "wbw":
  380. if (typeof display === "undefined") {
  381. setWbwMode("block");
  382. }
  383. if (typeof fields === "undefined") {
  384. setFieldDisplay({
  385. meaning: true,
  386. factors: true,
  387. factorMeaning: true,
  388. case: true,
  389. });
  390. }
  391. break;
  392. }
  393. }, [newMode, mode, book, para, display, fields]);
  394. const magicDictLookup = () => {
  395. let _lang = GetUserSetting("setting.dict.lang", settings);
  396. const url = `/v2/wbwlookup`;
  397. console.log("magic dict url", url);
  398. if (typeof _lang === "object") {
  399. }
  400. post<IMagicDictRequest, IMagicDictResponse>(url, {
  401. book: book,
  402. para: para,
  403. word_start: wordStart,
  404. word_end: wordEnd,
  405. data: wordData,
  406. channel_id: channelId,
  407. lang: _lang?.toString().split(","),
  408. })
  409. .then((json) => {
  410. if (json.ok) {
  411. console.log("magic dict result", json.data);
  412. update(json.data);
  413. if (channelType !== "nissaya") {
  414. saveWbwAll(json.data);
  415. }
  416. } else {
  417. console.error(json.message);
  418. }
  419. })
  420. .finally(() => {
  421. setLoading(false);
  422. if (typeof onMagicDictDone !== "undefined") {
  423. onMagicDictDone();
  424. }
  425. });
  426. };
  427. const wbwToXml = (item: IWbw) => {
  428. return {
  429. pali: item.word,
  430. real: item.real,
  431. id: `${book}-${para}-` + item.sn.join("-"),
  432. type: item.type,
  433. gramma: item.grammar,
  434. mean: item.meaning
  435. ? {
  436. value: item.meaning.value,
  437. status: item.meaning?.status,
  438. }
  439. : undefined,
  440. org: item.factors,
  441. om: item.factorMeaning,
  442. case: item.case,
  443. parent: item.parent,
  444. pg: item.grammar2,
  445. parent2: item.parent2,
  446. rela: item.relation,
  447. lock: item.locked,
  448. note: item.note,
  449. bmt: item.bookMarkText,
  450. bmc: item.bookMarkColor,
  451. attachments: JSON.stringify(item.attachments),
  452. cf: item.confidence,
  453. };
  454. };
  455. const saveWbwAll = (wbwData: IWbw[]) => {
  456. let arrSn: number[] = [];
  457. wbwData.forEach((value) => {
  458. if (!arrSn.includes(value.sn[0])) {
  459. arrSn.push(value.sn[0]);
  460. }
  461. });
  462. const postParam: IWbwRequest = {
  463. book: book,
  464. para: para,
  465. channel_id: channelId,
  466. sn: wbwData[0].sn[0],
  467. data: arrSn.map((item) => {
  468. return {
  469. sn: item,
  470. words: wbwData.filter((value) => value.sn[0] === item).map(wbwToXml),
  471. };
  472. }),
  473. };
  474. postWord(postParam);
  475. };
  476. const postWord = (postParam: IWbwRequest) => {
  477. const url = `/v2/wbw`;
  478. console.info("wbw api request", url, postParam);
  479. post<IWbwRequest, IWbwUpdateResponse>(url, postParam).then((json) => {
  480. console.info("wbw api response", json);
  481. if (json.ok) {
  482. message.info(json.data.count + " updated");
  483. setWordData(paraMark(json.data.rows));
  484. } else {
  485. message.error(json.message);
  486. }
  487. });
  488. };
  489. const saveWord = (wbwData: IWbw[], sn: number) => {
  490. if (channelType === "nissaya") {
  491. } else {
  492. const data = wbwData.filter((value) => value.sn[0] === sn);
  493. const postParam: IWbwRequest = {
  494. book: book,
  495. para: para,
  496. channel_id: channelId,
  497. sn: sn,
  498. data: [
  499. {
  500. sn: sn,
  501. words: data.map(wbwToXml),
  502. },
  503. ],
  504. };
  505. postWord(postParam);
  506. }
  507. };
  508. const wordSplit = (id: number, hyphen = "-") => {
  509. let factors = wordData[id]?.factors?.value;
  510. if (typeof factors === "string") {
  511. let sFm = wordData[id]?.factorMeaning?.value;
  512. if (typeof sFm === "undefined" || sFm === null) {
  513. sFm = new Array(factors.split("+")).fill([""]).join("+");
  514. }
  515. if (wordData[id].case?.value?.split("#")[0] === ".un.") {
  516. factors = `[+${factors}+]`;
  517. sFm = `+${sFm}+`;
  518. } else if (hyphen !== "") {
  519. factors = factors.replaceAll("+", `+${hyphen}+`);
  520. sFm = sFm.replaceAll("+", `+${hyphen}+`);
  521. }
  522. let fm = sFm.split("+");
  523. const children: IWbw[] | undefined = factors
  524. .split("+")
  525. .map((item, index) => {
  526. return {
  527. word: { value: item, status: 5 },
  528. real: {
  529. value: item
  530. .replaceAll("-", "")
  531. .replaceAll("[", "")
  532. .replaceAll("]", ""),
  533. status: 5,
  534. },
  535. meaning: { value: fm[index], status: 5 },
  536. book: wordData[id].book,
  537. para: wordData[id].para,
  538. sn: [...wordData[id].sn, index],
  539. confidence: 1,
  540. };
  541. });
  542. if (typeof children !== "undefined") {
  543. console.log("children", children);
  544. const newData: IWbw[] = [...wordData];
  545. newData.splice(id + 1, 0, ...children);
  546. console.log("new-data", newData);
  547. update(newData);
  548. saveWord(newData, wordData[id].sn[0]);
  549. }
  550. }
  551. };
  552. const wbwPublish = (wbwData: IWbw[], isPublic: boolean) => {
  553. let wordData: IDictRequest[] = [];
  554. wbwData.forEach((data) => {
  555. if (
  556. (typeof data.meaning?.value === "string" &&
  557. data.meaning?.value.trim().length > 0) ||
  558. (typeof data.factorMeaning?.value === "string" &&
  559. data.factorMeaning.value.trim().length > 0)
  560. ) {
  561. const [wordType, wordGrammar] = data.case?.value
  562. ? data.case?.value?.split("#")
  563. : ["", ""];
  564. let conf = data.confidence * 100;
  565. if (data.confidence.toString() === "0.5") {
  566. conf = 100;
  567. }
  568. wordData.push({
  569. word: data.real.value ? data.real.value : "",
  570. type: wordType,
  571. grammar: wordGrammar,
  572. mean: data.meaning?.value,
  573. parent: data.parent?.value,
  574. factors: data.factors?.value,
  575. factormean: data.factorMeaning?.value,
  576. note: data.note?.value,
  577. confidence: conf,
  578. language: channelLang,
  579. status: isPublic ? 30 : 5,
  580. });
  581. }
  582. });
  583. UserWbwPost(wordData, "wbw")
  584. .finally(() => {
  585. setLoading(false);
  586. })
  587. .then((json) => {
  588. if (json.ok) {
  589. message.success(
  590. "wbw " + intl.formatMessage({ id: "flashes.success" })
  591. );
  592. } else {
  593. message.error(json.message);
  594. }
  595. });
  596. };
  597. interface wbwOptions {
  598. studio?: IStudio;
  599. answer?: IWbw;
  600. }
  601. const wbwRender = (item: IWbw, id: number, options?: wbwOptions) => {
  602. return (
  603. <WbwWord
  604. data={item}
  605. answer={options?.answer}
  606. channelId={channelId}
  607. key={id}
  608. mode={displayMode}
  609. display={wbwMode}
  610. fields={fieldDisplay}
  611. studio={studio}
  612. readonly={readonly}
  613. onChange={(e: IWbw, isPublish: boolean, isPublic: boolean) => {
  614. let newData = [...wordData];
  615. newData.forEach((value, index, array) => {
  616. //把新的数据更新到数组
  617. if (value.sn.join() === e.sn.join()) {
  618. console.log("found", e.sn);
  619. array[index] = e;
  620. }
  621. });
  622. if (e.sn.length > 1) {
  623. //把meaning 数据更新到 拆分前单词的factor meaning
  624. const factorMeaning = newData
  625. .filter((value) => {
  626. if (
  627. value.sn.length === e.sn.length &&
  628. e.sn.slice(0, e.sn.length - 1).join() ===
  629. value.sn.slice(0, e.sn.length - 1).join() &&
  630. value.real.value &&
  631. value.real.value.length > 0
  632. ) {
  633. return true;
  634. } else {
  635. return false;
  636. }
  637. })
  638. .map((item) => item.meaning?.value)
  639. .join("+");
  640. console.log("fm", factorMeaning);
  641. newData.forEach((value, index, array) => {
  642. //把新的数据更新到数组
  643. if (value.sn.join() === e.sn.slice(0, e.sn.length - 1).join()) {
  644. console.log("found", value.sn);
  645. array[index].factorMeaning = {
  646. value: factorMeaning,
  647. status: 5,
  648. };
  649. if (array[index].meaning?.status !== WbwStatus.manual) {
  650. array[index].meaning = {
  651. value: factorMeaning.replaceAll("+", " "),
  652. status: 5,
  653. };
  654. }
  655. }
  656. });
  657. }
  658. update(newData);
  659. saveWord(newData, e.sn[0]);
  660. if (isPublish === true) {
  661. wbwPublish([e], isPublic);
  662. }
  663. }}
  664. onSplit={() => {
  665. const newData: IWbw[] = JSON.parse(JSON.stringify(wordData));
  666. if (
  667. id < wordData.length - 1 &&
  668. wordData[id + 1].sn.join("-").indexOf(wordData[id].sn.join("-")) ===
  669. 0
  670. ) {
  671. //合并
  672. console.log("合并");
  673. const compactData = newData.filter((value, index) => {
  674. if (
  675. index !== id &&
  676. value.sn.join("-").indexOf(wordData[id].sn.join("-")) === 0
  677. ) {
  678. return false;
  679. } else {
  680. return true;
  681. }
  682. });
  683. update(compactData);
  684. saveWord(compactData, wordData[id].sn[0]);
  685. } else {
  686. //拆开
  687. console.log("拆开");
  688. wordSplit(id);
  689. }
  690. }}
  691. />
  692. );
  693. };
  694. const resetWbw = () => {
  695. const newData: IWbw[] = [];
  696. let count = 0;
  697. wordData.forEach((value: IWbw) => {
  698. if (
  699. value.type?.value !== null &&
  700. value.type?.value !== ".ctl." &&
  701. value.real.value &&
  702. value.real.value.length > 0
  703. ) {
  704. count++;
  705. newData.push({
  706. uid: value.uid,
  707. book: value.book,
  708. para: value.para,
  709. sn: value.sn,
  710. word: value.word,
  711. real: value.real,
  712. style: value.style,
  713. meaning: { value: "", status: 7 },
  714. type: { value: "", status: 7 },
  715. grammar: { value: "", status: 7 },
  716. grammar2: { value: "", status: 7 },
  717. parent: { value: "", status: 7 },
  718. parent2: { value: "", status: 7 },
  719. case: { value: "", status: 7 },
  720. factors: { value: "", status: 7 },
  721. factorMeaning: { value: "", status: 7 },
  722. confidence: value.confidence,
  723. });
  724. } else {
  725. newData.push(value);
  726. }
  727. });
  728. message.info(`已经重置${count}个`);
  729. update(newData);
  730. saveWbwAll(newData);
  731. };
  732. let updatedAt = moment("1970-1-1");
  733. data.forEach((value) => {
  734. if (moment(value.updated_at).isAfter(updatedAt)) {
  735. updatedAt = moment(value.updated_at);
  736. }
  737. });
  738. const deleteWbw = () => {
  739. const url = `/v2/wbw-sentence/${book}-${para}-${wordStart}-${wordEnd}?channel=${channelId}`;
  740. console.info("api request", url);
  741. setLoading(true);
  742. delete_<IDeleteResponse>(url)
  743. .then((json) => {
  744. console.debug("api response", json);
  745. if (json.ok) {
  746. message.success(
  747. intl.formatMessage(
  748. { id: "message.delete.success" },
  749. { count: json.data }
  750. )
  751. );
  752. } else {
  753. message.error(json.message);
  754. }
  755. })
  756. .finally(() => setLoading(false))
  757. .catch((e) => console.log("Oops errors!", e));
  758. };
  759. return (
  760. <div style={{ width: "100%" }}>
  761. <div
  762. style={{
  763. display: showProgress ? "flex" : "none",
  764. justifyContent: "space-between",
  765. }}
  766. >
  767. <div className="progress" style={{ width: 400 }}>
  768. <Progress percent={progress} size="small" />
  769. </div>
  770. <Space>
  771. <Studio data={studio} hideAvatar />
  772. {<TimeShow updatedAt={updatedAt.toString()} />}
  773. </Space>
  774. </div>
  775. <div className={`layout-${layoutDirection}`}>
  776. <Dropdown
  777. menu={{
  778. items: [
  779. {
  780. key: "magic-dict-current",
  781. label: intl.formatMessage({
  782. id: "buttons.magic-dict",
  783. }),
  784. },
  785. {
  786. key: "progress",
  787. label: "显示/隐藏进度条",
  788. },
  789. {
  790. key: "check",
  791. label: "显示/隐藏错误提示",
  792. },
  793. {
  794. key: "wbw-dict-publish-all",
  795. label: "发布全部单词",
  796. },
  797. {
  798. type: "divider",
  799. },
  800. {
  801. key: "copy-text",
  802. label: intl.formatMessage({
  803. id: "buttons.copy.pali.text",
  804. }),
  805. },
  806. {
  807. key: "reset",
  808. label: intl.formatMessage({
  809. id: "buttons.reset.wbw",
  810. }),
  811. danger: true,
  812. },
  813. {
  814. type: "divider",
  815. },
  816. {
  817. key: "delete",
  818. label: intl.formatMessage({
  819. id: "buttons.delete.wbw.sentence",
  820. }),
  821. danger: true,
  822. disabled: true,
  823. },
  824. ],
  825. onClick: ({ key }) => {
  826. console.log(`Click on item ${key}`);
  827. switch (key) {
  828. case "magic-dict-current":
  829. setLoading(true);
  830. magicDictLookup();
  831. break;
  832. case "wbw-dict-publish-all":
  833. wbwPublish(
  834. wordData,
  835. user?.roles?.includes("basic") ? false : true
  836. );
  837. break;
  838. case "copy-text":
  839. const paliText = wordData
  840. .filter((value) => value.type?.value !== ".ctl.")
  841. .map((item) => item.word.value)
  842. .join(" ");
  843. navigator.clipboard.writeText(paliText).then(() => {
  844. message.success("已经拷贝到剪贴板");
  845. });
  846. break;
  847. case "progress":
  848. setShowProgress((origin) => !origin);
  849. break;
  850. case "check":
  851. loadAnswer();
  852. setCheck(!check);
  853. break;
  854. case "reset":
  855. modal.confirm({
  856. title: "清除逐词解析数据",
  857. icon: <ExclamationCircleOutlined />,
  858. content: "清除这个句子的逐词解析数据,此操作不可恢复",
  859. okText: "确认",
  860. cancelText: "取消",
  861. onOk: () => {
  862. resetWbw();
  863. },
  864. });
  865. break;
  866. case "delete":
  867. modal.confirm({
  868. title: "清除逐词解析数据",
  869. icon: <ExclamationCircleOutlined />,
  870. content: "删除整句的逐词解析数据,此操作不可恢复",
  871. okText: "确认",
  872. cancelText: "取消",
  873. onOk: () => {
  874. deleteWbw();
  875. },
  876. });
  877. break;
  878. }
  879. },
  880. }}
  881. placement="bottomLeft"
  882. >
  883. <Button
  884. loading={loading}
  885. onClick={(e) => e.preventDefault()}
  886. icon={<MoreOutlined />}
  887. size="small"
  888. type="text"
  889. style={{ backgroundColor: "lightblue", opacity: 0.3 }}
  890. />
  891. </Dropdown>
  892. {layoutDirection === "h" ? (
  893. wordData
  894. .map((item, index) => {
  895. let newItem = item;
  896. const spell = item.real.value;
  897. if (spell) {
  898. const matched = sysGrammar?.find((value) =>
  899. value.word.split("...").includes(spell)
  900. );
  901. if (matched) {
  902. console.debug("wbw sent grammar matched", matched);
  903. newItem.grammarId = matched.guid;
  904. }
  905. }
  906. return newItem;
  907. })
  908. .map((item, id) => {
  909. const aa = courseAnswer ?? answer;
  910. const currAnswer = aa?.find(
  911. (value) => value.sn.join() === item.sn.join()
  912. );
  913. return wbwRender(item, id, {
  914. studio: studio,
  915. answer: check ? currAnswer : undefined,
  916. });
  917. })
  918. ) : (
  919. <Tree
  920. selectable={true}
  921. blockNode
  922. treeData={wordData
  923. .filter((value) => value.sn.length === 1)
  924. .map((item, id) => {
  925. const children = wordData.filter(
  926. (value) =>
  927. value.sn.length === 2 &&
  928. value.sn.slice(0, 1).join() === wordData[id].sn.join()
  929. );
  930. return {
  931. title: wbwRender(item, id),
  932. key: item.sn.join(),
  933. isLeaf: !item.factors?.value?.includes("+"),
  934. children:
  935. children.length > 0
  936. ? children.map((item, id) => {
  937. return {
  938. title: wbwRender(item, id),
  939. key: item.sn.join(),
  940. isLeaf: true,
  941. };
  942. })
  943. : undefined,
  944. };
  945. })}
  946. loadData={({ key, children }: any) =>
  947. new Promise<void>((resolve) => {
  948. console.log("key", key, children);
  949. if (children) {
  950. resolve();
  951. return;
  952. }
  953. wordSplit(key, "");
  954. resolve();
  955. })
  956. }
  957. />
  958. )}
  959. </div>
  960. </div>
  961. );
  962. };
  963. interface IWidgetWbwSent {
  964. props: string;
  965. }
  966. const WbwSentWidget = ({ props }: IWidgetWbwSent) => {
  967. const prop = JSON.parse(atob(props)) as IWidget;
  968. return <WbwSentCtl {...prop} />;
  969. };
  970. export default WbwSentWidget;