WbwWord.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import { useState, useEffect, useRef } from "react";
  2. import type { UploadFile } from "antd/es/upload/interface";
  3. import { useAppSelector } from "../../../hooks";
  4. import { add, wordIndex } from "../../../reducers/inline-dict";
  5. import { get } from "../../../request";
  6. import store from "../../../store";
  7. import { IApiResponseDictList } from "../../api/Dict";
  8. import WbwCase from "./WbwCase";
  9. import { bookMarkColor } from "./WbwDetailBookMark";
  10. import WbwFactorMeaning from "./WbwFactorMeaning";
  11. import WbwFactors from "./WbwFactors";
  12. import WbwMeaning from "./WbwMeaning";
  13. import WbwPali from "./WbwPali";
  14. import "./wbw.css";
  15. import WbwPara from "./WbwPara";
  16. import WbwPage from "./WbwPage";
  17. import WbwRelationAdd from "./WbwRelationAdd";
  18. import { ArticleMode } from "../../article/Article";
  19. export type TFieldName =
  20. | "word"
  21. | "real"
  22. | "meaning"
  23. | "type"
  24. | "grammar"
  25. | "grammar2"
  26. | "case"
  27. | "parent"
  28. | "parent2"
  29. | "factors"
  30. | "factorMeaning"
  31. | "relation"
  32. | "note"
  33. | "bookMarkColor"
  34. | "bookMarkText"
  35. | "locked"
  36. | "confidence";
  37. export interface IWbwField {
  38. field: TFieldName;
  39. value: string;
  40. }
  41. enum WbwStatus {
  42. initiate = 0,
  43. auto = 3,
  44. manual = 5,
  45. }
  46. export interface WbwElement<R> {
  47. value: R;
  48. status: WbwStatus;
  49. }
  50. export interface IWbw {
  51. uid?: string;
  52. book: number;
  53. para: number;
  54. sn: number[];
  55. word: WbwElement<string>;
  56. real: WbwElement<string>;
  57. meaning?: WbwElement<string | null>;
  58. type?: WbwElement<string | null>;
  59. grammar?: WbwElement<string | null>;
  60. style?: WbwElement<string | null>;
  61. case?: WbwElement<string | null>;
  62. parent?: WbwElement<string | null>;
  63. parent2?: WbwElement<string | null>;
  64. grammar2?: WbwElement<string | null>;
  65. factors?: WbwElement<string | null>;
  66. factorMeaning?: WbwElement<string | null>;
  67. relation?: WbwElement<string | null>;
  68. note?: WbwElement<string | null>;
  69. bookMarkColor?: WbwElement<number | null>;
  70. bookMarkText?: WbwElement<string | null>;
  71. locked?: boolean;
  72. confidence: number;
  73. attachments?: UploadFile[];
  74. hasComment?: boolean;
  75. }
  76. export interface IWbwFields {
  77. meaning?: boolean;
  78. factors?: boolean;
  79. factorMeaning?: boolean;
  80. case?: boolean;
  81. }
  82. export type TWbwDisplayMode = "block" | "inline";
  83. interface IWidget {
  84. data: IWbw;
  85. display?: TWbwDisplayMode;
  86. fields?: IWbwFields;
  87. mode?: ArticleMode;
  88. wordDark?: boolean;
  89. onChange?: Function;
  90. onSplit?: Function;
  91. }
  92. const WbwWordWidget = ({
  93. data,
  94. display = "block",
  95. mode = "edit",
  96. fields = { meaning: true, factors: true, factorMeaning: true, case: true },
  97. wordDark = false,
  98. onChange,
  99. onSplit,
  100. }: IWidget) => {
  101. const [wordData, setWordData] = useState(data);
  102. const fieldDisplay = fields;
  103. const [newFactors, setNewFactors] = useState<string>();
  104. const [showRelationTool, setShowRelationTool] = useState(false);
  105. const intervalRef = useRef<number | null>(null); //防抖计时器句柄
  106. const inlineWordIndex = useAppSelector(wordIndex);
  107. useEffect(() => {
  108. setWordData(data);
  109. }, [data]);
  110. const color = wordData.bookMarkColor?.value
  111. ? bookMarkColor[wordData.bookMarkColor.value]
  112. : "unset";
  113. const wbwCtl = wordData.type?.value === ".ctl." ? "wbw_ctl" : "";
  114. const wbwAnchor = wordData.grammar?.value === ".a." ? "wbw_anchor" : "";
  115. const wbwDark = wordDark ? "dark" : undefined;
  116. const styleWbw: React.CSSProperties = {
  117. display: display === "block" ? "block" : "flex",
  118. };
  119. /**
  120. * 停止查字典计时
  121. * 在两种情况下停止计时
  122. * 1. 开始查字典
  123. * 2. 防抖时间内鼠标移出单词区
  124. */
  125. const stopLookup = () => {
  126. if (intervalRef.current) {
  127. window.clearInterval(intervalRef.current);
  128. intervalRef.current = null;
  129. }
  130. };
  131. /**
  132. * 查字典
  133. * @param word 要查的单词
  134. */
  135. const lookup = (word: string) => {
  136. stopLookup();
  137. //查询这个词在内存字典里是否有
  138. if (inlineWordIndex.includes(word)) {
  139. //已经有了,退出
  140. return;
  141. }
  142. get<IApiResponseDictList>(`/v2/wbwlookup?word=${word}`).then((json) => {
  143. console.log("lookup ok", json.data.count);
  144. console.log("time", json.data.time);
  145. //存储到redux
  146. store.dispatch(add(json.data.rows));
  147. });
  148. console.log("lookup", word);
  149. };
  150. if (wordData.type?.value === ".ctl.") {
  151. if (wordData.word.value.includes("para")) {
  152. return <WbwPara data={wordData} />;
  153. } else {
  154. return <WbwPage data={wordData} />;
  155. }
  156. } else {
  157. return (
  158. <div
  159. className={`wbw_word ${display}_${mode} ${wbwCtl} ${wbwAnchor} ${wbwDark} `}
  160. style={styleWbw}
  161. onMouseEnter={() => {
  162. setShowRelationTool(true);
  163. if (
  164. intervalRef.current === null &&
  165. wordData.real &&
  166. wordData.real.value?.length > 0
  167. ) {
  168. //开始计时,计时结束查字典
  169. intervalRef.current = window.setInterval(
  170. lookup,
  171. 300,
  172. wordData.real.value
  173. );
  174. }
  175. }}
  176. onMouseLeave={() => {
  177. stopLookup();
  178. setShowRelationTool(false);
  179. }}
  180. >
  181. {showRelationTool ? <WbwRelationAdd data={data} /> : undefined}
  182. <WbwPali
  183. key="pali"
  184. data={wordData}
  185. display={display}
  186. onSave={(e: IWbw) => {
  187. console.log("save", e);
  188. const newData: IWbw = JSON.parse(JSON.stringify(e));
  189. setWordData(newData);
  190. if (typeof onChange !== "undefined") {
  191. onChange(e);
  192. }
  193. }}
  194. />
  195. <div
  196. className="wbw_body"
  197. style={{
  198. background: `linear-gradient(90deg, rgba(255, 255, 255, 0), ${color})`,
  199. }}
  200. >
  201. {fieldDisplay?.meaning ? (
  202. <WbwMeaning
  203. key="meaning"
  204. mode={mode}
  205. data={wordData}
  206. display={display}
  207. onChange={(e: string) => {
  208. console.log("meaning change", e);
  209. const newData: IWbw = JSON.parse(JSON.stringify(wordData));
  210. newData.meaning = { value: e, status: 5 };
  211. setWordData(newData);
  212. if (typeof onChange !== "undefined") {
  213. onChange(newData);
  214. }
  215. }}
  216. />
  217. ) : undefined}
  218. {fieldDisplay?.factors ? (
  219. <WbwFactors
  220. key="factors"
  221. data={wordData}
  222. display={display}
  223. onChange={(e: string) => {
  224. console.log("factor change", e);
  225. const newData: IWbw = JSON.parse(JSON.stringify(wordData));
  226. newData.factors = { value: e, status: 5 };
  227. setNewFactors(e);
  228. setWordData(newData);
  229. if (typeof onChange !== "undefined") {
  230. onChange(newData);
  231. }
  232. }}
  233. />
  234. ) : undefined}
  235. {fieldDisplay?.factorMeaning ? (
  236. <WbwFactorMeaning
  237. key="fm"
  238. data={wordData}
  239. display={display}
  240. factors={newFactors}
  241. onChange={(e: string) => {
  242. const newData: IWbw = JSON.parse(JSON.stringify(wordData));
  243. newData.factorMeaning = { value: e, status: 5 };
  244. setWordData(newData);
  245. if (typeof onChange !== "undefined") {
  246. onChange(newData);
  247. }
  248. }}
  249. />
  250. ) : undefined}
  251. {fieldDisplay?.case ? (
  252. <WbwCase
  253. key="case"
  254. data={wordData}
  255. display={display}
  256. onSplit={(e: boolean) => {
  257. console.log("onSplit", wordData.factors?.value);
  258. if (typeof onSplit !== "undefined") {
  259. onSplit(e);
  260. }
  261. }}
  262. onChange={(e: string) => {
  263. const newData: IWbw = JSON.parse(JSON.stringify(wordData));
  264. newData.case = { value: e, status: 7 };
  265. setWordData(newData);
  266. if (typeof onChange !== "undefined") {
  267. onChange(newData);
  268. }
  269. }}
  270. />
  271. ) : undefined}
  272. </div>
  273. </div>
  274. );
  275. }
  276. };
  277. export default WbwWordWidget;