powerpoint.tsx 5.6 KB
import styles from "./powerpoint.module.scss";
import chatStyles from "@/app/components/chat.module.scss";
import clsx from "clsx";

import { IconButton } from "../button";
import Locale from "@/app/locales";
import { useNavigate, useLocation } from "react-router-dom";
import { useMobileScreen } from "@/app/utils";
import { Path } from "@/app/constant";
import { useRef, useEffect, useMemo } from "react";
import { CreatorType, DocmeeUI } from "@docmee/sdk-ui";
import { getClientConfig } from "@/app/config/client";
import { useAppConfig, useAccessStore } from "@/app/store";
import type { generateError, generateOutline } from "@/app/types/docmee";
import axios from "axios";
import ReturnIcon from "@/app/icons/return.svg";
import MinIcon from "@/app/icons/min.svg";
import MaxIcon from "@/app/icons/max.svg";
import { message } from "antd";

// 错误消息映射
const errorMap: { [key: number]: string } = {
  [-1]: "操作失败",
  [88]: "功能受限(积分已用完 或 非VIP)",
  [98]: "认证失败(检查token是否过期)",
  [99]: "登录过期",
  [1001]: "数据不存在",
  [1002]: "数据访问异常",
  [1003]: "无权限访问",
  [1006]: "内容涉及敏感信息",
  [1009]: "AI服务异常",
  [1010]: "你的次数已用完",
  [1012]: "请求太频繁,限流",
};

// 获取错误消息的函数
const getErrorMessage = (errorCode: number): string => {
  return errorMap[errorCode] || `未知错误(错误码:${errorCode})`;
};

export function PowerPoint() {
  const accessStore = useAccessStore();
  const containerRef = useRef<HTMLDivElement>(null);
  const isPowerpoint = location.pathname === Path.Powerpoint;
  const isMobileScreen = useMobileScreen();
  const navigate = useNavigate();
  const clientConfig = useMemo(() => getClientConfig(), []);
  const showMaxIcon = !isMobileScreen && !clientConfig?.isApp;
  const config = useAppConfig();
  const scrollRef = useRef<HTMLDivElement>(null);

  const query = useLocation();
  const { msg, pptMessage } = query.state || {};

  const getToken = async () => {
    if (!accessStore.accessCode) {
      return message.error("请先输入登录秘钥");
    }
    const res = await fetch("/api/ppt/createApiToken", {
      method: "POST",
      body: JSON.stringify(accessStore.accessCode),
    });
    const data = await res.json();
    console.log(data);
    if (data.status == 200) {
      return data.data.data.token;
    } else {
      message.error(data.error || "获取 token 失败");
    }
    return "";
  };

  useEffect(() => {
    const initializeDocmee = async () => {
      let token = localStorage.getItem("token");
      // 如果本地没有token,则获取新token
      if (!token) {
        token = await getToken();
        if (!token) {
          message.error("无效token请检查登录密码!");
          return navigate(Path.Settings); // 跳转回聊天页
        }
        localStorage.setItem("token", token);
      }
      if (!containerRef.current) {
        throw new Error("Container element not found");
      }
      const docmee = new DocmeeUI({
        container: containerRef.current,
        page: "creator-v2",
        token: token,
        mode: "light",
        lang: "zh",
        isMobile: isMobileScreen,
        creatorData: {
          type: CreatorType.AI_GEN,
          subject: "Ai行业未来10年的发展预测",
        },
      });
      if (msg) {
        docmee.on("mounted", (msg: generateOutline) => {
          docmee.changeCreatorData({ text: pptMessage }, true);
        });
      }
      docmee.on("beforeGenerate", (msg: generateOutline) => {
        axios.post("https://docmee.cn/api/ppt/v2/generateContent", msg, {
          headers: { token: token },
        });
      });
      docmee.on("error", (msg: generateError) => {
        if (msg.data.code == 98) {
          docmee.updateToken(token);
        }
        message.error(msg.data.message);
      });
      return () => docmee.destroy();
    };
    initializeDocmee().catch(console.error);
  }, [navigate, isMobileScreen, msg, pptMessage]);

  return (
    <>
      <div style={{ width: "100%", height: "100%" }}>
        <div className={chatStyles.chat} key={"1"}>
          <div className="window-header" data-tauri-drag-region>
            <div
              className={clsx(
                "window-header-title",
                chatStyles["chat-body-title"],
              )}
            >
              <div className={`window-header-main-title`}>PPT制作</div>
            </div>
            <div className={chatStyles["chat-message-actions"]}>
              <div className={chatStyles["chat-input-actions"]}></div>
            </div>
            <div className="window-actions">
              <IconButton
                aria="返回首页"
                icon={<ReturnIcon />}
                bordered
                title={Locale.Chat.Actions.ChatList}
                onClick={() => navigate(Path.Chat)}
              />
              {showMaxIcon && (
                <div className="window-action-button">
                  <IconButton
                    aria={Locale.Chat.Actions.FullScreen}
                    icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
                    bordered
                    onClick={() => {
                      config.update(
                        (config) => (config.tightBorder = !config.tightBorder),
                      );
                    }}
                  />
                </div>
              )}
            </div>
          </div>
          <div className={chatStyles["chat-body"]} ref={scrollRef}>
            <div ref={containerRef} className={styles["container"]}></div>
          </div>
        </div>
      </div>
    </>
  );
}