visuddhinanda 2 yıl önce
ebeveyn
işleme
838a7045f5

+ 182 - 0
dashboard/src/components/export/ExportModal.tsx

@@ -0,0 +1,182 @@
+import { Modal, Progress, Select, Typography, message } from "antd";
+import { useEffect, useRef, useState } from "react";
+import { get } from "../../request";
+import { ArticleType } from "../article/Article";
+
+const { Text } = Typography;
+
+interface IExportResponse {
+  ok: boolean;
+  message?: string;
+  data: string;
+}
+
+interface IStatus {
+  progress: number;
+  message: string;
+}
+interface IExportStatusResponse {
+  ok: boolean;
+  message?: string;
+  data: {
+    url?: string;
+    status: IStatus;
+  };
+}
+interface IWidget {
+  type?: ArticleType;
+  articleId?: string;
+  book?: string | null;
+  para?: string | null;
+  channelId?: string | null;
+  anthologyId?: string | null;
+  open?: boolean;
+  onClose?: Function;
+}
+
+const ExportModalWidget = ({
+  type,
+  book,
+  para,
+  channelId,
+  articleId,
+  anthologyId,
+  open = false,
+  onClose,
+}: IWidget) => {
+  const [isModalOpen, setIsModalOpen] = useState(open);
+  const [filename, setFilename] = useState<string>();
+  const [url, setUrl] = useState<string>();
+  const [format, setFormat] = useState<string>("html");
+  const [exportStatus, setExportStatus] = useState<IStatus>();
+  const [exportStart, setExportStart] = useState(false);
+  const filenameRef = useRef(filename);
+
+  useEffect(() => {
+    // 及时更新 count 值
+    filenameRef.current = filename;
+  });
+  const queryStatus = () => {
+    console.log("timer", filenameRef.current);
+    if (typeof filenameRef.current === "undefined") {
+      return;
+    }
+    const url = `/v2/export/${filenameRef.current}`;
+    console.log("url", url);
+    get<IExportStatusResponse>(url).then((json) => {
+      if (json.ok) {
+        console.log("filename", json);
+        setExportStatus(json.data.status);
+        if (json.data.status.progress === 1) {
+          setFilename(undefined);
+          setUrl(json.data.url);
+        }
+      } else {
+      }
+    });
+  };
+
+  useEffect(() => {
+    const interval = setInterval(() => queryStatus(), 3000);
+    return () => clearInterval(interval);
+  }, []);
+
+  const exportChapter = (
+    book: number,
+    para: number,
+    channel: string,
+    format: string
+  ): void => {
+    const url = `/v2/export?book=${book}&par=${para}&channel=${channel}&format=${format}`;
+    console.log("url", url);
+    setExportStart(true);
+    get<IExportResponse>(url).then((json) => {
+      if (json.ok) {
+        const filename = json.data;
+        console.log("filename", filename);
+        setFilename(filename);
+      } else {
+      }
+    });
+  };
+  const closeModal = () => {
+    if (typeof onClose !== "undefined") {
+      onClose();
+    }
+  };
+  useEffect(() => setIsModalOpen(open), [open]);
+  return (
+    <Modal
+      destroyOnClose
+      title="导出"
+      width={400}
+      open={isModalOpen}
+      onOk={() => {
+        console.log("type", type);
+        if (type === "chapter") {
+          if (articleId && channelId) {
+            const para = articleId.split("-").map((item) => parseInt(item));
+            const channels = channelId.split("_");
+            if (para.length === 2) {
+              exportChapter(para[0], para[1], channels[0], "html");
+            } else {
+              console.error("段落编号错误", articleId);
+            }
+          }
+        } else {
+          message.error("目前只支持章节导出");
+        }
+      }}
+      onCancel={closeModal}
+      okText={"导出"}
+      okButtonProps={{ disabled: exportStart }}
+    >
+      <div style={{ display: "flex", justifyContent: "space-between" }}>
+        <span>格式</span>
+        <Select
+          defaultValue={format}
+          bordered={false}
+          options={[
+            {
+              value: "pdf",
+              label: "PDF",
+              disabled: true,
+            },
+            {
+              value: "word",
+              label: "Word",
+              disabled: true,
+            },
+            {
+              value: "html",
+              label: "Html",
+            },
+          ]}
+          onSelect={(value) => setFormat(value)}
+        />
+      </div>
+      <div style={{ display: exportStart ? "block" : "none" }}>
+        <Text>{exportStatus ? exportStatus.message : "正在生成……"}</Text>
+        <Progress
+          percent={exportStatus ? Math.round(exportStatus?.progress * 100) : 0}
+          status={
+            exportStatus
+              ? exportStatus.progress === 1
+                ? "success"
+                : "active"
+              : "normal"
+          }
+        />
+        {url ? (
+          <a href={url} target="_blank" rel="noreferrer">
+            {"下载"}
+          </a>
+        ) : (
+          <></>
+        )}
+      </div>
+    </Modal>
+  );
+};
+
+export default ExportModalWidget;

+ 74 - 0
dashboard/src/components/export/ShareButton.tsx

@@ -0,0 +1,74 @@
+import { Button, Dropdown, Space, Typography } from "antd";
+import { ShareAltOutlined, ExportOutlined } from "@ant-design/icons";
+import ExportModal from "./ExportModal";
+import { useState } from "react";
+import { ArticleType } from "../article/Article";
+
+const { Text } = Typography;
+
+interface IWidget {
+  type?: ArticleType;
+  articleId?: string;
+  book?: string | null;
+  para?: string | null;
+  channelId?: string | null;
+  anthologyId?: string | null;
+}
+const ShareButtonWidget = ({
+  type,
+  book,
+  para,
+  channelId,
+  articleId,
+  anthologyId,
+}: IWidget) => {
+  const [exportOpen, setExportOpen] = useState(false);
+
+  return (
+    <>
+      <Dropdown
+        trigger={["click"]}
+        menu={{
+          items: [
+            {
+              label: (
+                <Space>
+                  {"Export"}
+                  <Text type="secondary" style={{ fontSize: "80%" }}>
+                    {"PDF,Word,Html"}
+                  </Text>
+                </Space>
+              ),
+              key: "export",
+              icon: <ExportOutlined />,
+            },
+          ],
+          onClick: ({ key }) => {
+            switch (key) {
+              case "export":
+                setExportOpen(true);
+                break;
+
+              default:
+                break;
+            }
+          },
+        }}
+      >
+        <Button type="text" icon={<ShareAltOutlined color="#fff" />} />
+      </Dropdown>
+      <ExportModal
+        type={type}
+        articleId={articleId}
+        book={book}
+        para={para}
+        channelId={channelId}
+        anthologyId={anthologyId}
+        open={exportOpen}
+        onClose={() => setExportOpen(false)}
+      />
+    </>
+  );
+};
+
+export default ShareButtonWidget;