writie-panel.tsx 9.5 KB
import { Select } from "@/app/components/ui-lib";
import { ControlParamItem } from "../sd";
import { SideBarTail } from "@/app/components/sidebar";
import { useEffect, useState } from "react";
import { IconButton } from "../button";
import { message } from "antd";
import { useChatStore } from "@/app/store";
// 定义mergedData数据结构
const mergedData = [
    {
        title: "写作用途",
        required: true,
        type: "select",
        default: "公司官网",
        options: [
            { name: '公司官网', value: '1' },
            { name: '小红书', value: '2' },
            { name: '微信', value: '3' },
            { name: '公众号', value: '4' },
            { name: '今日头条', value: '5' },
        ]
    },
    {
        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: '产品推广文案' },
            { name: '品牌宣传文案', value: '品牌宣传文案' },
            { name: '产品说明书', value: '产品说明书' },
            { name: '产品介绍', value: '产品介绍' },
        ]
    },
    {
        title: "是否图文",
        type: "select",
        required: false,
        default: "是",
        options: [
            { name: '是', value: 'true' },
            { name: '否', value: 'false' },
        ]
    },
];

export interface WritePanelProps {
    htmlCode: string;
    setHtmlCode: React.Dispatch<React.SetStateAction<string>>;
}
export function WritingPanel(props: WritePanelProps) {
    const { htmlCode, setHtmlCode } = props;
    // 为每个选择框单独声明状态,存储name
    const chatStore = useChatStore();
    const [writingPurposeName, setWritingPurposeName] = useState('公司官网'); // 写作用途
    const [imageModeName, setImageModeName] = useState('免费配图'); // 图片模式
    const [writingStyleName, setWritingStyleName] = useState('专业'); // 写作风格
    const [writingLanguageName, setWritingLanguageName] = useState('中文'); // 写作语言
    const [writingTypeName, setWritingTypeName] = useState('产品推广文案'); // 写作类型
    const [isImgName, setIsImgName] = useState('是'); // 是否图文
    // 为输入框和文本区域单独声明状态
    const [writingCount, setWritingCount] = useState('200'); // 写作字数
    const [prompt, setPrompt] = useState(''); // 提示词
    const [isLoading, setIsLoading] = useState(false)
    // 处理选择框变更事件
    const handleSelectChange = (index: number, value: string) => {
        const options = mergedData[index].options;
        const selectedName = options.find(opt => opt.value === value)?.name || '';

        switch (index) {
            case 0:
                setWritingPurposeName(selectedName);
                break;
            case 1:
                setImageModeName(selectedName);
                break;
            case 2:
                setWritingStyleName(selectedName);
                break;
            case 3:
                setWritingLanguageName(selectedName);
                break;
            case 4:
                setWritingTypeName(selectedName);
                break;
            case 5:
                setIsImgName(selectedName);
                break;
            default:
                break;
        }
    };

    // 处理输入框变更事件
    const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setWritingCount(e.target.value);
    };

    // 处理文本区域变更事件
    const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
        setPrompt(e.target.value);
    };

    // 提交表单时的处理函数
    const handleSubmit = async () => {
        if (!prompt.trim()) { return message.error('请输入提示词') }
        try {
            const isImg = `文案要配上图片,实现图文混排,要美观,
            要符合${writingPurposeName}的排版标准和写作风格,写作风格要${writingStyleName},
            你没有图片没关系,把图文混排的效果实现,
            并在你认为要插入图片的地方将图片的Prompt用英文输出给:
            ![description](https://image.pollinations.ai/prompt/description?nologo=true),记得图片地址后面的?nologo=true一定不能去掉了,
            因为这个语法可以自动按照提示生成并渲染图片。你可以帮我大幅提高生成图片质量和丰富程度,
            比如增加相机光圈、具体场景描述等内容,注意图片一定要用<img,
            否则在HTML下图片可能显示不了`
            const input = `帮我使用${writingLanguageName}写一篇主题是${prompt}的${writingTypeName},
            ${isImgName ? isImg : ''},字数要求不少于${writingCount}字,字数不包括html代码和图片Prompt。输出成标准的html,直接给结果,不要做任何解释`
            setIsLoading(true)
            console.log("------------------------" + input)
            const response = await chatStore.directLlmInvoke(input, 'gpt-4o-mini');
            let cleanedContent = response.startsWith('```html') ? response.substring(8) : response;
            if (cleanedContent.endsWith('```')) {
                cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4);
            }
            setHtmlCode(cleanedContent)
        } catch (error) {
            message.error("生成失败,请重试")
        } finally {
            setIsLoading(false)
        }

    };

    return (
        <div>
            {/* 动态渲染选择框 */}
            {mergedData.map((item, index) => {
                let currentValue = '';
                switch (index) {
                    case 0:
                        currentValue = writingPurposeName;
                        break;
                    case 1:
                        currentValue = imageModeName;
                        break;
                    case 2:
                        currentValue = writingStyleName;
                        break;
                    case 3:
                        currentValue = writingLanguageName;
                        break;
                    case 4:
                        currentValue = writingTypeName;
                        break;
                    case 5:
                        currentValue = isImgName;
                        break;
                    default:
                        currentValue = '';
                        break;
                }

                return (
                    <ControlParamItem
                        key={item.title}
                        title={item.title}
                        required={item.required}
                    >
                        <Select
                            aria-label={item.title}
                            value={item.options.find(opt => opt.name === currentValue)?.value || ''}
                            onChange={(e) => handleSelectChange(index, e.target.value)}
                        >
                            {item.options.map((opt) => (
                                <option key={opt.value} value={opt.value}>
                                    {opt.name}
                                </option>
                            ))}
                        </Select>
                    </ControlParamItem>
                );
            })}

            {/* 写作字数输入框 */}
            <ControlParamItem
                title="写作字数"
                required={true}
            >
                <input
                    aria-label="写作字数"
                    type="number"
                    placeholder="200"
                    min="200"
                    max="5000"
                    value={writingCount}
                    onChange={handleInputChange}
                />
            </ControlParamItem>

            {/* 提示词文本区域 */}
            <ControlParamItem
                title="提示词"
                required={true}
            >
                <textarea
                    rows={3}
                    style={{ maxWidth: "100%", width: "100%", padding: "10px" }}
                    placeholder="请输入"
                    value={prompt}
                    onChange={handleTextareaChange}
                ></textarea>
            </ControlParamItem>

            {/* 提交按钮 */}
            <SideBarTail
                secondaryAction={
                    <IconButton
                        text="提交生成"
                        type="primary"
                        shadow
                        onClick={handleSubmit}
                    ></IconButton>
                }
            />
        </div>
    );
}