SentTab.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. import { useEffect, useState } from "react";
  2. import { Badge, Space, Tabs, Typography, message } from "antd";
  3. import {
  4. TranslationOutlined,
  5. CloseOutlined,
  6. BlockOutlined,
  7. } from "@ant-design/icons";
  8. import SentTabButton from "./SentTabButton";
  9. import SentCanRead from "./SentCanRead";
  10. import SentSim from "./SentSim";
  11. import { useIntl } from "react-intl";
  12. import TocPath, {
  13. type ITocPathNode,
  14. } from "../../../../src/components/tipitaka/TocPath";
  15. import type { IWbw } from "../Wbw/WbwWord";
  16. import RelaGraphic from "../Wbw/RelaGraphic";
  17. import SentMenu from "./SentMenu";
  18. import type { ArticleMode } from "../../article/Article";
  19. import type { IResNumber, ISentence } from "../SentEdit";
  20. import SentTabCopy from "./SentTabCopy";
  21. import { fullUrl } from "../../../utils";
  22. import SentWbw from "./SentWbw";
  23. import SentTabButtonWbw from "./SentTabButtonWbw";
  24. const { Text } = Typography;
  25. interface IWidget {
  26. id: string;
  27. book: number;
  28. para: number;
  29. wordStart: number;
  30. wordEnd: number;
  31. channelsId?: string[];
  32. path?: ITocPathNode[];
  33. layout?: "row" | "column";
  34. tranNum?: number;
  35. nissayaNum?: number;
  36. commNum?: number;
  37. originNum: number;
  38. simNum?: number;
  39. wbwData?: IWbw[];
  40. magicDictLoading?: boolean;
  41. compact?: boolean;
  42. mode?: ArticleMode;
  43. loadedRes?: IResNumber;
  44. origin?: ISentence[];
  45. onMagicDict?: Function;
  46. onCompact?: Function;
  47. onModeChange?: Function;
  48. onAffix?: Function;
  49. }
  50. const SentTabWidget = ({
  51. id,
  52. book,
  53. para,
  54. wordStart,
  55. wordEnd,
  56. channelsId,
  57. path,
  58. tranNum = 0,
  59. nissayaNum = 0,
  60. commNum = 0,
  61. originNum,
  62. simNum = 0,
  63. wbwData,
  64. magicDictLoading = false,
  65. compact = false,
  66. mode,
  67. loadedRes,
  68. origin,
  69. onMagicDict,
  70. onCompact,
  71. onModeChange,
  72. onAffix,
  73. }: IWidget) => {
  74. const intl = useIntl();
  75. const [isCompact, setIsCompact] = useState(compact);
  76. const [hover, setHover] = useState(false);
  77. const [currKey, setCurrKey] = useState("close");
  78. const [currTranNum, setCurrTranNum] = useState(tranNum);
  79. const [currNissayaNum, setCurrNissayaNum] = useState(nissayaNum);
  80. const [currCommNum, setCurrCommNum] = useState(commNum);
  81. const [currSimilarNum, setCurrSimilarNum] = useState(simNum);
  82. const [showWbwProgress, setShowWbwProgress] = useState(false);
  83. console.log("SentTabWidget render");
  84. useEffect(() => setIsCompact(compact), [compact]);
  85. const mPath = path
  86. ? [
  87. ...path,
  88. { book: book, paragraph: para, title: para.toString(), level: 100 },
  89. ]
  90. : [];
  91. if (typeof id === "undefined") {
  92. return <></>;
  93. }
  94. const sentId = id.split("_");
  95. const sId = sentId[0].split("-");
  96. const tabButtonStyle: React.CSSProperties | undefined = compact
  97. ? { visibility: hover || currKey !== "close" ? "visible" : "hidden" }
  98. : undefined;
  99. return (
  100. <Tabs
  101. className={
  102. "sent_tabs" +
  103. (isCompact ? " compact" : "") +
  104. (currKey === "close" ? " curr_close" : "")
  105. }
  106. onMouseEnter={() => setHover(true)}
  107. onMouseLeave={() => setHover(false)}
  108. activeKey={currKey}
  109. type="card"
  110. onChange={(activeKey: string) => {
  111. setCurrKey(activeKey);
  112. }}
  113. tabBarStyle={{ marginBottom: 0 }}
  114. size="small"
  115. tabBarGutter={0}
  116. tabBarExtraContent={
  117. <Space>
  118. <TocPath
  119. link="none"
  120. data={mPath}
  121. channels={channelsId}
  122. trigger={path ? path.length > 0 ? path[0].title : <></> : <></>}
  123. />
  124. <Text>{sentId[0]}</Text>
  125. <SentTabCopy wbwData={wbwData} text={`{{${sentId[0]}}}`} />
  126. <SentMenu
  127. book={book}
  128. para={para}
  129. loading={magicDictLoading}
  130. mode={mode}
  131. onMagicDict={(type: string) => {
  132. if (typeof onMagicDict !== "undefined") {
  133. onMagicDict(type);
  134. }
  135. }}
  136. onMenuClick={(key: string) => {
  137. switch (key) {
  138. case "compact":
  139. if (typeof onCompact !== "undefined") {
  140. setIsCompact(true);
  141. onCompact(true);
  142. }
  143. break;
  144. case "normal":
  145. if (typeof onCompact !== "undefined") {
  146. setIsCompact(false);
  147. onCompact(false);
  148. }
  149. break;
  150. case "origin-edit":
  151. if (typeof onModeChange !== "undefined") {
  152. onModeChange("edit");
  153. }
  154. break;
  155. case "origin-wbw":
  156. if (typeof onModeChange !== "undefined") {
  157. onModeChange("wbw");
  158. }
  159. break;
  160. case "copy-id":
  161. const id = `{{${book}-${para}-${wordStart}-${wordEnd}}}`;
  162. navigator.clipboard.writeText(id).then(() => {
  163. message.success("编号已经拷贝到剪贴板");
  164. });
  165. break;
  166. case "copy-link":
  167. let link = `/article/para/${book}-${para}?mode=edit`;
  168. link += `&book=${book}&par=${para}`;
  169. if (channelsId) {
  170. link += `&channel=` + channelsId?.join("_");
  171. }
  172. link += `&focus=${book}-${para}-${wordStart}-${wordEnd}`;
  173. navigator.clipboard.writeText(fullUrl(link)).then(() => {
  174. message.success("链接地址已经拷贝到剪贴板");
  175. });
  176. break;
  177. case "affix":
  178. if (typeof onAffix !== "undefined") {
  179. onAffix();
  180. }
  181. break;
  182. default:
  183. break;
  184. }
  185. }}
  186. />
  187. </Space>
  188. }
  189. items={[
  190. {
  191. label: (
  192. <span style={tabButtonStyle}>
  193. <Badge size="small" count={0}>
  194. <CloseOutlined />
  195. </Badge>
  196. </span>
  197. ),
  198. key: "close",
  199. children: <></>,
  200. },
  201. {
  202. label: (
  203. <SentTabButton
  204. style={tabButtonStyle}
  205. icon={<TranslationOutlined />}
  206. type="translation"
  207. sentId={id}
  208. count={
  209. currTranNum
  210. ? currTranNum -
  211. (loadedRes?.translation ? loadedRes.translation : 0)
  212. : undefined
  213. }
  214. title={intl.formatMessage({
  215. id: "channel.type.translation.label",
  216. })}
  217. />
  218. ),
  219. key: "translation",
  220. children: (
  221. <div className="content">
  222. <SentCanRead
  223. book={parseInt(sId[0])}
  224. para={parseInt(sId[1])}
  225. wordStart={parseInt(sId[2])}
  226. wordEnd={parseInt(sId[3])}
  227. type="translation"
  228. channelsId={channelsId}
  229. onCreate={() => setCurrTranNum((origin) => origin + 1)}
  230. />
  231. </div>
  232. ),
  233. },
  234. {
  235. label: (
  236. <SentTabButton
  237. style={tabButtonStyle}
  238. icon={<CloseOutlined />}
  239. type="nissaya"
  240. sentId={id}
  241. count={
  242. currNissayaNum
  243. ? currNissayaNum -
  244. (loadedRes?.nissaya ? loadedRes.nissaya : 0)
  245. : undefined
  246. }
  247. title={intl.formatMessage({
  248. id: "channel.type.nissaya.label",
  249. })}
  250. />
  251. ),
  252. key: "nissaya",
  253. children: (
  254. <SentCanRead
  255. book={parseInt(sId[0])}
  256. para={parseInt(sId[1])}
  257. wordStart={parseInt(sId[2])}
  258. wordEnd={parseInt(sId[3])}
  259. type="nissaya"
  260. channelsId={channelsId}
  261. onCreate={() => setCurrNissayaNum((origin) => origin + 1)}
  262. />
  263. ),
  264. },
  265. {
  266. label: (
  267. <SentTabButton
  268. style={tabButtonStyle}
  269. icon={<TranslationOutlined />}
  270. type="commentary"
  271. sentId={id}
  272. count={
  273. currCommNum
  274. ? currCommNum -
  275. (loadedRes?.commentary ? loadedRes.commentary : 0)
  276. : undefined
  277. }
  278. title={intl.formatMessage({
  279. id: "channel.type.commentary.label",
  280. })}
  281. />
  282. ),
  283. key: "commentary",
  284. children: (
  285. <SentCanRead
  286. book={parseInt(sId[0])}
  287. para={parseInt(sId[1])}
  288. wordStart={parseInt(sId[2])}
  289. wordEnd={parseInt(sId[3])}
  290. type="commentary"
  291. channelsId={channelsId}
  292. onCreate={() => setCurrCommNum((origin) => origin + 1)}
  293. />
  294. ),
  295. },
  296. {
  297. label: (
  298. <SentTabButton
  299. icon={<BlockOutlined />}
  300. type="original"
  301. sentId={id}
  302. count={originNum}
  303. title={intl.formatMessage({
  304. id: "channel.type.original.label",
  305. })}
  306. />
  307. ),
  308. key: "original",
  309. children: (
  310. <SentCanRead
  311. book={parseInt(sId[0])}
  312. para={parseInt(sId[1])}
  313. wordStart={parseInt(sId[2])}
  314. wordEnd={parseInt(sId[3])}
  315. type="original"
  316. origin={origin}
  317. />
  318. ),
  319. },
  320. {
  321. label: (
  322. <SentTabButton
  323. style={tabButtonStyle}
  324. icon={<BlockOutlined />}
  325. type="original"
  326. sentId={id}
  327. count={currSimilarNum}
  328. title={intl.formatMessage({
  329. id: "buttons.sim",
  330. })}
  331. />
  332. ),
  333. key: "sim",
  334. children: (
  335. <SentSim
  336. book={parseInt(sId[0])}
  337. para={parseInt(sId[1])}
  338. wordStart={parseInt(sId[2])}
  339. wordEnd={parseInt(sId[3])}
  340. channelsId={channelsId}
  341. limit={5}
  342. onCreate={() => setCurrSimilarNum((origin) => origin + 1)}
  343. />
  344. ),
  345. },
  346. {
  347. label: (
  348. <SentTabButtonWbw
  349. style={tabButtonStyle}
  350. sentId={id}
  351. count={0}
  352. onMenuClick={(keyPath: string[]) => {
  353. switch (keyPath.join("-")) {
  354. case "show-progress":
  355. setShowWbwProgress((origin) => !origin);
  356. break;
  357. }
  358. }}
  359. />
  360. ),
  361. key: "wbw",
  362. children: (
  363. <SentWbw
  364. book={parseInt(sId[0])}
  365. para={parseInt(sId[1])}
  366. wordStart={parseInt(sId[2])}
  367. wordEnd={parseInt(sId[3])}
  368. channelsId={channelsId}
  369. wbwProgress={showWbwProgress}
  370. />
  371. ),
  372. },
  373. {
  374. label: <span style={tabButtonStyle}>{"关系图"}</span>,
  375. key: "relation-graphic",
  376. children: <RelaGraphic wbwData={wbwData} />,
  377. },
  378. ]}
  379. />
  380. );
  381. };
  382. export default SentTabWidget;