WbwPali.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { useEffect, useState } from "react";
  2. import { Button, Popover, Space, Typography } from "antd";
  3. import {
  4. TagTwoTone,
  5. InfoCircleOutlined,
  6. CommentOutlined,
  7. ApartmentOutlined,
  8. } from "@ant-design/icons";
  9. import "./wbw.css";
  10. import WbwDetail from "./WbwDetail";
  11. import { IWbw, TWbwDisplayMode } from "./WbwWord";
  12. import { bookMarkColor } from "./WbwDetailBookMark";
  13. import WbwVideoButton from "./WbwVideoButton";
  14. import CommentBox from "../../discussion/DiscussionBox";
  15. import PaliText from "./PaliText";
  16. import store from "../../../store";
  17. import { lookup } from "../../../reducers/command";
  18. import { useAppSelector } from "../../../hooks";
  19. import { add, relationAddParam } from "../../../reducers/relation-add";
  20. import { ArticleMode } from "../../article/Article";
  21. const { Paragraph } = Typography;
  22. interface IWidget {
  23. data: IWbw;
  24. display?: TWbwDisplayMode;
  25. mode?: ArticleMode;
  26. onSave?: Function;
  27. }
  28. const WbwPaliWidget = ({ data, mode, display, onSave }: IWidget) => {
  29. const [popOpen, setPopOpen] = useState(false);
  30. const [paliColor, setPaliColor] = useState("unset");
  31. const [hasComment, setHasComment] = useState(data.hasComment);
  32. /**
  33. * 处理 relation 链接事件
  34. * 高亮可能的单词
  35. */
  36. const addParam = useAppSelector(relationAddParam);
  37. useEffect(() => {
  38. let grammar = data.case?.value
  39. ?.replace("#", "$")
  40. .replaceAll(".", "")
  41. .split("$");
  42. if (data.grammar2?.value) {
  43. if (grammar) {
  44. grammar = [data.grammar2?.value, ...grammar];
  45. } else {
  46. grammar = [data.grammar2?.value];
  47. }
  48. }
  49. console.log("grammar", grammar);
  50. if (typeof grammar === "undefined") {
  51. return;
  52. }
  53. const match = addParam?.relations?.filter((value) => {
  54. let caseMatch = true;
  55. let spellMatch = true;
  56. if (!value.to) {
  57. return false;
  58. }
  59. if (value.to?.case) {
  60. let matchCount = 0;
  61. if (grammar) {
  62. for (const iterator of value.to.case) {
  63. if (grammar?.includes(iterator)) {
  64. matchCount++;
  65. }
  66. }
  67. }
  68. if (matchCount !== value.to.case.length) {
  69. caseMatch = false;
  70. }
  71. }
  72. if (value.from?.spell) {
  73. if (data.real.value !== value.from?.spell) {
  74. spellMatch = false;
  75. }
  76. }
  77. return caseMatch && spellMatch;
  78. });
  79. if (match && match.length > 0) {
  80. setPaliColor("greenyellow");
  81. }
  82. }, [
  83. addParam?.relations,
  84. data.case?.value,
  85. data.grammar2?.value,
  86. data.real.value,
  87. ]);
  88. /**
  89. * 点击连接或取消后,打开弹窗
  90. */
  91. useEffect(() => {
  92. if (addParam?.command === "apply" || addParam?.command === "cancel") {
  93. setPaliColor("unset");
  94. if (
  95. addParam.src_sn === data.sn.join("-") &&
  96. addParam.book === data.book &&
  97. addParam.para === data.para
  98. ) {
  99. setPopOpen(true);
  100. store.dispatch(
  101. add({
  102. book: data.book,
  103. para: data.para,
  104. src_sn: data.sn.join("-"),
  105. command: "finish",
  106. })
  107. );
  108. }
  109. }
  110. }, [
  111. addParam?.book,
  112. addParam?.command,
  113. addParam?.para,
  114. addParam?.src_sn,
  115. data.book,
  116. data.para,
  117. data.sn,
  118. ]);
  119. const handleClickChange = (open: boolean) => {
  120. if (mode === "wbw") {
  121. if (open) {
  122. setPaliColor("lightblue");
  123. } else {
  124. setPaliColor("unset");
  125. }
  126. setPopOpen(open);
  127. }
  128. };
  129. const wbwDetail = (
  130. <WbwDetail
  131. data={data}
  132. onClose={() => {
  133. setPaliColor("unset");
  134. setPopOpen(false);
  135. }}
  136. onSave={(e: IWbw, isPublish: boolean) => {
  137. if (typeof onSave !== "undefined") {
  138. onSave(e, isPublish);
  139. setPopOpen(false);
  140. setPaliColor("unset");
  141. }
  142. }}
  143. onCommentCountChange={(count: number) => {
  144. if (count > 0) {
  145. setHasComment(true);
  146. } else {
  147. setHasComment(false);
  148. }
  149. }}
  150. />
  151. );
  152. const noteIcon = data.note?.value ? (
  153. data.note.value.trim() !== "" ? (
  154. <Popover content={data.note?.value} placement="bottom">
  155. <InfoCircleOutlined style={{ color: "blue" }} />
  156. </Popover>
  157. ) : undefined
  158. ) : undefined;
  159. const color = data.bookMarkColor?.value
  160. ? bookMarkColor[data.bookMarkColor.value]
  161. : "white";
  162. //生成视频播放按钮
  163. const videoList = data.attachments?.filter((item) =>
  164. item.content_type?.includes("video")
  165. );
  166. const videoIcon = videoList ? (
  167. <WbwVideoButton
  168. video={videoList?.map((item) => {
  169. return {
  170. videoId: item.id,
  171. type: item.content_type,
  172. title: item.title,
  173. };
  174. })}
  175. />
  176. ) : undefined;
  177. const relationIcon = data.relation ? (
  178. <ApartmentOutlined style={{ color: "blue" }} />
  179. ) : undefined;
  180. const bookMarkIcon =
  181. data.bookMarkText?.value && data.bookMarkText.value.trim() !== "" ? (
  182. <Popover
  183. content={<Paragraph copyable>{data.bookMarkText.value}</Paragraph>}
  184. placement="bottom"
  185. >
  186. <TagTwoTone twoToneColor={color} />
  187. </Popover>
  188. ) : undefined;
  189. const classPali = data.style?.value === "note" ? "wbw_note" : "pali";
  190. let padding: string;
  191. if (typeof data.real !== "undefined" && data.real.value !== "") {
  192. padding = "4px";
  193. } else {
  194. padding = "4px 0";
  195. }
  196. let pali = <PaliText text={data.word.value} termToLocal={false} />;
  197. if (data.word.value.indexOf("}") >= 0) {
  198. const paliArray = data.word.value.replace("{", "").split("}");
  199. pali = (
  200. <>
  201. <span style={{ fontWeight: 700 }}>
  202. <PaliText text={paliArray[0]} termToLocal={false} />
  203. </span>
  204. <PaliText text={paliArray[1]} termToLocal={false} />
  205. </>
  206. );
  207. }
  208. const paliWord = (
  209. <span
  210. className={classPali}
  211. style={{
  212. backgroundColor: paliColor,
  213. padding: padding,
  214. borderRadius: 5,
  215. }}
  216. onClick={() => {
  217. //发送点词查询消息
  218. if (typeof data.real?.value === "string") {
  219. store.dispatch(lookup(data.real.value));
  220. }
  221. }}
  222. >
  223. {pali}
  224. </span>
  225. );
  226. let commentShellStyle: React.CSSProperties = {
  227. display: "inline-block",
  228. };
  229. const discussionIcon = hasComment ? (
  230. <div style={commentShellStyle}>
  231. <CommentBox
  232. resId={data.uid}
  233. resType="wbw"
  234. trigger={<Button icon={<CommentOutlined />} type="text" title="讨论" />}
  235. onCommentCountChange={(count: number) => {
  236. if (count > 0) {
  237. setHasComment(true);
  238. } else {
  239. setHasComment(false);
  240. }
  241. }}
  242. />
  243. </div>
  244. ) : undefined;
  245. if (typeof data.real !== "undefined" && data.real.value !== "") {
  246. //非标点符号
  247. return (
  248. <div className="pali_shell">
  249. <Popover
  250. content={wbwDetail}
  251. placement="bottom"
  252. trigger="click"
  253. open={popOpen}
  254. onOpenChange={handleClickChange}
  255. >
  256. {paliWord}
  257. </Popover>
  258. <Space>
  259. {videoIcon}
  260. {noteIcon}
  261. {bookMarkIcon}
  262. {relationIcon}
  263. {discussionIcon}
  264. </Space>
  265. </div>
  266. );
  267. } else {
  268. //标点符号
  269. return (
  270. <div className="pali_shell" style={{ cursor: "unset" }}>
  271. {paliWord}
  272. </div>
  273. );
  274. }
  275. };
  276. export default WbwPaliWidget;