作者 202304001

新增生成PPT功能,新增生成图片功能

... ... @@ -16,7 +16,10 @@ import { handle as xaiHandler } from "../../xai";
import { handle as chatglmHandler } from "../../glm";
import { handle as proxyHandler } from "../../proxy";
//20250321 新增佐糖API
import {handle as zuotangHandler} from "../../zuotang";
import { handle as zuotangHandler } from "../../zuotang";
//20250328新增PPT API
import { handle as docmeeHandler } from "../../docmee";
import { handle as generateImgHandler } from "../../generateImg";
async function handle(
req: NextRequest,
... ... @@ -55,7 +58,11 @@ async function handle(
case ApiPath.OpenAI:
return openaiHandler(req, { params });
case ApiPath.ZuoTang:
return zuotangHandler(req,{ params })
return zuotangHandler(req, { params });
case ApiPath.Docmee:
return docmeeHandler(req, { params });
case ApiPath.OpenAiImg:
return generateImgHandler(req, { params });
default:
return proxyHandler(req, { params });
}
... ...
import { getServerSideConfig } from "@/app/config/server";
import { NextRequest, NextResponse } from "next/server";
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const config = getServerSideConfig();
const baseUrl = config.docmeeUrl;
const subPath = params.path.join("/");
// if(subPath==='createApiToken'){const reqUrl = `${baseUrl}/api/user/${subPath}`;}
const reqUrl = `${baseUrl}/api/user/${subPath}`;
console.log(reqUrl);
try {
// 获取 accessCode
const body = await new Response(req.body).text();
const uid = JSON.parse(body);
const headers = new Headers({
"Api-Key": config.docmeeApiKey,
"Content-Type": "application/json",
});
console.log("********************" + config.docmeeApiKey);
// 使用 async/await 处理 fetch 请求
const response = await fetch(reqUrl, {
headers,
method: "POST",
body: JSON.stringify({ uid: uid, limit: config.docmeeMaxDailyUses }),
});
if (!response.ok) {
throw new Error(
`HTTP error! status: ${response.status} ${response.text()}`,
);
}
const data = await response.json();
console.log("-----------------data", data);
// 返回固定的 token
return NextResponse.json({
data: data,
status: 200,
});
} catch (error) {
console.error("处理请求时出错:", error);
return NextResponse.json({
data: "处理请求时出错",
status: 500,
});
}
}
... ...
import { getServerSideConfig } from "@/app/config/server";
import { NextRequest, NextResponse } from "next/server";
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const config = getServerSideConfig();
const baseUrl = config.baseUrl;
const subPath = params.path.join("/");
// if(subPath==='createApiToken'){const reqUrl = `${baseUrl}/api/user/${subPath}`;}
const reqUrl = `${baseUrl}/v1/images/${subPath}`;
const apiKey = config.apiKey;
try {
const headers = new Headers({
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
});
const body = await new Response(req.body).text();
const prompt = JSON.parse(body);
const response = await fetch(reqUrl, {
headers,
method: "POST",
body: JSON.stringify({
model: "dall-e-3",
prompt: prompt,
n: 1,
size: "1024x1024",
}),
});
const data = await response.json();
return NextResponse.json({
data: data,
status: 200,
});
} catch {
return NextResponse.json({
data: "处理请求时出错",
status: 500,
});
}
}
... ...
... ... @@ -30,7 +30,6 @@ export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("------------------------------------进入opanai handle")
console.log("[OpenAI Route] params ", params);
if (req.method === "OPTIONS") {
... ... @@ -61,7 +60,6 @@ export async function handle(
try {
const response = await requestOpenai(req);
// list models
if (subpath === OpenaiPath.ListModelPath && response.status === 200) {
const resJson = (await response.json()) as OpenAIListModelResponse;
... ...
import { getServerSideConfig } from "@/app/config/server";
import type { CreateTaskResponse, GetTaskResponse, GetGenerateTaskResponse, LocalData } from "../types/zuotang";
import type {
CreateTaskResponse,
GetTaskResponse,
GetGenerateTaskResponse,
LocalData,
} from "../types/zuotang";
import { NextRequest, NextResponse } from "next/server";
import { error } from "console";
import { message } from "antd";
import md5 from "spark-md5";
// 类型守卫函数
function isString(value: unknown): value is string {
return typeof value === "string";
return typeof value === "string";
}
// 统一错误响应生成器
function createErrorResponse(
message: string,
status: number,
maxDailyUses?: number
message: string,
status: number,
maxDailyUses?: number,
): NextResponse<CreateTaskResponse> {
const response: CreateTaskResponse = {
status,
message,
data: { task_id: "" },
maxDailyUses,
};
return NextResponse.json(response, { status, headers: { "Content-Type": "application/json" } });
const response: CreateTaskResponse = {
status,
message,
data: { task_id: "" },
maxDailyUses,
};
return NextResponse.json(response, {
status,
headers: { "Content-Type": "application/json" },
});
}
// 处理每日使用限制逻辑
function parseDailyUsage(allowNum: string, configMax: number): number {
if (allowNum === "first") return configMax;
const parsed = parseInt(allowNum, 10);
return Number.isNaN(parsed) ? configMax : parsed;
if (allowNum === "first") return configMax;
const parsed = parseInt(allowNum, 10);
return Number.isNaN(parsed) ? configMax : parsed;
}
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } }
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const config = getServerSideConfig();
const baseUrl = config.bgRemovalUrl;
const subPath = params.path.join("/");
const reqUrl = `${baseUrl}/api/tasks/${subPath}`;
try {
if (req.method === "POST") {
const formData = await req.formData();
// 验证访问码
const accessCode = formData.get("accessCode");
if (!isString(accessCode) || !config.codes.has(md5.hash(accessCode))) {
return createErrorResponse("无效访问密码!", 401);
}
// 解析使用限制数据
const localData = formData.get("localData");
if (!isString(localData)) {
return createErrorResponse("无效请求参数", 400);
}
const localDataObj: LocalData = JSON.parse(localData);
const maxDailyUses = parseDailyUsage(localDataObj.maxDailyUses, config.maxDailyUses);
if (maxDailyUses <= 0) {
return createErrorResponse("今日次数已用完!", 429, 0);
}
// 准备API请求
const imageFile = formData.get("image_file");
if (!(imageFile instanceof Blob)) {
return createErrorResponse("无效图片文件", 400);
}
const headers = new Headers({ "X-API-KEY": config.bgRemovalApiKey });
const newFormData = new FormData();
newFormData.append("image_file", imageFile);
if (subPath === "visual/r-background") {
newFormData.append("batch_size", "1");
const prompt = formData.get("prompt") as string;
const trimmedPrompt = prompt ? prompt.trim() : null;
if (!trimmedPrompt) {
return createErrorResponse("背景提示词不能为空!", 400);
}
newFormData.append("prompt", trimmedPrompt);
}
const response = await fetch(reqUrl, {
headers,
method: "POST",
body: newFormData,
});
if (!response.ok) {
throw new Error(`API请求失败: ${response.statusText}`);
}
const responseData: CreateTaskResponse = await response.json();
responseData.maxDailyUses = maxDailyUses - 1;
return NextResponse.json(responseData, {
status: response.status,
headers: { "Content-Type": "application/json" },
});
} else if (req.method === "GET") {
const headers = { "X-API-KEY": config.bgRemovalApiKey };
const response = await fetch(reqUrl, { headers });
if (!response.ok) {
throw new Error(`API请求失败: ${response.statusText}`);
}
const isVisualRoute = subPath.includes("visual/r-background");
const responseData = isVisualRoute
? (await response.json() as GetTaskResponse)
: (await response.json() as GetGenerateTaskResponse);
return NextResponse.json(responseData, {
status: response.status,
headers: { "Content-Type": "application/json" },
});
const config = getServerSideConfig();
const baseUrl = config.bgRemovalUrl;
const subPath = params.path.join("/");
const reqUrl = `${baseUrl}/api/tasks/${subPath}`;
try {
if (req.method === "POST") {
const formData = await req.formData();
// 验证访问码
const accessCode = formData.get("accessCode");
if (!isString(accessCode) || !config.codes.has(md5.hash(accessCode))) {
return createErrorResponse("无效访问密码!", 401);
}
// 解析使用限制数据
const localData = formData.get("localData");
if (!isString(localData)) {
return createErrorResponse("无效请求参数", 400);
}
const localDataObj: LocalData = JSON.parse(localData);
const maxDailyUses = parseDailyUsage(
localDataObj.maxDailyUses,
config.maxDailyUses,
);
if (maxDailyUses <= 0) {
return createErrorResponse("今日次数已用完!", 429, 0);
}
// 准备API请求
const imageFile = formData.get("image_file");
if (!(imageFile instanceof Blob)) {
return createErrorResponse("无效图片文件", 400);
}
const headers = new Headers({ "X-API-KEY": config.bgRemovalApiKey });
const newFormData = new FormData();
newFormData.append("image_file", imageFile);
if (subPath === "visual/r-background") {
newFormData.append("batch_size", "1");
const prompt = formData.get("prompt") as string;
const trimmedPrompt = prompt ? prompt.trim() : null;
if (!trimmedPrompt) {
return createErrorResponse("背景提示词不能为空!", 400);
}
return createErrorResponse("方法不允许", 405);
} catch (error) {
console.error("请求处理错误:", error);
return createErrorResponse(
error instanceof Error ? error.message : "服务器内部错误",
500
);
newFormData.append("prompt", trimmedPrompt);
}
const response = await fetch(reqUrl, {
headers,
method: "POST",
body: newFormData,
});
if (!response.ok) {
throw new Error(`API请求失败: ${response.statusText}`);
}
const responseData: CreateTaskResponse = await response.json();
responseData.maxDailyUses = maxDailyUses - 1;
return NextResponse.json(responseData, {
status: response.status,
headers: { "Content-Type": "application/json" },
});
} else if (req.method === "GET") {
const headers = { "X-API-KEY": config.bgRemovalApiKey };
const response = await fetch(reqUrl, { headers });
if (!response.ok) {
throw new Error(`API请求失败: ${response.statusText}`);
}
const isVisualRoute = subPath.includes("visual/r-background");
const responseData = isVisualRoute
? ((await response.json()) as GetTaskResponse)
: ((await response.json()) as GetGenerateTaskResponse);
return NextResponse.json(responseData, {
status: response.status,
headers: { "Content-Type": "application/json" },
});
}
return createErrorResponse("方法不允许", 405);
} catch (error) {
console.error("请求处理错误:", error);
return createErrorResponse(
error instanceof Error ? error.message : "服务器内部错误",
500,
);
}
}
... ...
... ... @@ -278,6 +278,25 @@ export function BgPanel(props: FileProps) {
const { pollTask } = useTaskPoller();
const { updateLocalUsage, getLocalData } = useLocalStorage();
const handleGenerateImg = async () => {
if (!prompt.trim()) {
return message.error("请先输入提示词");
}
setIsLoading(true);
const res = await fetch(`${ApiPath.OpenAiImg}/generations`, {
method: "POST",
body: JSON.stringify(prompt),
});
if (!res.ok) {
const errorData = await res.json();
throw new Error(errorData.message || Locale.BgRemoval.error.reqErr);
}
const responseData = await res.json();
console.log(responseData.data.data[0]);
setPreviewUrl(responseData.data.data[0].url);
setIsLoading(false);
};
const handleApiRequest = async (endpoint: string) => {
if (!previewUrl) {
message.error(Locale.BgRemoval.error.selectImg);
... ... @@ -391,6 +410,13 @@ export function BgPanel(props: FileProps) {
<ControlParamItem title={Locale.BgRemoval.subTitle}>
<div className={styles["ai-models"]}>
<IconButton
text={Locale.BgRemoval.generateImg}
type="primary"
shadow
onClick={handleGenerateImg}
disabled={isLoading}
/>
<IconButton
text={Locale.BgRemoval.bgRemoveBtn}
type="primary"
shadow
... ...
... ... @@ -53,6 +53,7 @@ import HeadphoneIcon from "../icons/headphone.svg";
import ExcelIcon from "../icons/excel.svg";
import WordIcon from "../icons/word.svg";
import MindIcon from "../icons/mind.svg";
import PptIcon from "../icons/ppt.svg";
import {
BOT_HELLO,
... ... @@ -1739,6 +1740,11 @@ function _Chat() {
navigate("/mind", { state: { msg: true } });
}
//20250328新增PPT导出
function toPowerpoint(pptMessage: string) {
navigate("/powerpoint", { state: { pptMessage: pptMessage, msg: true } });
}
return (
<>
<div className={styles.chat} key={session.id}>
... ... @@ -2019,6 +2025,15 @@ function _Chat() {
/>
</>
)}
<ChatAction
text={Locale.Chat.Actions.Ppt}
icon={<PptIcon />}
onClick={() =>
toPowerpoint(
getMessageTextContent(message),
)
}
/>
{/* 以上 20250317 新增Word excel导出按钮 */}
{config.ttsConfig.enable && (
<ChatAction
... ...
... ... @@ -86,21 +86,29 @@ const McpMarketPage = dynamic(
loading: () => <Loading noLogo />,
},
);
//以下新增思维导图路由,抠图路由 20250319
//以下新增思维导图路由20250319
const Mind = dynamic(async () => (await import("./mind")).MindPage, {
loading: () => <Loading noLogo />,
});
//以下新增抠图页面20250319
const BgRemoval = dynamic(async () => (await import("./bgRemoval")).BgRemoval, {
loading: () => <Loading noLogo />,
});
//以下新增写作页面20250325
const WritingPage = dynamic(
async () => (await import("./writing")).WritingPage,
{
loading: () => <Loading noLogo />,
},
);
//以下新增PPT制作 页面 20250328
const PowerPoint = dynamic(
async () => (await import("./powerpoint")).PowerPoint,
{
loading: () => <Loading noLogo />,
},
);
export function useSwitchTheme() {
const config = useAppConfig();
... ... @@ -189,6 +197,7 @@ function Screen() {
const isMind = location.pathname === Path.Mind;
const isBgRemoval = location.pathname === Path.BgRemoval;
const isWrting = location.pathname === Path.Writing;
const isPowerpoint = location.pathname === Path.Powerpoint;
const isMobileScreen = useMobileScreen();
const shouldTightBorder =
... ... @@ -215,6 +224,8 @@ function Screen() {
if (isBgRemoval) return <BgRemoval />;
//20250325新增AI写作界面
if (isWrting) return <WritingPage />;
//20250328新增ppt制作页面
if (isPowerpoint) return <PowerPoint />;
return (
<>
... ...
... ... @@ -49,6 +49,8 @@ export function MindPage() {
// 初始化配置项
const options: Options = {
el: containerRef.current,
locale: "zh_CN",
draggable: true,
contextMenu: true,
toolBar: true,
nodeMenu: true,
... ... @@ -66,7 +68,6 @@ export function MindPage() {
newMessages,
"gpt-4o-mini",
);
console.log("原始响应:", response);
let cleanedContent = response.startsWith("```json")
? response.substring(8)
: response;
... ... @@ -77,7 +78,6 @@ export function MindPage() {
);
}
const parsedData: MindElixirData = JSON.parse(cleanedContent);
console.log("解析后响应:", parsedData);
// 增强校验逻辑
if (
!parsedData?.nodeData?.id ||
... ...
export * from "./powerpoint";
... ...
.container{
width: 100%;
height: 100%;
}
\ No newline at end of file
... ...
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>
</>
);
}
... ...
import { useMobileScreen } from "@/app/utils";
import dynamic from "next/dynamic";
import {
SideBarContainer,
SideBarHeader,
useDragSideBar,
useHotKey,
} from "@/app/components/sidebar";
import { IconButton } from "@/app/components/button";
import ReturnIcon from "@/app/icons/return.svg";
import HistoryIcon from "@/app/icons/history.svg";
import Locale from "@/app/locales";
import { Path } from "@/app/constant";
import { useNavigate } from "react-router-dom";
import SDIcon from "@/app/icons/sd.svg";
const MindPanel = dynamic(
async () => (await import("@/app/components/mind")).MindPanel,
{
loading: () => null,
},
);
export function PptSiderBar(props: { className?: string }) {
const isMobileScreen = useMobileScreen();
const { onDragStart, shouldNarrow } = useDragSideBar();
const navigate = useNavigate();
useHotKey();
return (
<>
<SideBarContainer
onDragStart={onDragStart}
shouldNarrow={shouldNarrow}
{...props}
>
{isMobileScreen ? (
<div
className="window-header"
data-tauri-drag-region
style={{
paddingLeft: 0,
paddingRight: 0,
}}
>
<div className="window-actions">
<div className="window-action-button">
<IconButton
icon={<ReturnIcon />}
bordered
title={Locale.Sd.Actions.ReturnHome}
onClick={() => navigate(Path.Home)}
/>
</div>
</div>
<SDIcon width={50} height={50} />
<div className="window-actions">
<div className="window-action-button">
<IconButton
icon={<HistoryIcon />}
bordered
title={Locale.Sd.Actions.History}
onClick={() => navigate(Path.SdNew)}
/>
</div>
</div>
</div>
) : (
<SideBarHeader
title={
<IconButton
icon={<ReturnIcon />}
bordered
title={Locale.Sd.Actions.ReturnHome}
onClick={() => navigate(Path.Home)}
/>
}
></SideBarHeader>
)}
</SideBarContainer>
</>
);
}
... ...
... ... @@ -685,6 +685,10 @@ export function Settings() {
type="text"
placeholder={Locale.Settings.Access.AccessCode.Placeholder}
onChange={(e) => {
console.log("更改密码了");
if (localStorage.getItem("token")) {
localStorage.removeItem("token");
} //20250328新增更改访问密码删除本地储存token
accessStore.update(
(access) => (access.accessCode = e.currentTarget.value),
);
... ...
... ... @@ -38,8 +38,9 @@ const DISCOVERY = [
{ name: "Stable Diffusion", path: Path.Sd },
{ name: Locale.SearchChat.Page.Title, path: Path.SearchChat },
{ name: "智能抠图", path: Path.BgRemoval },
{ name: "AI-writing", path: Path.Writing },
{ name: "AI-Writing", path: Path.Writing },
{ name: "思维导图", path: Path.Mind },
{ name: "AI-PPT", path: Path.Powerpoint },
];
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
... ...
... ... @@ -98,11 +98,14 @@ declare global {
BACKGROUND_REMOVAL_API_KEY: string;
MAX_DAILY_USES: number;
NXET_PUBLIC_BGREMOVAL_MODEL : string;
NXET_PUBLIC_WRITING_MODEL :string;
DOCMEE_URL: string;
DOCMEE_API_KEY: string;
NXET_PUBLIC_BGREMOVAL_MODEL: string;
NXET_PUBLIC_WRITING_MODEL: string;
}
}
}
}
const ACCESS_CODES = (function getAccessCodes(): Set<string> {
const code = process.env.CODE;
... ... @@ -124,7 +127,8 @@ function getApiKey(keys?: string) {
const apiKey = apiKeys[randomIndex];
if (apiKey) {
console.log(
`[Server Config] using ${randomIndex + 1} of ${apiKeys.length
`[Server Config] using ${randomIndex + 1} of ${
apiKeys.length
} api key - ${apiKey}`,
);
}
... ... @@ -277,8 +281,12 @@ export const getServerSideConfig = () => {
bgRemovalUrl: process.env.BACKGROUND_REMOVAL_URL ?? "",
bgRemovalApiKey: process.env.BACKGROUND_REMOVAL_API_KEY ?? "",
maxDailyUses: process.env.MAX_DAILY_USES,
//20250328新增 ppt api
docmeeUrl: process.env.DOCMEE_URL,
docmeeApiKey: process.env.DOCMEE_API_KEY ?? "",
docmeeMaxDailyUses: process.env.DOCMEE_MAX_DAILY_USES,
bgRemovalModel : process.env.NXET_PUBLIC_BGREMOVAL_MODEL,
bgRemovalModel: process.env.NXET_PUBLIC_BGREMOVAL_MODEL,
writingModel: process.env.NXET_PUBLIC_WRITING_MODEL,
};
};
... ...
... ... @@ -54,8 +54,9 @@ export enum Path {
McpMarket = "/mcp-market",
//20250317新增路由 思维导图
Mind = "/mind",
BgRemoval="/background-removal",
Writing="/aiWriting"
BgRemoval = "/background-removal",
Writing = "/aiWriting",
Powerpoint = "/powerpoint",
}
export enum ApiPath {
... ... @@ -77,7 +78,9 @@ export enum ApiPath {
DeepSeek = "/api/deepseek",
SiliconFlow = "/api/siliconflow",
//20250321 新增佐糖API
ZuoTang="/api/tasks"
ZuoTang = "/api/tasks",
Docmee = "/api/ppt",
OpenAiImg = "/api/v1",
}
///api/tasks/visual/segmentation
//api/tasks/visual/segmentation/{task_id}
... ...
import { title } from "process";
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
import { SAAS_CHAT_UTM_URL } from "@/app/constant";
... ... @@ -62,11 +61,11 @@ const cn = {
//20250317新增
Word: "导出Word",
Excel: "下载Excel",
Pdf:"导出PDF",
Ppt:"导出PPT",
Pdf: "导出PDF",
Ppt: "导出PPT",
Mind: "生成思维导图",
Drag: "拖动模式",
ReWrite:"重写",
ReWrite: "重写",
},
Commands: {
new: "新建聊天",
... ... @@ -860,26 +859,27 @@ const cn = {
},
// 20250320新增
BgRemoval:{
Title:"智能抠图",
subTitle:"AI抠图",
error:{
reqErr:"请求失败",
selectImg:"请选择图片",
code:"请先输入访问密码",
prompt:"请输入背景提示词",
resultErr:"结果图片加载失败",
downLoadErr:"请先完成图片处理",
statuErr:"状态查询失败",
timeoutErr:"处理超时,请稍后重试",
imgLoadingErr:"图片加载失败",
},
success:"图片处理成功,请在一小时内保存图片!",
bgRemoveBtn:"一键抠图",
downloadImg:"下载图片",
generateBg:"生成背景",
promptTitle:"背景提示词",
}
BgRemoval: {
Title: "智能抠图",
subTitle: "AI抠图",
error: {
reqErr: "请求失败",
selectImg: "请选择图片",
code: "请先输入访问密码",
prompt: "请输入背景提示词",
resultErr: "结果图片加载失败",
downLoadErr: "请先完成图片处理",
statuErr: "状态查询失败",
timeoutErr: "处理超时,请稍后重试",
imgLoadingErr: "图片加载失败",
},
success: "图片处理成功,请在一小时内保存图片!",
generateImg: "生成图片",
bgRemoveBtn: "一键抠图",
downloadImg: "下载图片",
generateBg: "生成背景",
promptTitle: "背景提示词",
},
};
type DeepPartial<T> = T extends object
... ...
export type generateOutline = {
data: {
fields: {
enableWeb: boolean;
length: string;
prompt: string;
subject: string;
_lang: string;
text?: string;
file?: File;
website?: string;
outline?: File;
};
subtype: string;
};
type: string;
};
export type generateError = {
data: {
code: number;
message: string;
};
type: string;
};
... ...
... ... @@ -21,6 +21,7 @@
"test:ci": "node --no-warnings --experimental-vm-modules $(yarn bin jest) --ci"
},
"dependencies": {
"@docmee/sdk-ui": "^1.1.17",
"@fortaine/fetch-event-source": "^3.0.6",
"@hello-pangea/dnd": "^16.5.0",
"@modelcontextprotocol/sdk": "^1.0.4",
... ...
... ... @@ -1371,6 +1371,13 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@docmee/sdk-ui@^1.1.17":
version "1.1.17"
resolved "https://registry.npmmirror.com/@docmee/sdk-ui/-/sdk-ui-1.1.17.tgz#549ee8b20dfe07eada422e9943b651d5a5196dd0"
integrity sha512-K/pWu2tg9ZrE9wbI5Naylh+LVd86kwMG7A9pu5urN1XjV+MAHj7ruOMrmqMkGbN5fVfFN8DbxWx4yUMcCMHTxA==
dependencies:
query-string "^9.1.1"
"@emotion/hash@^0.8.0":
version "0.8.0"
resolved "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz"
... ... @@ -4266,6 +4273,11 @@ decode-named-character-reference@^1.0.0:
dependencies:
character-entities "^2.0.0"
decode-uri-component@^0.4.1:
version "0.4.1"
resolved "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5"
integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==
dedent@^1.0.0:
version "1.5.3"
resolved "https://registry.npmmirror.com/dedent/-/dedent-1.5.3.tgz"
... ... @@ -5136,6 +5148,11 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
filter-obj@^5.1.0:
version "5.1.0"
resolved "https://registry.npmmirror.com/filter-obj/-/filter-obj-5.1.0.tgz#5bd89676000a713d7db2e197f660274428e524ed"
integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==
find-cache-dir@^3.3.1:
version "3.3.2"
resolved "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz"
... ... @@ -8035,6 +8052,15 @@ pure-rand@^6.0.0:
resolved "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz"
integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==
query-string@^9.1.1:
version "9.1.1"
resolved "https://registry.npmmirror.com/query-string/-/query-string-9.1.1.tgz#dbfebb4196aeb2919915f2b2b81b91b965cf03a0"
integrity sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==
dependencies:
decode-uri-component "^0.4.1"
filter-obj "^5.1.0"
split-on-first "^3.0.0"
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz"
... ... @@ -9068,6 +9094,11 @@ spawn-command@0.0.2:
resolved "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz"
integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==
split-on-first@^3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7"
integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz"
... ...