1、对话界面增加:生成文章
2、增加深度思考中 3、导出过滤思考内容 4、增加 风格 是 用途的 二级联动
正在显示
11 个修改的文件
包含
435 行增加
和
93 行删除
@@ -55,6 +55,7 @@ import WordIcon from "../icons/word.svg"; | @@ -55,6 +55,7 @@ import WordIcon from "../icons/word.svg"; | ||
55 | import MindIcon from "../icons/mind.svg"; | 55 | import MindIcon from "../icons/mind.svg"; |
56 | import PptIcon from "../icons/ppt.svg"; | 56 | import PptIcon from "../icons/ppt.svg"; |
57 | import FileIcon from "../icons/file.svg"; | 57 | import FileIcon from "../icons/file.svg"; |
58 | +import WriteIcon from "../icons/write.svg"; | ||
58 | 59 | ||
59 | import { | 60 | import { |
60 | BOT_HELLO, | 61 | BOT_HELLO, |
@@ -139,8 +140,9 @@ import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions"; | @@ -139,8 +140,9 @@ import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions"; | ||
139 | import { getExcelData, toExcel } from "../utils/fileExport/export2Excel"; | 140 | import { getExcelData, toExcel } from "../utils/fileExport/export2Excel"; |
140 | import { exportWord, getWordData } from "../utils/fileExport/word"; | 141 | import { exportWord, getWordData } from "../utils/fileExport/word"; |
141 | import { getMindPrompt } from "../utils/prompt"; | 142 | import { getMindPrompt } from "../utils/prompt"; |
142 | -import { message } from "antd"; | 143 | +import { message as msgModal } from "antd"; |
143 | import { getPdfData } from "../utils/fileExport/toPdf"; | 144 | import { getPdfData } from "../utils/fileExport/toPdf"; |
145 | +import { removeDeepThink } from "../utils/deepThink"; | ||
144 | const localStorage = safeLocalStorage(); | 146 | const localStorage = safeLocalStorage(); |
145 | 147 | ||
146 | const ttsPlayer = createTTSPlayer(); | 148 | const ttsPlayer = createTTSPlayer(); |
@@ -1757,7 +1759,7 @@ function _Chat() { | @@ -1757,7 +1759,7 @@ function _Chat() { | ||
1757 | if (message.content === content) { | 1759 | if (message.content === content) { |
1758 | newMessages.push({ | 1760 | newMessages.push({ |
1759 | role: "user", | 1761 | role: "user", |
1760 | - content: getMindPrompt(content, true), | 1762 | + content: getMindPrompt(removeDeepThink(content), true), |
1761 | }); | 1763 | }); |
1762 | break; | 1764 | break; |
1763 | } | 1765 | } |
@@ -1768,7 +1770,15 @@ function _Chat() { | @@ -1768,7 +1770,15 @@ function _Chat() { | ||
1768 | 1770 | ||
1769 | //20250328新增PPT导出 | 1771 | //20250328新增PPT导出 |
1770 | function toPowerpoint(pptMessage: string) { | 1772 | function toPowerpoint(pptMessage: string) { |
1771 | - navigate("/powerpoint", { state: { msg: true, pptMessage: pptMessage } }); | 1773 | + navigate(Path.Powerpoint, { |
1774 | + state: { msg: true, pptMessage: removeDeepThink(pptMessage) }, | ||
1775 | + }); | ||
1776 | + } | ||
1777 | + | ||
1778 | + function toWrite(message: string) { | ||
1779 | + navigate(Path.Writing, { | ||
1780 | + state: { msg: true, writeMessage: removeDeepThink(message) }, | ||
1781 | + }); | ||
1772 | } | 1782 | } |
1773 | //20250402新增上传文件 | 1783 | //20250402新增上传文件 |
1774 | const [fileData, setFileData] = useState(""); | 1784 | const [fileData, setFileData] = useState(""); |
@@ -1782,7 +1792,7 @@ function _Chat() { | @@ -1782,7 +1792,7 @@ function _Chat() { | ||
1782 | fileInput.style.display = "none"; | 1792 | fileInput.style.display = "none"; |
1783 | const handleFileResult = (fileName: string, data: any) => { | 1793 | const handleFileResult = (fileName: string, data: any) => { |
1784 | if (!data) { | 1794 | if (!data) { |
1785 | - message.error("未读取到内容"); | 1795 | + msgModal.error(Locale.ComError.Notread); |
1786 | return; | 1796 | return; |
1787 | } | 1797 | } |
1788 | setFileData(`'''filedata | 1798 | setFileData(`'''filedata |
@@ -1791,9 +1801,12 @@ function _Chat() { | @@ -1791,9 +1801,12 @@ function _Chat() { | ||
1791 | '''filedata`); | 1801 | '''filedata`); |
1792 | setFileName(fileName); | 1802 | setFileName(fileName); |
1793 | }; | 1803 | }; |
1794 | - const handleError = (error: any, defaultMsg = "上传失败") => { | 1804 | + const handleError = ( |
1805 | + error: any, | ||
1806 | + defaultMsg = Locale.ComError.UploadErr, | ||
1807 | + ) => { | ||
1795 | console.error(`${defaultMsg}:`, error); | 1808 | console.error(`${defaultMsg}:`, error); |
1796 | - message.error(defaultMsg); | 1809 | + msgModal.error(defaultMsg); |
1797 | }; | 1810 | }; |
1798 | // 文件处理器映射 | 1811 | // 文件处理器映射 |
1799 | const fileHandlers: Record<string, (file: File) => Promise<any>> = { | 1812 | const fileHandlers: Record<string, (file: File) => Promise<any>> = { |
@@ -1812,7 +1825,7 @@ function _Chat() { | @@ -1812,7 +1825,7 @@ function _Chat() { | ||
1812 | .slice(file.name.lastIndexOf(".")); | 1825 | .slice(file.name.lastIndexOf(".")); |
1813 | // 校验文件类型 | 1826 | // 校验文件类型 |
1814 | if (!Object.keys(fileHandlers).includes(fileExt)) { | 1827 | if (!Object.keys(fileHandlers).includes(fileExt)) { |
1815 | - message.error("不支持的文件类型"); | 1828 | + msgModal.error(Locale.ComError.UnsupportedFile); |
1816 | return; | 1829 | return; |
1817 | } | 1830 | } |
1818 | // 获取处理器并执行 | 1831 | // 获取处理器并执行 |
@@ -2077,11 +2090,17 @@ function _Chat() { | @@ -2077,11 +2090,17 @@ function _Chat() { | ||
2077 | <ChatAction | 2090 | <ChatAction |
2078 | text={Locale.Export.Excel} | 2091 | text={Locale.Export.Excel} |
2079 | icon={<ExcelIcon />} | 2092 | icon={<ExcelIcon />} |
2080 | - onClick={() => | ||
2081 | - toExcel( | ||
2082 | - getMessageTextContent(message), | ||
2083 | - ) | ||
2084 | - } | 2093 | + onClick={() => { |
2094 | + try { | ||
2095 | + toExcel( | ||
2096 | + getMessageTextContent(message), | ||
2097 | + ); | ||
2098 | + } catch (error) { | ||
2099 | + msgModal.error( | ||
2100 | + Locale.ComError.ExcelErr, | ||
2101 | + ); | ||
2102 | + } | ||
2103 | + }} | ||
2085 | /> | 2104 | /> |
2086 | {!isTableContent( | 2105 | {!isTableContent( |
2087 | getMessageTextContent(message), | 2106 | getMessageTextContent(message), |
@@ -2117,6 +2136,15 @@ function _Chat() { | @@ -2117,6 +2136,15 @@ function _Chat() { | ||
2117 | ) | 2136 | ) |
2118 | } | 2137 | } |
2119 | /> | 2138 | /> |
2139 | + <ChatAction | ||
2140 | + text={Locale.Chat.Actions.Write} | ||
2141 | + icon={<WriteIcon />} | ||
2142 | + onClick={() => | ||
2143 | + toWrite( | ||
2144 | + getMessageTextContent(message), | ||
2145 | + ) | ||
2146 | + } | ||
2147 | + /> | ||
2120 | {/* 以上 20250317 新增Word excel导出按钮 */} | 2148 | {/* 以上 20250317 新增Word excel导出按钮 */} |
2121 | {config.ttsConfig.enable && ( | 2149 | {config.ttsConfig.enable && ( |
2122 | <ChatAction | 2150 | <ChatAction |
@@ -2189,6 +2217,7 @@ function _Chat() { | @@ -2189,6 +2217,7 @@ function _Chat() { | ||
2189 | fontFamily={fontFamily} | 2217 | fontFamily={fontFamily} |
2190 | parentRef={scrollRef} | 2218 | parentRef={scrollRef} |
2191 | defaultShow={i >= messages.length - 6} | 2219 | defaultShow={i >= messages.length - 6} |
2220 | + isUser={isUser} | ||
2192 | /> | 2221 | /> |
2193 | {getMessageImages(message).length == 1 && ( | 2222 | {getMessageImages(message).length == 1 && ( |
2194 | <img | 2223 | <img |
@@ -28,6 +28,10 @@ import clsx from "clsx"; | @@ -28,6 +28,10 @@ import clsx from "clsx"; | ||
28 | import { ChartComponentProps, extractDataFromText } from "./chart/getChartData"; | 28 | import { ChartComponentProps, extractDataFromText } from "./chart/getChartData"; |
29 | import { ChartComponent } from "./chart"; | 29 | import { ChartComponent } from "./chart"; |
30 | import styles from "./chat.module.scss"; | 30 | import styles from "./chat.module.scss"; |
31 | +import { Collapse } from "antd"; | ||
32 | +import { useDebounce } from "../utils/hooks"; | ||
33 | +import { createDeepThink } from "../utils/deepThink"; | ||
34 | + | ||
31 | export function Mermaid(props: { code: string }) { | 35 | export function Mermaid(props: { code: string }) { |
32 | const ref = useRef<HTMLDivElement>(null); | 36 | const ref = useRef<HTMLDivElement>(null); |
33 | const [hasError, setHasError] = useState(false); | 37 | const [hasError, setHasError] = useState(false); |
@@ -276,15 +280,8 @@ function tryWrapHtmlCode(text: string) { | @@ -276,15 +280,8 @@ function tryWrapHtmlCode(text: string) { | ||
276 | }, | 280 | }, |
277 | ); | 281 | ); |
278 | } | 282 | } |
279 | -// 首先定义完善的类型 | ||
280 | -type FileMatch = { | ||
281 | - fullMatch: string; | ||
282 | - fileName: string; | ||
283 | - start: number; | ||
284 | - end: number; | ||
285 | -}; | ||
286 | - | ||
287 | -function _MarkDownContent(props: { content: string }) { | 283 | + |
284 | +function _MarkDownContent(props: { content: string; isUser: boolean }) { | ||
288 | const escapedContent = useMemo(() => { | 285 | const escapedContent = useMemo(() => { |
289 | return tryWrapHtmlCode(escapeBrackets(props.content)); | 286 | return tryWrapHtmlCode(escapeBrackets(props.content)); |
290 | }, [props.content]); | 287 | }, [props.content]); |
@@ -303,7 +300,6 @@ function _MarkDownContent(props: { content: string }) { | @@ -303,7 +300,6 @@ function _MarkDownContent(props: { content: string }) { | ||
303 | 300 | ||
304 | type Segment = TextSegment | ElementSegment; | 301 | type Segment = TextSegment | ElementSegment; |
305 | 302 | ||
306 | - // 将子节点转换为可分析的段落段 | ||
307 | function parseChildrenToSegments(children: React.ReactNode): Segment[] { | 303 | function parseChildrenToSegments(children: React.ReactNode): Segment[] { |
308 | const segments: Segment[] = []; | 304 | const segments: Segment[] = []; |
309 | let textBuffer = ""; | 305 | let textBuffer = ""; |
@@ -346,7 +342,6 @@ function _MarkDownContent(props: { content: string }) { | @@ -346,7 +342,6 @@ function _MarkDownContent(props: { content: string }) { | ||
346 | return segments; | 342 | return segments; |
347 | } | 343 | } |
348 | 344 | ||
349 | - // 主处理函数 | ||
350 | function CustomParagraph({ children }: { children: React.ReactNode }) { | 345 | function CustomParagraph({ children }: { children: React.ReactNode }) { |
351 | const segments = parseChildrenToSegments(children); | 346 | const segments = parseChildrenToSegments(children); |
352 | const fullText = segments | 347 | const fullText = segments |
@@ -408,6 +403,54 @@ function _MarkDownContent(props: { content: string }) { | @@ -408,6 +403,54 @@ function _MarkDownContent(props: { content: string }) { | ||
408 | return <p dir="auto">{newChildren}</p>; | 403 | return <p dir="auto">{newChildren}</p>; |
409 | } | 404 | } |
410 | 405 | ||
406 | + const CollapsibleBlockquote = ({ | ||
407 | + children, | ||
408 | + }: { | ||
409 | + children: React.ReactNode; | ||
410 | + }) => { | ||
411 | + const [isFinalized, setIsFinalized] = useState(false); | ||
412 | + const [lastUpdated, setLastUpdated] = useState(Date.now()); | ||
413 | + const contentString = React.Children.toArray(children).join(""); | ||
414 | + | ||
415 | + // 使用防抖检测内容是否稳定 | ||
416 | + const debouncedCheck = useDebounce(() => { | ||
417 | + if (Date.now() - lastUpdated > 2000) { | ||
418 | + // 2秒内无更新视为最终 | ||
419 | + setIsFinalized(true); | ||
420 | + } | ||
421 | + }, 2000); | ||
422 | + useEffect(() => { | ||
423 | + setIsFinalized(false); | ||
424 | + setLastUpdated(Date.now()); | ||
425 | + debouncedCheck(); | ||
426 | + }, [contentString]); | ||
427 | + | ||
428 | + return ( | ||
429 | + <Collapse | ||
430 | + defaultActiveKey={["1"]} | ||
431 | + size="small" | ||
432 | + items={[ | ||
433 | + { | ||
434 | + key: "1", | ||
435 | + label: isFinalized ? "已深度思考" : "思考中...", | ||
436 | + children: ( | ||
437 | + <blockquote | ||
438 | + style={{ | ||
439 | + padding: "10px", | ||
440 | + borderLeft: "2px solid #ccc", | ||
441 | + transition: "all 0.3s ease", | ||
442 | + fontSize: "12px", | ||
443 | + }} | ||
444 | + > | ||
445 | + {children} | ||
446 | + </blockquote> | ||
447 | + ), | ||
448 | + }, | ||
449 | + ]} | ||
450 | + /> | ||
451 | + ); | ||
452 | + }; | ||
453 | + | ||
411 | return ( | 454 | return ( |
412 | <ReactMarkdown | 455 | <ReactMarkdown |
413 | remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]} | 456 | remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]} |
@@ -424,7 +467,15 @@ function _MarkDownContent(props: { content: string }) { | @@ -424,7 +467,15 @@ function _MarkDownContent(props: { content: string }) { | ||
424 | components={{ | 467 | components={{ |
425 | pre: PreCode, | 468 | pre: PreCode, |
426 | code: CustomCode, | 469 | code: CustomCode, |
427 | - p: CustomParagraph, | 470 | + p: props.isUser |
471 | + ? CustomParagraph | ||
472 | + : (pProps) => <p {...pProps} dir="auto" />, | ||
473 | + blockquote: ({ children }) => | ||
474 | + props.isUser ? ( | ||
475 | + <blockquote>{children}</blockquote> | ||
476 | + ) : ( | ||
477 | + <CollapsibleBlockquote>{children}</CollapsibleBlockquote> | ||
478 | + ), | ||
428 | a: (aProps) => { | 479 | a: (aProps) => { |
429 | const href = aProps.href || ""; | 480 | const href = aProps.href || ""; |
430 | if (/\.(aac|mp3|opus|wav)$/.test(href)) { | 481 | if (/\.(aac|mp3|opus|wav)$/.test(href)) { |
@@ -462,6 +513,7 @@ export function Markdown( | @@ -462,6 +513,7 @@ export function Markdown( | ||
462 | fontFamily?: string; | 513 | fontFamily?: string; |
463 | parentRef?: RefObject<HTMLDivElement>; | 514 | parentRef?: RefObject<HTMLDivElement>; |
464 | defaultShow?: boolean; | 515 | defaultShow?: boolean; |
516 | + isUser: boolean; | ||
465 | } & React.DOMAttributes<HTMLDivElement>, | 517 | } & React.DOMAttributes<HTMLDivElement>, |
466 | ) { | 518 | ) { |
467 | const mdRef = useRef<HTMLDivElement>(null); | 519 | const mdRef = useRef<HTMLDivElement>(null); |
@@ -481,7 +533,10 @@ export function Markdown( | @@ -481,7 +533,10 @@ export function Markdown( | ||
481 | {props.loading ? ( | 533 | {props.loading ? ( |
482 | <LoadingIcon /> | 534 | <LoadingIcon /> |
483 | ) : ( | 535 | ) : ( |
484 | - <MarkdownContent content={props.content} /> | 536 | + <MarkdownContent |
537 | + content={createDeepThink(props.content)} | ||
538 | + isUser={props.isUser} | ||
539 | + /> | ||
485 | )} | 540 | )} |
486 | </div> | 541 | </div> |
487 | ); | 542 | ); |
@@ -8,8 +8,7 @@ import { useChatStore } from "@/app/store"; | @@ -8,8 +8,7 @@ import { useChatStore } from "@/app/store"; | ||
8 | import { getWrtingPrompt } from "@/app/utils/prompt"; | 8 | import { getWrtingPrompt } from "@/app/utils/prompt"; |
9 | import type { writePromptParam } from "@/app/types/prompt"; | 9 | import type { writePromptParam } from "@/app/types/prompt"; |
10 | 10 | ||
11 | -// 定义mergedData数据结构 | ||
12 | -const mergedData = [ | 11 | +export const mergedData = [ |
13 | { | 12 | { |
14 | title: "写作用途", | 13 | title: "写作用途", |
15 | required: true, | 14 | required: true, |
@@ -79,6 +78,40 @@ const mergedData = [ | @@ -79,6 +78,40 @@ const mergedData = [ | ||
79 | }, | 78 | }, |
80 | ]; | 79 | ]; |
81 | 80 | ||
81 | +// 20250408新增写作风格选项映射 | ||
82 | +const getWritingStyleOptions = (purpose: string) => { | ||
83 | + switch (purpose) { | ||
84 | + case "公司官网": | ||
85 | + return [ | ||
86 | + { name: "专业", value: "professional" }, | ||
87 | + { name: "活泼", value: "lively" }, | ||
88 | + { name: "严谨", value: "strict" }, | ||
89 | + ]; | ||
90 | + case "小红书": | ||
91 | + return [ | ||
92 | + { name: "俏皮", value: "playful" }, | ||
93 | + { name: "幽默", value: "humorous" }, | ||
94 | + ]; | ||
95 | + case "微信公众号": | ||
96 | + return [ | ||
97 | + { name: "夸张", value: "exaggerated" }, | ||
98 | + { name: "可爱", value: "cute" }, | ||
99 | + ]; | ||
100 | + case "今日头条": | ||
101 | + return [ | ||
102 | + { name: "丰满", value: "full" }, | ||
103 | + { name: "可爱", value: "cute" }, | ||
104 | + { name: "健康", value: "healthy" }, | ||
105 | + ]; | ||
106 | + default: | ||
107 | + return [ | ||
108 | + { name: "专业", value: "professional" }, | ||
109 | + { name: "活泼", value: "lively" }, | ||
110 | + { name: "严谨", value: "strict" }, | ||
111 | + ]; | ||
112 | + } | ||
113 | +}; | ||
114 | + | ||
82 | export interface WritePanelProps { | 115 | export interface WritePanelProps { |
83 | htmlCode: string; | 116 | htmlCode: string; |
84 | setHtmlCode: React.Dispatch<React.SetStateAction<string>>; | 117 | setHtmlCode: React.Dispatch<React.SetStateAction<string>>; |
@@ -96,7 +129,6 @@ export function WritingPanel(props: WritePanelProps) { | @@ -96,7 +129,6 @@ export function WritingPanel(props: WritePanelProps) { | ||
96 | setWidth, | 129 | setWidth, |
97 | setHtmlheader, | 130 | setHtmlheader, |
98 | } = props; | 131 | } = props; |
99 | - // 为每个选择框单独声明状态,存储name | ||
100 | const chatStore = useChatStore(); | 132 | const chatStore = useChatStore(); |
101 | const [writingPurposeName, setWritingPurposeName] = useState("公司官网"); // 写作用途 | 133 | const [writingPurposeName, setWritingPurposeName] = useState("公司官网"); // 写作用途 |
102 | const [imageModeName, setImageModeName] = useState("免费配图"); // 图片模式 | 134 | const [imageModeName, setImageModeName] = useState("免费配图"); // 图片模式 |
@@ -108,34 +140,49 @@ export function WritingPanel(props: WritePanelProps) { | @@ -108,34 +140,49 @@ export function WritingPanel(props: WritePanelProps) { | ||
108 | const [writingCount, setWritingCount] = useState("200"); // 写作字数 | 140 | const [writingCount, setWritingCount] = useState("200"); // 写作字数 |
109 | const [prompt, setPrompt] = useState(""); // 提示词 | 141 | const [prompt, setPrompt] = useState(""); // 提示词 |
110 | const [isLoading, setIsLoading] = useState(false); | 142 | const [isLoading, setIsLoading] = useState(false); |
143 | + | ||
144 | + // 生成动态数据 | ||
145 | + const dynamicMergedData = mergedData.map((item) => { | ||
146 | + if (item.title === "写作风格") { | ||
147 | + return { | ||
148 | + ...item, | ||
149 | + options: getWritingStyleOptions(writingPurposeName), | ||
150 | + }; | ||
151 | + } | ||
152 | + return item; | ||
153 | + }); | ||
154 | + | ||
111 | // 处理选择框变更事件 | 155 | // 处理选择框变更事件 |
112 | const handleSelectChange = (index: number, value: string) => { | 156 | const handleSelectChange = (index: number, value: string) => { |
113 | - const options = mergedData[index].options; | ||
114 | - const selectedName = options.find((opt) => opt.value === value)?.name || ""; | ||
115 | - const selectedValue = | ||
116 | - options.find((opt) => opt.value === value)?.value || ""; | ||
117 | - switch (index) { | ||
118 | - case 0: | 157 | + const item = dynamicMergedData[index]; |
158 | + const selectedOption = item.options.find((opt) => opt.value === value); | ||
159 | + const selectedName = selectedOption?.name || ""; | ||
160 | + | ||
161 | + switch (item.title) { | ||
162 | + case "写作用途": | ||
119 | setWritingPurposeName(selectedName); | 163 | setWritingPurposeName(selectedName); |
120 | - setWidth(selectedValue); | 164 | + setWidth(value); |
165 | + // 自动更新写作风格为对应选项的第一个 | ||
166 | + const newStyles = getWritingStyleOptions(selectedName); | ||
167 | + if (newStyles.length > 0) { | ||
168 | + setWritingStyleName(newStyles[0].name); | ||
169 | + } | ||
121 | break; | 170 | break; |
122 | - case 1: | 171 | + case "图片模式": |
123 | setImageModeName(selectedName); | 172 | setImageModeName(selectedName); |
124 | break; | 173 | break; |
125 | - case 2: | 174 | + case "写作风格": |
126 | setWritingStyleName(selectedName); | 175 | setWritingStyleName(selectedName); |
127 | break; | 176 | break; |
128 | - case 3: | 177 | + case "写作语言": |
129 | setWritingLanguageName(selectedName); | 178 | setWritingLanguageName(selectedName); |
130 | break; | 179 | break; |
131 | - case 4: | 180 | + case "写作类型": |
132 | setWritingTypeName(selectedName); | 181 | setWritingTypeName(selectedName); |
133 | break; | 182 | break; |
134 | - case 5: | 183 | + case "是否图文": |
135 | setIsImgName(selectedName); | 184 | setIsImgName(selectedName); |
136 | break; | 185 | break; |
137 | - default: | ||
138 | - break; | ||
139 | } | 186 | } |
140 | }; | 187 | }; |
141 | 188 | ||
@@ -174,7 +221,6 @@ export function WritingPanel(props: WritePanelProps) { | @@ -174,7 +221,6 @@ export function WritingPanel(props: WritePanelProps) { | ||
174 | if (cleanedContent.endsWith("```")) { | 221 | if (cleanedContent.endsWith("```")) { |
175 | cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4); | 222 | cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4); |
176 | } | 223 | } |
177 | - | ||
178 | //保存html头部 | 224 | //保存html头部 |
179 | const bodyTagRegex = /<body[^>]*>/i; | 225 | const bodyTagRegex = /<body[^>]*>/i; |
180 | const bodyTagMatch = cleanedContent.match(bodyTagRegex); | 226 | const bodyTagMatch = cleanedContent.match(bodyTagRegex); |
@@ -198,30 +244,27 @@ export function WritingPanel(props: WritePanelProps) { | @@ -198,30 +244,27 @@ export function WritingPanel(props: WritePanelProps) { | ||
198 | return ( | 244 | return ( |
199 | <div> | 245 | <div> |
200 | {/* 动态渲染选择框 */} | 246 | {/* 动态渲染选择框 */} |
201 | - {mergedData.map((item, index) => { | 247 | + {dynamicMergedData.map((item, index) => { |
202 | let currentValue = ""; | 248 | let currentValue = ""; |
203 | - switch (index) { | ||
204 | - case 0: | 249 | + switch (item.title) { |
250 | + case "写作用途": | ||
205 | currentValue = writingPurposeName; | 251 | currentValue = writingPurposeName; |
206 | break; | 252 | break; |
207 | - case 1: | 253 | + case "图片模式": |
208 | currentValue = imageModeName; | 254 | currentValue = imageModeName; |
209 | break; | 255 | break; |
210 | - case 2: | 256 | + case "写作风格": |
211 | currentValue = writingStyleName; | 257 | currentValue = writingStyleName; |
212 | break; | 258 | break; |
213 | - case 3: | 259 | + case "写作语言": |
214 | currentValue = writingLanguageName; | 260 | currentValue = writingLanguageName; |
215 | break; | 261 | break; |
216 | - case 4: | 262 | + case "写作类型": |
217 | currentValue = writingTypeName; | 263 | currentValue = writingTypeName; |
218 | break; | 264 | break; |
219 | - case 5: | 265 | + case "是否图文": |
220 | currentValue = isImgName; | 266 | currentValue = isImgName; |
221 | break; | 267 | break; |
222 | - default: | ||
223 | - currentValue = ""; | ||
224 | - break; | ||
225 | } | 268 | } |
226 | 269 | ||
227 | return ( | 270 | return ( |
@@ -9,10 +9,16 @@ import { useMobileScreen } from "@/app/utils"; | @@ -9,10 +9,16 @@ import { useMobileScreen } from "@/app/utils"; | ||
9 | import { IconButton } from "../button"; | 9 | import { IconButton } from "../button"; |
10 | import Locale from "@/app/locales"; | 10 | import Locale from "@/app/locales"; |
11 | import { Path } from "@/app/constant"; | 11 | import { Path } from "@/app/constant"; |
12 | -import { useNavigate } from "react-router-dom"; | 12 | +import { useLocation, useNavigate } from "react-router-dom"; |
13 | import { getClientConfig } from "@/app/config/client"; | 13 | import { getClientConfig } from "@/app/config/client"; |
14 | -import React, { useCallback, useMemo, useRef, useState } from "react"; | ||
15 | -import { useAppConfig, useMindMapStore } from "@/app/store"; | 14 | +import React, { |
15 | + useCallback, | ||
16 | + useEffect, | ||
17 | + useMemo, | ||
18 | + useRef, | ||
19 | + useState, | ||
20 | +} from "react"; | ||
21 | +import { useAppConfig, useChatStore, useMindMapStore } from "@/app/store"; | ||
16 | import { ChatAction } from "../chat"; | 22 | import { ChatAction } from "../chat"; |
17 | import { useWindowSize } from "@/app/utils"; | 23 | import { useWindowSize } from "@/app/utils"; |
18 | import { exportHtmlToWord } from "@/app/utils/fileExport/word"; | 24 | import { exportHtmlToWord } from "@/app/utils/fileExport/word"; |
@@ -37,11 +43,14 @@ import HtmlIcon from "@/app/icons/HTML.svg"; | @@ -37,11 +43,14 @@ import HtmlIcon from "@/app/icons/HTML.svg"; | ||
37 | 43 | ||
38 | import { message } from "antd"; | 44 | import { message } from "antd"; |
39 | import { HTMLPreview } from "../artifacts"; | 45 | import { HTMLPreview } from "../artifacts"; |
40 | -import { getMindPrompt } from "@/app/utils/prompt"; | 46 | +import { getMindPrompt, getWrtingPrompt } from "@/app/utils/prompt"; |
41 | import { htmlToPdf2 } from "@/app/utils/fileExport/toPdf"; | 47 | import { htmlToPdf2 } from "@/app/utils/fileExport/toPdf"; |
42 | -import { htmlToExcel } from "@/app/utils/fileExport/export2Excel"; | 48 | +import { hasTable, htmlToExcel } from "@/app/utils/fileExport/export2Excel"; |
49 | +import { writePromptParam } from "@/app/types/prompt"; | ||
50 | +import { mergedData } from "./writie-panel"; | ||
43 | 51 | ||
44 | export function WritingPage() { | 52 | export function WritingPage() { |
53 | + const chatStore = useChatStore(); | ||
45 | const isMobileScreen = useMobileScreen(); | 54 | const isMobileScreen = useMobileScreen(); |
46 | const navigate = useNavigate(); | 55 | const navigate = useNavigate(); |
47 | const clientConfig = useMemo(() => getClientConfig(), []); | 56 | const clientConfig = useMemo(() => getClientConfig(), []); |
@@ -58,6 +67,8 @@ export function WritingPage() { | @@ -58,6 +67,8 @@ export function WritingPage() { | ||
58 | const [htmlCode, setHtmlCode] = useState( | 67 | const [htmlCode, setHtmlCode] = useState( |
59 | localStorage.getItem("htmlCode") || "", | 68 | localStorage.getItem("htmlCode") || "", |
60 | ); | 69 | ); |
70 | + const query = useLocation(); //获取路由参数 | ||
71 | + let { msg, writeMessage } = query.state || {}; //获取路由参数 | ||
61 | 72 | ||
62 | //编辑器 | 73 | //编辑器 |
63 | const toolbarOptions = [ | 74 | const toolbarOptions = [ |
@@ -69,9 +80,66 @@ export function WritingPage() { | @@ -69,9 +80,66 @@ export function WritingPage() { | ||
69 | ["link", "image"], | 80 | ["link", "image"], |
70 | ]; | 81 | ]; |
71 | 82 | ||
83 | + useEffect(() => { | ||
84 | + if (!msg) { | ||
85 | + return; | ||
86 | + } | ||
87 | + if (!writeMessage) { | ||
88 | + return; | ||
89 | + } | ||
90 | + const navigateGetData = async () => { | ||
91 | + try { | ||
92 | + const param: writePromptParam = { | ||
93 | + writingPurposeName: mergedData[0].default, | ||
94 | + writingStyleName: mergedData[2].default, | ||
95 | + writingLanguageName: mergedData[3].default, | ||
96 | + prompt: writeMessage, | ||
97 | + writingTypeName: mergedData[4].default, | ||
98 | + isImgName: mergedData[5].default, | ||
99 | + writingCount: "200", | ||
100 | + }; | ||
101 | + const input = getWrtingPrompt(param); | ||
102 | + setLoading(true); | ||
103 | + console.log("------------------------" + input); | ||
104 | + const response = await chatStore.directLlmInvoke(input, "gpt-4o-mini"); | ||
105 | + let cleanedContent = response.startsWith("```html") | ||
106 | + ? response.substring(8) | ||
107 | + : response; | ||
108 | + if (cleanedContent.endsWith("```")) { | ||
109 | + cleanedContent = cleanedContent.substring( | ||
110 | + 0, | ||
111 | + cleanedContent.length - 4, | ||
112 | + ); | ||
113 | + } | ||
114 | + //保存html头部 | ||
115 | + const bodyTagRegex = /<body[^>]*>/i; | ||
116 | + const bodyTagMatch = cleanedContent.match(bodyTagRegex); | ||
117 | + if (bodyTagMatch && bodyTagMatch.index !== undefined) { | ||
118 | + // 截取从文档开头到 <body> 标签的起始位置 | ||
119 | + const contentUpToBody = cleanedContent.slice( | ||
120 | + 0, | ||
121 | + bodyTagMatch.index + bodyTagMatch[0].length, | ||
122 | + ); | ||
123 | + setHtmlheader(contentUpToBody); //保存html头部 | ||
124 | + } | ||
125 | + localStorage.setItem("htmlCode", cleanedContent); | ||
126 | + setHtmlCode(cleanedContent); | ||
127 | + } catch (error) { | ||
128 | + message.error("生成失败,请重试"); | ||
129 | + } finally { | ||
130 | + setLoading(false); | ||
131 | + } | ||
132 | + }; | ||
133 | + navigateGetData(); | ||
134 | + }, []); | ||
135 | + | ||
72 | // 生成完整HTML内容 | 136 | // 生成完整HTML内容 |
73 | const generateFullHtml = useCallback( | 137 | const generateFullHtml = useCallback( |
74 | - () => `${htmlHeader}${htmlCode}</body></html>`, | 138 | + () => `${htmlHeader} |
139 | + <div style="width:${width}"> | ||
140 | + ${htmlCode} | ||
141 | + </div> | ||
142 | + </body></html>`, | ||
75 | [htmlHeader, htmlCode], | 143 | [htmlHeader, htmlCode], |
76 | ); | 144 | ); |
77 | 145 | ||
@@ -87,7 +155,7 @@ export function WritingPage() { | @@ -87,7 +155,7 @@ export function WritingPage() { | ||
87 | }; | 155 | }; |
88 | //跳转到ppt页面 | 156 | //跳转到ppt页面 |
89 | function toPowerpoint(pptMessage: string) { | 157 | function toPowerpoint(pptMessage: string) { |
90 | - navigate("/powerpoint", { state: { msg: true, pptMessage: pptMessage } }); | 158 | + navigate(Path.Powerpoint, { state: { msg: true, pptMessage: pptMessage } }); |
91 | } | 159 | } |
92 | // 转至思维导图页面 | 160 | // 转至思维导图页面 |
93 | const toMind = useCallback( | 161 | const toMind = useCallback( |
@@ -102,7 +170,7 @@ export function WritingPage() { | @@ -102,7 +170,7 @@ export function WritingPage() { | ||
102 | ], | 170 | ], |
103 | content, | 171 | content, |
104 | ); | 172 | ); |
105 | - navigate("/mind", { state: { msg: true } }); | 173 | + navigate(Path.Mind, { state: { msg: true } }); |
106 | }, | 174 | }, |
107 | [navigate], | 175 | [navigate], |
108 | ); | 176 | ); |
@@ -124,12 +192,6 @@ export function WritingPage() { | @@ -124,12 +192,6 @@ export function WritingPage() { | ||
124 | } | 192 | } |
125 | }, [generateFullHtml]); | 193 | }, [generateFullHtml]); |
126 | 194 | ||
127 | - function hasTable(htmlContent: string): boolean { | ||
128 | - const parser = new DOMParser(); | ||
129 | - const doc = parser.parseFromString(htmlContent, "text/html"); | ||
130 | - return doc.querySelector("table") !== null; | ||
131 | - } | ||
132 | - | ||
133 | return ( | 195 | return ( |
134 | <> | 196 | <> |
135 | <WriteSiderBar | 197 | <WriteSiderBar |
@@ -201,7 +263,8 @@ export function WritingPage() { | @@ -201,7 +263,8 @@ export function WritingPage() { | ||
201 | icon={<PdfIcon />} | 263 | icon={<PdfIcon />} |
202 | onClick={async () => { | 264 | onClick={async () => { |
203 | setLoading(true); | 265 | setLoading(true); |
204 | - await htmlToPdf2(htmlCode); | 266 | + const html = `<div style="width:${width}">${htmlCode}</div>`; |
267 | + await htmlToPdf2(html); | ||
205 | setLoading(false); | 268 | setLoading(false); |
206 | }} | 269 | }} |
207 | disabled={isEdit} | 270 | disabled={isEdit} |
@@ -210,11 +273,7 @@ export function WritingPage() { | @@ -210,11 +273,7 @@ export function WritingPage() { | ||
210 | text={Locale.Export.Word} | 273 | text={Locale.Export.Word} |
211 | icon={<WordIcon />} | 274 | icon={<WordIcon />} |
212 | onClick={() => { | 275 | onClick={() => { |
213 | - const html = `${htmlHeader} | ||
214 | - ${htmlCode} | ||
215 | - </body> | ||
216 | - </html> | ||
217 | - `; | 276 | + const html = generateFullHtml(); |
218 | exportHtmlToWord(html); | 277 | exportHtmlToWord(html); |
219 | }} | 278 | }} |
220 | disabled={isEdit} | 279 | disabled={isEdit} |
@@ -28,7 +28,7 @@ export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com"; | @@ -28,7 +28,7 @@ export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com"; | ||
28 | export const MOONSHOT_BASE_URL = "https://api.moonshot.cn"; | 28 | export const MOONSHOT_BASE_URL = "https://api.moonshot.cn"; |
29 | export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com"; | 29 | export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com"; |
30 | 30 | ||
31 | -export const DEEPSEEK_BASE_URL = "https://api.deepseek.com"; | 31 | +export const DEEPSEEK_BASE_URL = "https://openapi.baolinai.top"; |
32 | 32 | ||
33 | export const XAI_BASE_URL = "https://api.x.ai"; | 33 | export const XAI_BASE_URL = "https://api.x.ai"; |
34 | 34 | ||
@@ -490,7 +490,7 @@ const openaiModels = [ | @@ -490,7 +490,7 @@ const openaiModels = [ | ||
490 | // as it is cheaper, more capable, multimodal, and just as fast. gpt-3.5-turbo is still available for use in the API. | 490 | // as it is cheaper, more capable, multimodal, and just as fast. gpt-3.5-turbo is still available for use in the API. |
491 | "gpt-3.5-turbo", | 491 | "gpt-3.5-turbo", |
492 | "gpt-3.5-turbo-1106", | 492 | "gpt-3.5-turbo-1106", |
493 | - "gpt-3.5-turbo-0125", | 493 | + "deepseek-r1", |
494 | "gpt-4", | 494 | "gpt-4", |
495 | "gpt-4-0613", | 495 | "gpt-4-0613", |
496 | "gpt-4-32k", | 496 | "gpt-4-32k", |
@@ -840,3 +840,4 @@ export const DEFAULT_GA_ID = "G-89WN60ZK2E"; | @@ -840,3 +840,4 @@ export const DEFAULT_GA_ID = "G-89WN60ZK2E"; | ||
840 | 840 | ||
841 | export const SAAS_CHAT_URL = "https://nextchat.club"; | 841 | export const SAAS_CHAT_URL = "https://nextchat.club"; |
842 | export const SAAS_CHAT_UTM_URL = "https://nextchat.club?utm=github"; | 842 | export const SAAS_CHAT_UTM_URL = "https://nextchat.club?utm=github"; |
843 | +export const UPLOAD_FILE_MAX_LINE = 100; |
@@ -62,6 +62,7 @@ const cn = { | @@ -62,6 +62,7 @@ const cn = { | ||
62 | //20250317新增 | 62 | //20250317新增 |
63 | ReWrite: "重写", | 63 | ReWrite: "重写", |
64 | Chart: "查看图表", | 64 | Chart: "查看图表", |
65 | + Write: "生成文章", | ||
65 | }, | 66 | }, |
66 | Commands: { | 67 | Commands: { |
67 | new: "新建聊天", | 68 | new: "新建聊天", |
@@ -886,6 +887,14 @@ const cn = { | @@ -886,6 +887,14 @@ const cn = { | ||
886 | generateBg: "生成背景", | 887 | generateBg: "生成背景", |
887 | promptTitle: "背景提示词", | 888 | promptTitle: "背景提示词", |
888 | }, | 889 | }, |
890 | + | ||
891 | + //20250408新增错误信息 | ||
892 | + ComError: { | ||
893 | + ExcelErr: "未找到表格内容", | ||
894 | + UploadErr: "上传失败", | ||
895 | + UnsupportedFile: "不支持的文件类型", | ||
896 | + Notread: "未读取到内容", | ||
897 | + }, | ||
889 | }; | 898 | }; |
890 | 899 | ||
891 | type DeepPartial<T> = T extends object | 900 | type DeepPartial<T> = T extends object |
app/utils/deepThink.ts
0 → 100644
1 | +export function createDeepThink(content: string) { | ||
2 | + const lines = content.split("\n"); | ||
3 | + let deepThink: string[] = []; | ||
4 | + let j = 0; | ||
5 | + let isBreak = false; | ||
6 | + for (let i = 0; i < lines.length; i++) { | ||
7 | + if (lines[i] === "") { | ||
8 | + lines.splice(i, 1); // 删除空行 | ||
9 | + i--; | ||
10 | + continue; | ||
11 | + } | ||
12 | + if ( | ||
13 | + lines[i].startsWith(">") || | ||
14 | + lines[i].startsWith("2") || | ||
15 | + lines[i].startsWith("3") || | ||
16 | + lines[i].startsWith("4") | ||
17 | + ) { | ||
18 | + if (j !== 0 && lines[i].startsWith(">")) { | ||
19 | + lines[i] = lines[i].substring(1); | ||
20 | + } | ||
21 | + deepThink.push(lines[i]); | ||
22 | + lines.splice(i, 1); | ||
23 | + j++; | ||
24 | + i--; | ||
25 | + } else { | ||
26 | + break; | ||
27 | + } | ||
28 | + } | ||
29 | + const deepThinkStr = deepThink.join(""); | ||
30 | + const linesStr = lines.join("\n"); | ||
31 | + const result = [deepThinkStr, "", linesStr].join("\n"); | ||
32 | + return result; | ||
33 | +} | ||
34 | + | ||
35 | +export function removeDeepThink(content: string) { | ||
36 | + const lines = content.split("\n"); | ||
37 | + let deepThink: string[] = []; | ||
38 | + let j = 0; | ||
39 | + let isBreak = false; | ||
40 | + for (let i = 0; i < lines.length; i++) { | ||
41 | + if (lines[i] === "") { | ||
42 | + lines.splice(i, 1); // 删除空行 | ||
43 | + i--; | ||
44 | + continue; | ||
45 | + } | ||
46 | + if ( | ||
47 | + lines[i].startsWith(">") || | ||
48 | + lines[i].startsWith("2") || | ||
49 | + lines[i].startsWith("3") || | ||
50 | + lines[i].startsWith("4") | ||
51 | + ) { | ||
52 | + if (j !== 0 && lines[i].startsWith(">")) { | ||
53 | + lines[i] = lines[i].substring(1); | ||
54 | + } | ||
55 | + deepThink.push(lines[i]); | ||
56 | + lines.splice(i, 1); | ||
57 | + j++; | ||
58 | + i--; | ||
59 | + } else { | ||
60 | + break; | ||
61 | + } | ||
62 | + } | ||
63 | + return lines.join("\n"); | ||
64 | +} |
1 | /* eslint-disable */ | 1 | /* eslint-disable */ |
2 | +import { UPLOAD_FILE_MAX_LINE } from "@/app/constant"; | ||
2 | import * as XLSX from "xlsx"; | 3 | import * as XLSX from "xlsx"; |
3 | 4 | ||
4 | export function toExcel(content: string) { | 5 | export function toExcel(content: string) { |
6 | + if (hasTable(content)) { | ||
7 | + htmlToExcel(content); | ||
8 | + } else { | ||
9 | + markdownToExcel(content); | ||
10 | + } | ||
11 | +} | ||
12 | + | ||
13 | +export function hasTable(htmlContent: string): boolean { | ||
14 | + const parser = new DOMParser(); | ||
15 | + const doc = parser.parseFromString(htmlContent, "text/html"); | ||
16 | + return doc.querySelector("table") !== null; | ||
17 | +} | ||
18 | + | ||
19 | +export function markdownToExcel(content: string) { | ||
5 | let sheetName = "result"; // 默认表名 | 20 | let sheetName = "result"; // 默认表名 |
6 | let tableContent = ""; | 21 | let tableContent = ""; |
7 | 22 | ||
@@ -26,7 +41,7 @@ export function toExcel(content: string) { | @@ -26,7 +41,7 @@ export function toExcel(content: string) { | ||
26 | } | 41 | } |
27 | 42 | ||
28 | if (tableStartIndex === -1) { | 43 | if (tableStartIndex === -1) { |
29 | - console.error("表格内容未找到"); | 44 | + throw new Error("表格内容未找到"); |
30 | return; | 45 | return; |
31 | } | 46 | } |
32 | 47 | ||
@@ -114,7 +129,7 @@ export function getExcelData(file: File): Promise<any[][]> { | @@ -114,7 +129,7 @@ export function getExcelData(file: File): Promise<any[][]> { | ||
114 | const jsonData = XLSX.utils.sheet_to_json(worksheet, { | 129 | const jsonData = XLSX.utils.sheet_to_json(worksheet, { |
115 | header: 1, | 130 | header: 1, |
116 | }) as any[][]; | 131 | }) as any[][]; |
117 | - resolve(jsonData.slice(0, 100)); | 132 | + resolve(jsonData.slice(0, UPLOAD_FILE_MAX_LINE)); |
118 | } catch (error) { | 133 | } catch (error) { |
119 | reject(error); | 134 | reject(error); |
120 | } | 135 | } |
1 | +import { UPLOAD_FILE_MAX_LINE } from "@/app/constant"; | ||
1 | import { Document, Packer, Paragraph } from "docx"; | 2 | import { Document, Packer, Paragraph } from "docx"; |
2 | import { saveAs } from "file-saver"; | 3 | import { saveAs } from "file-saver"; |
3 | import * as mammoth from "mammoth"; | 4 | import * as mammoth from "mammoth"; |
4 | 5 | ||
6 | +export function removeDeepThink(content: string) { | ||
7 | + const lines = content.split("\n"); | ||
8 | + let deepThink: string[] = []; | ||
9 | + let j = 0; | ||
10 | + let isBreak = false; | ||
11 | + for (let i = 0; i < lines.length; i++) { | ||
12 | + if (lines[i] === "") { | ||
13 | + lines.splice(i, 1); // 删除空行 | ||
14 | + i--; | ||
15 | + continue; | ||
16 | + } | ||
17 | + if ( | ||
18 | + lines[i].startsWith(">") || | ||
19 | + lines[i].startsWith("2") || | ||
20 | + lines[i].startsWith("3") || | ||
21 | + lines[i].startsWith("4") | ||
22 | + ) { | ||
23 | + if (j !== 0 && lines[i].startsWith(">")) { | ||
24 | + lines[i] = lines[i].substring(1); | ||
25 | + } | ||
26 | + deepThink.push(lines[i]); | ||
27 | + lines.splice(i, 1); | ||
28 | + j++; | ||
29 | + i--; | ||
30 | + } else { | ||
31 | + break; | ||
32 | + } | ||
33 | + } | ||
34 | + return lines.join("\n"); | ||
35 | +} | ||
36 | + | ||
5 | export function exportWord(content: string) { | 37 | export function exportWord(content: string) { |
6 | - console.log(content); | 38 | + content = removeDeepThink(content); |
7 | // 简单类型检测示例 | 39 | // 简单类型检测示例 |
8 | const isHTML = (text: string): boolean => { | 40 | const isHTML = (text: string): boolean => { |
9 | return ( | 41 | return ( |
@@ -83,7 +115,7 @@ export async function getWordData(file: File) { | @@ -83,7 +115,7 @@ export async function getWordData(file: File) { | ||
83 | try { | 115 | try { |
84 | const arrayBuffer = await file.arrayBuffer(); | 116 | const arrayBuffer = await file.arrayBuffer(); |
85 | const { value, messages } = await mammoth.extractRawText({ arrayBuffer }); | 117 | const { value, messages } = await mammoth.extractRawText({ arrayBuffer }); |
86 | - return value; | 118 | + return value.slice(0, UPLOAD_FILE_MAX_LINE); |
87 | } catch (error) { | 119 | } catch (error) { |
88 | console.error("Error extracting Word content:", error); | 120 | console.error("Error extracting Word content:", error); |
89 | throw error; | 121 | throw error; |
1 | -import { useMemo } from "react"; | 1 | +import { useEffect, useMemo, useRef } from "react"; |
2 | import { useAccessStore, useAppConfig } from "../store"; | 2 | import { useAccessStore, useAppConfig } from "../store"; |
3 | import { collectModelsWithDefaultModel } from "./model"; | 3 | import { collectModelsWithDefaultModel } from "./model"; |
4 | 4 | ||
@@ -20,3 +20,24 @@ export function useAllModels() { | @@ -20,3 +20,24 @@ export function useAllModels() { | ||
20 | 20 | ||
21 | return models; | 21 | return models; |
22 | } | 22 | } |
23 | + | ||
24 | +export function useDebounce(callback: () => void, delay: number) { | ||
25 | + const timeoutRef = useRef<NodeJS.Timeout>(); | ||
26 | + | ||
27 | + useEffect(() => { | ||
28 | + return () => { | ||
29 | + if (timeoutRef.current) { | ||
30 | + clearTimeout(timeoutRef.current); | ||
31 | + } | ||
32 | + }; | ||
33 | + }, []); | ||
34 | + | ||
35 | + return () => { | ||
36 | + if (timeoutRef.current) { | ||
37 | + clearTimeout(timeoutRef.current); | ||
38 | + } | ||
39 | + timeoutRef.current = setTimeout(() => { | ||
40 | + callback(); | ||
41 | + }, delay); | ||
42 | + }; | ||
43 | +} |
1 | import type { writePromptParam } from "@/app/types/prompt"; | 1 | import type { writePromptParam } from "@/app/types/prompt"; |
2 | 2 | ||
3 | +//生成文章提示词 | ||
3 | export function getWrtingPrompt(param: writePromptParam): string { | 4 | export function getWrtingPrompt(param: writePromptParam): string { |
4 | const { | 5 | const { |
5 | isImgName, | 6 | isImgName, |
@@ -11,17 +12,23 @@ export function getWrtingPrompt(param: writePromptParam): string { | @@ -11,17 +12,23 @@ export function getWrtingPrompt(param: writePromptParam): string { | ||
11 | writingCount, | 12 | writingCount, |
12 | } = param; | 13 | } = param; |
13 | 14 | ||
15 | + // 根据用途调整文案类型描述 | ||
14 | const purposeMap: Record<string, string> = { | 16 | const purposeMap: Record<string, string> = { |
15 | - 公司官网: "公司官网的介绍", | ||
16 | - 小红书: "小红书的介绍", | ||
17 | - 微信: "微信的介绍", | ||
18 | - 公众号: "公众号的介绍", | ||
19 | - 今日头条: "今日头条的介绍", | 17 | + 公司官网: "正式的公司官网文案", |
18 | + 小红书: "符合小红书平台风格的文案", | ||
19 | + 微信公众号: "适合微信公众号传播的文案", | ||
20 | + 今日头条: "今日头条风格的热点文案", | ||
20 | }; | 21 | }; |
21 | const styleMap: Record<string, string> = { | 22 | const styleMap: Record<string, string> = { |
22 | - 专业: "专业的风格", | ||
23 | - 活泼: "活泼的风格", | ||
24 | - 严谨: "严谨的风格", | 23 | + 专业: "专业严谨", |
24 | + 活泼: "生动活泼", | ||
25 | + 严谨: "严谨细致", | ||
26 | + 俏皮: "俏皮可爱", | ||
27 | + 幽默: "幽默风趣", | ||
28 | + 夸张: "夸张吸睛", | ||
29 | + 可爱: "可爱亲切", | ||
30 | + 丰满: "内容丰满", | ||
31 | + 健康: "健康积极", | ||
25 | }; | 32 | }; |
26 | const typeMap: Record<string, string> = { | 33 | const typeMap: Record<string, string> = { |
27 | 产品推广文案: "产品推广的文案", | 34 | 产品推广文案: "产品推广的文案", |
@@ -35,22 +42,29 @@ export function getWrtingPrompt(param: writePromptParam): string { | @@ -35,22 +42,29 @@ export function getWrtingPrompt(param: writePromptParam): string { | ||
35 | const purpose = purposeMap[rawPurpose] || "公司官网的介绍"; | 42 | const purpose = purposeMap[rawPurpose] || "公司官网的介绍"; |
36 | const style = styleMap[rawStyle] || "专业的风格"; | 43 | const style = styleMap[rawStyle] || "专业的风格"; |
37 | 44 | ||
38 | - isImg = `文案要配上图片,实现图文混排,要美观,要符合${purpose}的排版标准和写作风格,写作风格要${style}, | 45 | + isImg = `要求图文混排,需符合以下要求: |
46 | + 文案要配上图片,实现图文混排,要美观,要符合${purpose}的排版标准和写作风格,写作风格要${style}, | ||
39 | 你没有图片没关系,把图文混排的效果实现,并在你认为要插入图片的地方将图片的Prompt用英文输出给:,记得图片地址后面的?nologo=true一定不能去掉了, | 47 | 你没有图片没关系,把图文混排的效果实现,并在你认为要插入图片的地方将图片的Prompt用英文输出给:,记得图片地址后面的?nologo=true一定不能去掉了, |
40 | 因为这个语法可以自动按照提示生成并渲染图片。你可以帮我大幅提高生成图片质量和丰富程度,比如增加相机光圈、具体场景描述等内容,注意图片一定要用<img,否则在HTML下图片可能显示不了`; | 48 | 因为这个语法可以自动按照提示生成并渲染图片。你可以帮我大幅提高生成图片质量和丰富程度,比如增加相机光圈、具体场景描述等内容,注意图片一定要用<img,否则在HTML下图片可能显示不了`; |
41 | } | 49 | } |
42 | const writingTypeName = typeMap[rawType] || "产品推广文案"; | 50 | const writingTypeName = typeMap[rawType] || "产品推广文案"; |
43 | 51 | ||
44 | - const input = `帮我使用${writingLanguageName}写一篇主题是${prompt}的${writingTypeName},${isImg},字数要求不少于${writingCount}字, | ||
45 | - 字数不包括html代码和图片Prompt。输出成标准的html并且样式必须为内联样式,直接给结果,不要做任何解释`; | ||
46 | - return input; | 52 | + return `请用${writingLanguageName}撰写一篇关于【${prompt}】的${writingTypeName}: |
53 | + ${isImg} | ||
54 | + 具体要求: | ||
55 | + 1. 写作风格:${styleMap[rawStyle] || "专业"} | ||
56 | + 2. 字数要求:不少于${writingCount}字(不计代码) | ||
57 | + 3. 输出格式:标准HTML带内联样式 | ||
58 | + 4. 特殊要求:直接输出结果,不要额外解释`; | ||
47 | } | 59 | } |
48 | 60 | ||
61 | +//生成图片提示词 | ||
49 | export function getBgPrompt(content: string) { | 62 | export function getBgPrompt(content: string) { |
50 | const input = `你现扮演生成创意思图片的提示词工程师,参考我的描述“${content}”帮我做优化润色5组,返回的数据用''分割,直接输出结果,不要做解释`; | 63 | const input = `你现扮演生成创意思图片的提示词工程师,参考我的描述“${content}”帮我做优化润色5组,返回的数据用''分割,直接输出结果,不要做解释`; |
51 | return input; | 64 | return input; |
52 | } | 65 | } |
53 | 66 | ||
67 | +//思维导图提示词 | ||
54 | export function getMindPrompt(content: string, isContext: boolean) { | 68 | export function getMindPrompt(content: string, isContext: boolean) { |
55 | const context = `联系上下文`; | 69 | const context = `联系上下文`; |
56 | let prompt = `请你帮我生成一份以"${content}"为主题的思维导图数据,请严格遵循以下要求生成思维导图数据: | 70 | let prompt = `请你帮我生成一份以"${content}"为主题的思维导图数据,请严格遵循以下要求生成思维导图数据: |
-
请 注册 或 登录 后发表评论