import chatStyles from "@/app/components/chat.module.scss"; import homeStyles from "@/app/components/home.module.scss"; import styles from "@/app/components/writing/writing.module.scss"; import clsx from "clsx"; import { WriteSiderBar } from "./write-siderBar"; import { WindowContent } from "@/app/components/home"; import { useMobileScreen } from "@/app/utils"; import { IconButton } from "../button"; import Locale from "@/app/locales"; import { Path } from "@/app/constant"; import { useNavigate } from "react-router-dom"; import { getClientConfig } from "@/app/config/client"; import React, { useMemo, useRef, useState } from "react"; import { useAppConfig } from "@/app/store"; import { ChatAction } from "../chat"; import { useWindowSize } from "@/app/utils"; import { exportWord } from "@/app/utils/excelAndWordUtils/word"; import { HTMLPreview } from "../artifacts"; import ReactQuill from "react-quill"; import "react-quill/dist/quill.snow.css"; import ReturnIcon from "@/app/icons/return.svg"; import MinIcon from "@/app/icons/min.svg"; import MaxIcon from "@/app/icons/max.svg"; import SDIcon from "@/app/icons/sd.svg"; import LoadingIcon from "@/app/icons/loading.svg"; import BotIcon from "@/app/icons/bot.svg"; import EditIcon from "@/app/icons/rename.svg"; import ReloadIcon from "@/app/icons/reload.svg"; import CopyIcon from "@/app/icons/copy.svg"; import ExcelIcon from "@/app/icons/excel.svg"; import WordIcon from "@/app/icons/word.svg"; import MindIcon from "@/app/icons/mind.svg"; import PptIcon from "@/app/icons/ppt.svg"; import PdfIcon from "@/app/icons/pdf.svg"; import { message } from "antd"; export function WritingPage() { const isMobileScreen = useMobileScreen(); const navigate = useNavigate(); const clientConfig = useMemo(() => getClientConfig(), []); const showMaxIcon = !isMobileScreen && !clientConfig?.isApp; const config = useAppConfig(); const scrollRef = useRef<HTMLDivElement>(null); const isWriting = location.pathname === Path.Writing; const { height } = useWindowSize(); const [isEdit, setIsEdit] = useState(false); const [loading, setLoading] = useState(false); const quillRef = useRef<ReactQuill | null>(null); const [htmlCode, setHtmlCode] = useState(""); //编辑器 const toolbarOptions = [ [{ font: [] }, { size: ["small", false, "large", "huge"] }], [{ header: [1, 2, 3, 4, 5, 6, false] }], ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }, { align: [] }], [{ color: [] }, { background: [] }], ["link", "image"], ]; const copyToClipboard = () => { // 检查quillRef.current是否为null if (quillRef.current) { const editor = quillRef.current.getEditor(); // 获取编辑器实例 const range = editor.getSelection(); // 获取当前选择的范围 const text = editor.getText(); // 获取编辑器中的文本内容 if (range && range.length > 0) { // 如果有选中的文本,复制选中的内容 const selectedText = text.substring( range.index, range.index + range.length, ); navigator.clipboard .writeText(selectedText) .then(() => { message.success("复制成功"); }) .catch((err) => { message.error("复制失败:"); }); } else { // 如果没有选择文本,就复制全部内容 navigator.clipboard .writeText(text) .then(() => { message.success("复制成功"); }) .catch((err) => { message.error("复制失败:"); }); } } else { console.error("Quill编辑器尚未初始化"); } }; return ( <> <WriteSiderBar className={clsx({ [homeStyles["sidebar-show"]]: isWriting })} htmlCode={htmlCode} setHtmlCode={setHtmlCode} loading={loading} setLoading={setLoading} /> <WindowContent> <div className={chatStyles.chat} key={"1"}> <div className="window-header" data-tauri-drag-region> {isMobileScreen && ( <div className="window-actions"> <div className={"window-action-button"}> <IconButton icon={<ReturnIcon />} bordered title={Locale.Chat.Actions.ChatList} onClick={() => navigate(Path.BgRemoval)} /> </div> </div> )} <div className={clsx( "window-header-title", chatStyles["chat-body-title"], )} > <div className={`window-header-main-title`}>AI-Writing</div> </div> <div className={chatStyles["chat-message-actions"]}> <div className={chatStyles["chat-input-actions"]}> <ChatAction text={Locale.Chat.Actions.ReWrite} icon={<ReloadIcon />} onClick={() => {}} /> <ChatAction text={Locale.Chat.Actions.Copy} icon={<CopyIcon />} onClick={copyToClipboard} /> {!isEdit ? ( <ChatAction text={Locale.Chat.Actions.Edit} icon={<EditIcon />} onClick={() => { setIsEdit(true); }} /> ) : ( <ChatAction text="取消编辑" icon={<EditIcon />} onClick={() => { setIsEdit(false); }} /> )} <ChatAction text={Locale.Chat.Actions.Pdf} icon={<PdfIcon />} onClick={() => {}} /> {htmlCode && ( <ChatAction text={Locale.Chat.Actions.Word} icon={<WordIcon />} onClick={() => { exportWord(htmlCode); }} /> )} <ChatAction text={Locale.Chat.Actions.Excel} icon={<ExcelIcon />} onClick={() => {}} /> <ChatAction text={Locale.Chat.Actions.Ppt} icon={<PptIcon />} onClick={() => {}} /> <ChatAction text={Locale.Chat.Actions.Mind} icon={<MindIcon />} onClick={() => {}} /> </div> </div> <div className="window-actions"> {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> )} {isMobileScreen && <SDIcon width={50} height={50} />} </div> </div> <div className={chatStyles["chat-body"]} ref={scrollRef}> {loading ? ( <div className={clsx("no-dark", styles["loading-content"])}> <BotIcon /> <LoadingIcon /> </div> ) : ( htmlCode && (isEdit ? ( <ReactQuill ref={quillRef} theme="snow" value={htmlCode} onChange={setHtmlCode} modules={{ toolbar: toolbarOptions, }} /> ) : ( <HTMLPreview code={htmlCode} autoHeight={!document.fullscreenElement} height={!document.fullscreenElement ? 600 : height} /> )) )} </div> </div> </WindowContent> </> ); }