writing.tsx 10.0 KB
import chatStyles from "@/app/components/chat.module.scss";
import homeStyles from "@/app/components/home.module.scss";
import clsx from "clsx";

import { WriteSiderBar } from "./write-siderBar";
import { WindowContent } from "@/app/components/home";
import { useMobileScreen, copyToClipboard } 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, { MutableRefObject, useEffect, 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 CloseIcon from "@/app/icons/close.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 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}/>
            <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}>
                        {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>
        </>
    )
}