作者 202304001

生成文章上下文

... ... @@ -452,45 +452,6 @@ export function BgPanel(props: PanelProps) {
return (
<>
<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
onClick={() => handleProcessImage("visual/segmentation")}
disabled={isLoading}
/>
<IconButton
text={Locale.BgRemoval.downloadImg}
type="primary"
shadow
onClick={handleDownload}
disabled={!previewUrl}
/>
<IconButton
text={Locale.BgRemoval.generateBg}
type="primary"
shadow
onClick={() => handleProcessImage("visual/r-background")}
disabled={isLoading}
/>
<IconButton
text={Locale.BgRemoval.generateBg}
type="primary"
shadow
onClick={() => console.log(Path.BgRemoval)}
disabled={isLoading}
/>
</div>
</ControlParamItem>
<ControlParamItem title={Locale.BgRemoval.promptTitle} required={true}>
<div className={styles["prompt"]}>
<textarea
... ... @@ -528,6 +489,38 @@ export function BgPanel(props: PanelProps) {
/>
)}
</ControlParamItem>
<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
onClick={() => handleProcessImage("visual/segmentation")}
disabled={isLoading}
/>
<IconButton
text={Locale.BgRemoval.downloadImg}
type="primary"
shadow
onClick={handleDownload}
disabled={!previewUrl}
/>
<IconButton
text={Locale.BgRemoval.generateBg}
type="primary"
shadow
onClick={() => handleProcessImage("visual/r-background")}
disabled={isLoading}
/>
</div>
</ControlParamItem>
</>
);
}
... ...
... ... @@ -102,7 +102,7 @@ export function MindPage() {
if (!content) return;
setIsLoading(true);
try {
const response = await chatStore.getMindData(newMessages, "gpt-4o-mini");
const response = await chatStore.sendContext(newMessages, "gpt-4o-mini");
const cleanedContent = response.replace(/^```json|```$/g, "");
const parsedData: MindElixirData = JSON.parse(cleanedContent);
// 增强校验逻辑
... ... @@ -115,7 +115,6 @@ export function MindPage() {
setData(parsedData);
navigate(Path.Mind, { replace: true, state: { msg: null } });
} catch (error) {
console.log(error);
message.error("请求失败,请重试");
} finally {
setIsLoading(false); // 确保关闭加载状态
... ...
export const maxWord = 5000;
export const minWord = 200;
export const rewriteItems = [
"全文润色",
"全文缩写",
"全文扩写",
"语气更专业",
"语气更口语化",
"语气更易读",
"语气更含蓄",
"语气更学术化",
"语气更有文采",
"语气更有网感",
];
export const mergedData = [
{
title: "写作用途",
required: true,
type: "select",
default: "公司官网",
options: [
{ name: "公司官网", value: "100%" },
{ name: "小红书", value: "400px" },
{ name: "微信公众号", value: "300px" },
{ name: "今日头条", value: "500px" },
],
},
{
title: "图片模式",
type: "select",
required: false,
default: "免费配图",
options: [
{ name: "免费配图", value: "free" },
{ name: "收费配图", value: "paid" },
],
},
{
title: "写作风格",
type: "select",
required: false,
default: "专业",
options: [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
],
},
{
title: "写作语言",
type: "select",
required: false,
default: "中文",
options: [
{ name: "中文", value: "Chinese" },
{ name: "英文", value: "English" },
{ name: "法文", value: "French" },
{ name: "德文", value: "German" },
],
},
{
title: "写作类型",
type: "select",
required: false,
default: "产品推广文案",
options: [
{ name: "产品推广文案", value: "promotion" },
{ name: "品牌宣传文案", value: "propagandize" },
{ name: "产品说明书", value: "instructionBook" },
{ name: "产品介绍", value: "introduce" },
],
},
{
title: "是否图文",
type: "select",
required: false,
default: "是",
options: [
{ name: "是", value: "true" },
{ name: "否", value: "false" },
],
},
];
// 20250408新增写作风格选项映射
export const getWritingStyleOptions = (purpose: string) => {
switch (purpose) {
case "公司官网":
return [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
];
case "小红书":
return [
{ name: "俏皮", value: "playful" },
{ name: "幽默", value: "humorous" },
];
case "微信公众号":
return [
{ name: "夸张", value: "exaggerated" },
{ name: "可爱", value: "cute" },
];
case "今日头条":
return [
{ name: "丰满", value: "full" },
{ name: "可爱", value: "cute" },
{ name: "健康", value: "healthy" },
];
default:
return [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
];
}
};
export interface writeMessage {
role: string;
content: string;
}
export interface WritePanelProps {
htmlCode: string;
setHtmlCode: React.Dispatch<React.SetStateAction<string>>;
loading: boolean;
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
setWidth: React.Dispatch<React.SetStateAction<string>>;
setHtmlheader: React.Dispatch<React.SetStateAction<string>>;
}
... ...
... ... @@ -3,7 +3,7 @@ import { ControlParamItem } from "../sd";
import { SideBarTail } from "@/app/components/sidebar";
import { useState } from "react";
import { IconButton } from "../button";
import { message } from "antd";
import { message as msgModal } from "antd";
import { useChatStore } from "@/app/store";
import { getWrtingPrompt } from "@/app/utils/prompt";
import type { writePromptParam } from "@/app/types/prompt";
... ... @@ -11,119 +11,15 @@ import { processChatFile } from "@/app/utils/fileUtil";
import Locale from "@/app/locales";
import styles from "./wrtie-panel.module.scss";
import DeleteIcon from "@/app/icons/delete.svg";
import {
getWritingStyleOptions,
maxWord,
mergedData,
minWord,
writeMessage,
WritePanelProps,
} from "./menuData";
export const mergedData = [
{
title: "写作用途",
required: true,
type: "select",
default: "公司官网",
options: [
{ name: "公司官网", value: "100%" },
{ name: "小红书", value: "400px" },
{ name: "微信公众号", value: "300px" },
{ name: "今日头条", value: "500px" },
],
},
{
title: "图片模式",
type: "select",
required: false,
default: "免费配图",
options: [
{ name: "免费配图", value: "free" },
{ name: "收费配图", value: "paid" },
],
},
{
title: "写作风格",
type: "select",
required: false,
default: "专业",
options: [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
],
},
{
title: "写作语言",
type: "select",
required: false,
default: "中文",
options: [
{ name: "中文", value: "Chinese" },
{ name: "英文", value: "English" },
{ name: "法文", value: "French" },
{ name: "德文", value: "German" },
],
},
{
title: "写作类型",
type: "select",
required: false,
default: "产品推广文案",
options: [
{ name: "产品推广文案", value: "promotion" },
{ name: "品牌宣传文案", value: "propagandize" },
{ name: "产品说明书", value: "instructionBook" },
{ name: "产品介绍", value: "introduce" },
],
},
{
title: "是否图文",
type: "select",
required: false,
default: "是",
options: [
{ name: "是", value: "true" },
{ name: "否", value: "false" },
],
},
];
// 20250408新增写作风格选项映射
const getWritingStyleOptions = (purpose: string) => {
switch (purpose) {
case "公司官网":
return [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
];
case "小红书":
return [
{ name: "俏皮", value: "playful" },
{ name: "幽默", value: "humorous" },
];
case "微信公众号":
return [
{ name: "夸张", value: "exaggerated" },
{ name: "可爱", value: "cute" },
];
case "今日头条":
return [
{ name: "丰满", value: "full" },
{ name: "可爱", value: "cute" },
{ name: "健康", value: "healthy" },
];
default:
return [
{ name: "专业", value: "professional" },
{ name: "活泼", value: "lively" },
{ name: "严谨", value: "strict" },
];
}
};
export interface WritePanelProps {
htmlCode: string;
setHtmlCode: React.Dispatch<React.SetStateAction<string>>;
loading: boolean;
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
setWidth: React.Dispatch<React.SetStateAction<string>>;
setHtmlheader: React.Dispatch<React.SetStateAction<string>>;
}
export function WritingPanel(props: WritePanelProps) {
const {
htmlCode,
... ... @@ -147,6 +43,7 @@ export function WritingPanel(props: WritePanelProps) {
const [fileData, setFileData] = useState("");
const [fileName, setFileName] = useState("");
const messages: writeMessage[] = [];
// 生成动态数据
const dynamicMergedData = mergedData.map((item) => {
... ... @@ -214,14 +111,14 @@ export function WritingPanel(props: WritePanelProps) {
setFileName(name);
} catch {
message.error(Locale.ComError.UploadErr);
msgModal.error(Locale.ComError.UploadErr);
}
};
// 提交表单时的处理函数
const handleSubmit = async () => {
if (!prompt.trim()) {
return message.error("请输入提示词");
return msgModal.error("请输入提示词");
}
try {
const param: writePromptParam = {
... ... @@ -237,7 +134,13 @@ export function WritingPanel(props: WritePanelProps) {
const input = getWrtingPrompt(param);
setLoading(true);
console.log("------------------------" + input);
const response = await chatStore.directLlmInvoke(input, "gpt-4o-mini");
messages.push({ role: "user", content: input });
const response = await chatStore.sendContext(messages, "gpt-4o-mini");
messages.push({ role: "assistant", content: response });
let cleanedContent = response.startsWith("```html")
? response.substring(8)
: response;
... ... @@ -256,9 +159,11 @@ export function WritingPanel(props: WritePanelProps) {
setHtmlheader(contentUpToBody); //保存html头部
}
localStorage.setItem("htmlCode", cleanedContent);
localStorage.setItem("aiWrite", JSON.stringify(messages));
setHtmlCode(cleanedContent);
} catch (error) {
message.error("生成失败,请重试");
msgModal.error("生成失败,请重试");
} finally {
setLoading(false);
}
... ... @@ -319,9 +224,9 @@ export function WritingPanel(props: WritePanelProps) {
<input
aria-label="写作字数"
type="number"
placeholder="200"
min="200"
max="5000"
placeholder={String(minWord)}
min={minWord}
max={maxWord}
value={writingCount}
onChange={handleInputChange}
/>
... ...
... ... @@ -39,14 +39,14 @@ import PptIcon from "@/app/icons/ppt.svg";
import PdfIcon from "@/app/icons/pdf.svg";
import HtmlIcon from "@/app/icons/HTML.svg";
import { message } from "antd";
import { Button, Dropdown, MenuProps, message as msgModal, Space } from "antd";
import { HTMLPreview } from "../artifacts";
import { getMindPrompt, getWrtingPrompt } from "@/app/utils/prompt";
import { htmlToPdf2 } from "@/app/utils/fileExport/toPdf";
import { hasTable, htmlToExcel } from "@/app/utils/fileExport/export2Excel";
import { writePromptParam } from "@/app/types/prompt";
import { mergedData } from "./writie-panel";
import dynamic from "next/dynamic";
import { rewriteItems, mergedData } from "./menuData";
const EditorComponent = dynamic(
async () => (await import("./editor")).EditorComponent,
... ... @@ -75,6 +75,23 @@ export function WritingPage() {
const query = useLocation(); //获取路由参数
let { msg, writeMessage } = query.state || {}; //获取路由参数
const items: MenuProps["items"] = rewriteItems.map((item, index) => ({
key: (index + 1).toString(),
label: (
<a
target="_blank"
rel="noopener noreferrer"
href="#"
onClick={(e) => {
e.preventDefault(); // 阻止默认行为
rewrite(item); // 调用 rewrite 函数并传递菜单项文本
}}
>
{item}
</a>
),
}));
useEffect(() => {
if (!msg) {
return;
... ... @@ -121,7 +138,7 @@ export function WritingPage() {
localStorage.setItem("htmlCode", cleanedContent);
setHtmlCode(cleanedContent);
} catch (error) {
message.error("生成失败,请重试");
msgModal.error("生成失败,请重试");
} finally {
setLoading(false);
}
... ... @@ -159,11 +176,9 @@ export function WritingPage() {
const blob = new Blob([htmlCode], { type: "text/html" });
const clipboardItem = new ClipboardItem({ "text/html": blob });
await navigator.clipboard.write([clipboardItem]);
message.success("复制成功!");
msgModal.success("复制成功!");
} catch (error) {
console.log(error);
message.error("复制失败");
msgModal.error("复制失败");
}
};
//跳转到ppt页面
... ... @@ -211,12 +226,67 @@ export function WritingPage() {
a.download = fileName;
a.click();
URL.revokeObjectURL(url);
message.success("导出成功");
msgModal.success("导出成功");
} catch (error) {
message.error("导出失败");
msgModal.error("导出失败");
}
}, [wrapContentInDivWithWidth]);
async function rewrite(msg: string) {
const messagesStr = localStorage.getItem("aiWrite");
if (!messagesStr) return;
let messages: any;
try {
messages = JSON.parse(messagesStr);
} catch (error) {
return;
}
// 检查是否是数组且包含合法 message 对象
if (!Array.isArray(messages)) return;
if (
!messages.every(
(m) =>
typeof m === "object" &&
m !== null &&
"role" in m &&
"content" in m &&
typeof m.role === "string" &&
typeof m.content === "string",
)
)
return;
try {
setLoading(true);
const response = await chatStore.sendContext(messages, "gpt-4o-mini");
messages.push({ role: "assistant", content: response });
let cleanedContent = response.startsWith("```html")
? response.substring(8)
: response;
if (cleanedContent.endsWith("```")) {
cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4);
}
const bodyTagRegex = /<body[^>]*>/i;
const bodyTagMatch = cleanedContent.match(bodyTagRegex);
if (bodyTagMatch?.index !== undefined) {
setHtmlheader(
cleanedContent.slice(0, bodyTagMatch.index + bodyTagMatch[0].length),
);
}
localStorage.setItem("htmlCode", cleanedContent);
localStorage.setItem("aiWrite", JSON.stringify(messages));
setHtmlCode(cleanedContent);
} catch (error) {
msgModal.error("重写失败,请重试");
} finally {
setLoading(false);
}
}
useEffect(() => {
localStorage.setItem("htmlCode", htmlCode);
}, [htmlCode]);
... ... @@ -257,12 +327,18 @@ export function WritingPage() {
<div className={chatStyles["chat-message-actions"]}>
{htmlCode && (
<div className={chatStyles["chat-input-actions"]}>
<ChatAction
text={Locale.Chat.Actions.ReWrite}
icon={<ReloadIcon />}
onClick={() => {}}
disabled={isEdit}
/>
<Dropdown
menu={{ items }}
placement="bottom"
arrow={{ pointAtCenter: true }}
>
<Button>
<Space>
<ReloadIcon />
{Locale.Chat.Actions.ReWrite}
</Space>
</Button>
</Dropdown>
<ChatAction
text={Locale.Chat.Actions.Copy}
icon={<CopyIcon />}
... ...
... ... @@ -895,7 +895,7 @@ export const useChatStore = createPersistStore(
});
},
async getMindData(
async sendContext(
messages: Array<{ role: string; content: string }>,
model: string,
): Promise<string> {
... ...
... ... @@ -12,8 +12,6 @@ export function getWrtingPrompt(param: writePromptParam): string {
writingCount,
fileData,
} = param;
console.log("********************", fileData);
console.log("**********************", prompt);
// 根据用途调整文案类型描述
const purposeMap: Record<string, string> = {
... ...