ParaHandle.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import { Button, Dropdown, type MenuProps, message, notification } from "antd"
  2. import { useNavigate, useSearchParams } from "react-router";
  3. import { fullUrl } from "../../utils";
  4. import { useIntl } from "react-intl";
  5. import { addToCart } from "./SentEdit/SentCart";
  6. import { scrollToTop } from "../../pages/library/article/show";
  7. import store from "../../store";
  8. import { modeChange } from "../../reducers/article-mode";
  9. interface IWidgetParaHandleCtl {
  10. book: number;
  11. para: number;
  12. mode?: string;
  13. channels?: string[];
  14. sentences: string[];
  15. onTranslate?: Function;
  16. }
  17. export const ParaHandleCtl = ({
  18. book,
  19. para,
  20. ___mode = "read",
  21. ___channels,
  22. sentences,
  23. onTranslate,
  24. }: IWidgetParaHandleCtl) => {
  25. const navigate = useNavigate();
  26. const [searchParams] = useSearchParams();
  27. const intl = useIntl();
  28. const items: MenuProps["items"] = [
  29. {
  30. key: "solo",
  31. label: intl.formatMessage({
  32. id: "labels.curr.paragraph.only",
  33. }),
  34. },
  35. {
  36. key: "solo-in-tab",
  37. label: intl.formatMessage({
  38. id: "labels.curr.paragraph.open",
  39. }),
  40. },
  41. {
  42. key: "ai-translate",
  43. label: intl.formatMessage({
  44. id: "buttons.ai.translate",
  45. }),
  46. },
  47. {
  48. type: "divider",
  49. },
  50. {
  51. key: "mode",
  52. label: intl.formatMessage({
  53. id: "buttons.set.display.mode",
  54. }),
  55. children: [
  56. {
  57. key: "mode-translate",
  58. label: intl.formatMessage({
  59. id: "buttons.translate",
  60. }),
  61. },
  62. {
  63. key: "mode-wbw",
  64. label: intl.formatMessage({
  65. id: "buttons.wbw",
  66. }),
  67. },
  68. ],
  69. },
  70. {
  71. type: "divider",
  72. },
  73. {
  74. key: "copy-sent",
  75. label: intl.formatMessage({
  76. id: "labels.curr.paragraph.copy.tpl",
  77. }),
  78. },
  79. {
  80. key: "cart-sent",
  81. label: intl.formatMessage({
  82. id: "labels.curr.paragraph.cart.tpl",
  83. }),
  84. },
  85. {
  86. key: "quote-link-tpl",
  87. label: intl.formatMessage({
  88. id: "labels.curr.paragraph.copy.quote.link.tpl",
  89. }),
  90. children: [
  91. {
  92. key: "quote-link-tpl-c",
  93. label: intl.formatMessage({
  94. id: "labels.page.number.type.c",
  95. }),
  96. },
  97. {
  98. key: "quote-link-tpl-m",
  99. label: intl.formatMessage({
  100. id: "labels.page.number.type.M",
  101. }),
  102. },
  103. {
  104. key: "quote-link-tpl-p",
  105. label: intl.formatMessage({
  106. id: "labels.page.number.type.P",
  107. }),
  108. },
  109. {
  110. key: "quote-link-tpl-t",
  111. label: intl.formatMessage({
  112. id: "labels.page.number.type.T",
  113. }),
  114. },
  115. ],
  116. },
  117. ];
  118. const copyToClipboard = (text: string) => {
  119. navigator.clipboard.writeText(text).then(() => {
  120. message.success("链接地址已经拷贝到剪贴板");
  121. });
  122. };
  123. const onClick: MenuProps["onClick"] = (e) => {
  124. /**
  125. * TODO 临时的解决方案。以后应该从传参获取其他参数,然后reducer 通知更新。
  126. * 因为如果是Article组件被嵌入其他页面。不能直接更新浏览器,而是应该更新Article组件内部
  127. */
  128. let url = `/article/para/${book}-${para}?book=${book}&par=${para}`;
  129. const param: string[] = [];
  130. searchParams.forEach((value: unknown, key: unknown) => {
  131. if (key !== "book" && key !== "par") {
  132. param.push(`${key}=${value}`);
  133. }
  134. });
  135. if (param.length > 0) {
  136. url += "&" + param.join("&");
  137. }
  138. switch (e.key) {
  139. case "solo":
  140. navigate(url);
  141. scrollToTop();
  142. break;
  143. case "solo-in-tab":
  144. window.open(fullUrl(url), "_blank");
  145. break;
  146. case "mode-translate":
  147. store.dispatch(modeChange({ mode: "edit", id: `${book}-${para}` }));
  148. break;
  149. case "ai-translate":
  150. if (typeof onTranslate !== "undefined") {
  151. onTranslate();
  152. }
  153. break;
  154. case "mode-wbw":
  155. store.dispatch(modeChange({ mode: "wbw", id: `${book}-${para}` }));
  156. break;
  157. case "copy-sent":
  158. copyToClipboard(sentences.map((item) => `{{${item}}}`).join(""));
  159. break;
  160. case "cart-sent":
  161. const cartData = sentences.map((item) => {
  162. return { id: `{{${item}}}`, text: `{{${item}}}` };
  163. });
  164. addToCart(cartData);
  165. notification.success({
  166. message: cartData.length + "个句子已经添加到Cart",
  167. });
  168. break;
  169. case "quote-link-tpl-c":
  170. copyToClipboard(`{{ql|type=c|book=${book}|para=${para}}}`);
  171. break;
  172. case "quote-link-tpl-m":
  173. copyToClipboard(`{{ql|type=m|book=${book}|para=${para}}}`);
  174. break;
  175. case "quote-link-tpl-p":
  176. copyToClipboard(`{{ql|type=p|book=${book}|para=${para}}}`);
  177. break;
  178. case "quote-link-tpl-t":
  179. copyToClipboard(`{{ql|type=t|book=${book}|para=${para}}}`);
  180. break;
  181. default:
  182. break;
  183. }
  184. };
  185. return (
  186. <div>
  187. <Dropdown
  188. menu={{ items, onClick }}
  189. placement="bottomLeft"
  190. trigger={["click"]}
  191. >
  192. <Button size="small" type="text">
  193. {para}
  194. </Button>
  195. </Dropdown>
  196. </div>
  197. );
  198. };
  199. interface IWidget {
  200. props: string;
  201. }
  202. const Widget = ({ props }: IWidget) => {
  203. const prop = JSON.parse(atob(props)) as IWidgetParaHandleCtl;
  204. return (
  205. <>
  206. <ParaHandleCtl {...prop} />
  207. </>
  208. );
  209. };
  210. export default Widget;