wujie应用联调

This commit is contained in:
董宏凯 committed 2026-06-11 17:11:28 +08:00
1 parent 0784f30e23
commit f3ca996116
35 files changed
+1590 -334

No files matched your search

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { PenLine } from 'lucide-react';
import { PenLine, SlidersHorizontal } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea';
@@ -163,17 +163,19 @@ export function AIWritingForm({
return (
<form onSubmit={handleSubmit} className="space-y-4 px-4 py-4">
<div className="rounded-2xl border border-[#ebe3e2] bg-[#fcf8f8] p-4 dark:border-[#213D5E] dark:bg-[#02244C] dark:text-white [&_h3]:dark:text-white [&_label]:dark:text-white">
<div className="flex items-center gap-2 border-b border-[#ece5e4] pb-3 dark:border-[#213D5E]">
<PenLine className="size-4 text-[#8b6f6f] dark:text-white" />
<h3 className="text-[18px] font-semibold leading-none text-[#2f2f2f] dark:text-white">
<div className="rounded-2xl border border-[#ece5e4] bg-card p-5 shadow-sm dark:border-[#213D5E] dark:bg-[#02244C] dark:text-white [&_h3]:dark:text-white [&_label]:dark:text-white">
<div className="flex items-center gap-2.5 border-b border-[#ece5e4] pb-3 dark:border-[#213D5E]">
<SlidersHorizontal className="size-4 text-primary dark:text-white" />
<h3 className="text-[18px] font-semibold leading-none tracking-tight text-[#2f2f2f] dark:text-white">
写作配置选项
</h3>
</div>
<div className="mt-4 space-y-2">
<div className="flex items-center justify-between">
<label className={FIELD_LABEL_CLASS}>写作意图</label>
<label className={FIELD_LABEL_CLASS}>
写作意图 <span className="text-primary dark:text-white">*</span>
</label>
<span className="text-sm text-muted-foreground dark:text-white/70">
{intent.length} / {MAX_INTENT_LENGTH} 字
</span>
@@ -368,7 +370,7 @@ export function AIWritingForm({
<Button
type="submit"
disabled={disabled || !intent.trim()}
className="mt-4 h-12 w-full gap-2 rounded-2xl text-base font-semibold"
className="mt-5 h-12 w-full gap-2 rounded-2xl text-base font-semibold shadow-lg shadow-primary/20 transition-all hover:shadow-primary/30 active:scale-[0.98]"
>
<PenLine className="size-4" />
开始 AI 写作
@@ -29,6 +29,7 @@ import { cn } from '@/lib/utils';
import { useArticleTypes } from '../../hooks/useArticleTypes';
import { useWritingForm } from '../../contexts/WritingFormContext';
import { WritingWelcome } from './WritingWelcome';
/**
* 写作配置 Q&A 面板(AI 写作入口页右侧,仅在未开始写作时显示)。
@@ -103,6 +104,9 @@ export function WritingSetupChat() {
[applyProposal],
);
// 对话还没有任何消息时显示欢迎/空状态屏(与原型图一致)
const showWelcome = thread.messages.length === 0 && !thread.isLoading;
const handleReject = useCallback(async () => {
await handleChatSubmit('这份配置先不应用,请继续帮我调整。');
}, [handleChatSubmit]);
@@ -111,58 +115,74 @@ export function WritingSetupChat() {
<ThreadContext.Provider value={{ thread }}>
<ArtifactsProvider>
<div className="flex size-full flex-col bg-background">
<header className="flex shrink-0 items-center gap-2 border-b px-4 py-3">
<Sparkles className="size-4 text-primary" />
<h2 className="text-sm font-semibold">写作助手</h2>
<span className="text-muted-foreground text-xs">描述你想写什么,我来帮你填好左侧配置</span>
</header>
{/* 空状态隐藏对话头部,直接展示欢迎屏(与原型图一致);有消息后再显示头部 */}
{!showWelcome && (
<header className="flex shrink-0 items-center gap-2 border-b px-4 py-3">
<Sparkles className="size-4 text-primary" />
<h2 className="text-sm font-semibold">写作助手</h2>
<span className="text-muted-foreground text-xs">描述你想写什么,我来帮你填好右侧配置</span>
</header>
)}
<div className="min-h-0 flex-1">
<MessageList
className="size-full"
threadId={threadId}
thread={thread}
onClarificationSubmit={handleChatSubmit}
onSetupWritingApprove={handleApprove}
onSetupWritingReject={handleReject}
/>
{showWelcome ? (
<WritingWelcome />
) : (
<MessageList
className="size-full"
threadId={threadId}
thread={thread}
onClarificationSubmit={handleChatSubmit}
onSetupWritingApprove={handleApprove}
onSetupWritingReject={handleReject}
/>
)}
</div>
<div className="bg-background shrink-0 space-y-2 border-t px-4 py-4">
<div className="flex items-center gap-2">
<span className="text-muted-foreground text-xs">当前对话模型</span>
{modelsLoading || models.length === 0 ? (
<div className="border-input bg-background text-muted-foreground flex h-7 min-w-[10rem] items-center rounded-md border px-2 text-xs shadow-xs dark:border-[#1B477E] dark:bg-[#022248]">
{modelsLoading ? '加载中…' : '暂无模型'}
<div className="bg-background shrink-0 border-t px-4 py-4">
{/* 与原型图一致:白色圆角卡片包裹「模型选择行 + 输入框」 */}
<div className="space-y-3 rounded-2xl border border-[#ece5e4] bg-card p-3 shadow-sm dark:border-[#1B477E] dark:bg-[#022248]/40">
<div className="flex items-center justify-between gap-3">
<div className="flex min-w-0 items-center gap-2 text-xs text-muted-foreground">
<Sparkles className="size-3.5 shrink-0 text-primary" />
<span className="shrink-0">当前对话模型</span>
{modelsLoading || models.length === 0 ? (
<div className="border-input bg-background text-muted-foreground flex h-7 min-w-[9rem] items-center rounded-md border px-2 text-xs shadow-xs dark:border-[#1B477E] dark:bg-[#022248]">
{modelsLoading ? '加载中…' : '暂无模型'}
</div>
) : (
<Select value={chatModel} onValueChange={setChatModel} disabled={thread.isLoading}>
<SelectTrigger
className={cn(
'border-input bg-background h-7 min-w-[9rem] max-w-[14rem] px-2 text-xs shadow-xs outline-none',
'dark:border-[#1B477E] dark:bg-[#022248] dark:text-[#AACEFF]',
)}
>
<SelectValue placeholder="选择模型" />
</SelectTrigger>
<SelectContent position="popper" className="w-[var(--radix-select-trigger-width)] dark:border-[#1B477E] dark:bg-[#022248]">
{models.map((model) => (
<SelectItem key={model.name} value={model.name} className="dark:text-[#AACEFF]">
{model.display_name || model.name}
</SelectItem>
))}
</SelectContent>
</Select>
)}
</div>
) : (
<Select value={chatModel} onValueChange={setChatModel} disabled={thread.isLoading}>
<SelectTrigger
className={cn(
'border-input bg-background h-7 min-w-[10rem] px-2 text-xs shadow-xs outline-none',
'dark:border-[#1B477E] dark:bg-[#022248] dark:text-[#AACEFF]',
)}
>
<SelectValue placeholder="选择模型" />
</SelectTrigger>
<SelectContent position="popper" className="w-[var(--radix-select-trigger-width)] dark:border-[#1B477E] dark:bg-[#022248]">
{models.map((model) => (
<SelectItem key={model.name} value={model.name} className="dark:text-[#AACEFF]">
{model.display_name || model.name}
</SelectItem>
))}
</SelectContent>
</Select>
)}
<span className="hidden shrink-0 text-[11px] text-muted-foreground/70 sm:block">
输入想法,按 Enter 直接解析填充
</span>
</div>
<PromptInput onSubmit={({ text }) => void handleChatSubmit(text)}>
<PromptInputTextarea
placeholder="例如:帮我写一篇 1500 字、面向行业专家关于人工智能商业趋势的分析报道"
/>
<PromptInputFooter className="justify-end">
<PromptInputSubmit status={submitStatus} />
</PromptInputFooter>
</PromptInput>
</div>
<PromptInput onSubmit={({ text }) => void handleChatSubmit(text)}>
<PromptInputTextarea
placeholder="例如:帮我写一篇 2000 字、面向行业专家的新能源汽车出口分析"
/>
<PromptInputFooter className="justify-end">
<PromptInputSubmit status={submitStatus} />
</PromptInputFooter>
</PromptInput>
</div>
</div>
</ArtifactsProvider>
@@ -0,0 +1,73 @@
import { PenTool, Sliders, Sparkles } from 'lucide-react';
/**
* AI 写作入口页左栏「欢迎/空状态」屏。
*
* 仅在写作助手对话还没有任何消息时显示(见 WritingSetupChat)。一旦用户在底栏发出
* 第一条消息,就回到正常的对话时间线。
*/
const STEPS = [
{
n: '1',
title: '对白意图解析',
desc: '在底栏用人话随便说出你的想法,AI 会瞬间解析要求并填充右侧配置。',
icon: Sparkles,
iconClass: 'bg-indigo-50 text-indigo-600 dark:bg-indigo-500/15 dark:text-indigo-300',
},
{
n: '2',
title: '参数多端对齐',
desc: '右侧配置框即刻对齐所需的体裁、目标行文大纲和字数限制。',
icon: Sliders,
iconClass: 'bg-amber-50 text-amber-600 dark:bg-amber-500/15 dark:text-amber-300',
},
{
n: '3',
title: '深度排版落成',
desc: '点击右侧红色创作按钮,精美的行业或创意文档极速铺开。',
icon: PenTool,
iconClass: 'bg-emerald-50 text-emerald-600 dark:bg-emerald-500/15 dark:text-emerald-300',
},
];
export function WritingWelcome() {
return (
<div className="size-full overflow-y-auto">
<div className="mx-auto flex min-h-full w-full max-w-3xl flex-col px-6 py-7">
{/* 标题 + 介绍 + 三步引导卡片:在区域里垂直居中 */}
<div className="flex flex-1 flex-col justify-center gap-6">
<div>
<h2 className="text-2xl font-bold leading-snug tracking-tight text-slate-800 dark:text-white">
欢迎使用「妙笔 AI」智力打字协作工坊
</h2>
<p className="mt-2 max-w-xl text-sm leading-relaxed text-slate-500 dark:text-white/60">
这是一个专业的自动化写作系统。你只需在下方对话框中
<strong className="font-semibold text-slate-700 dark:text-white/80">
输入你的本意(例如:「帮我写一篇 1500 字的新能源汽车报告」)
</strong>
,系统即会自动为你在右侧填充所需的字数、体裁等格式进行对齐;你也可在右侧进行纯人工的参数深度定制,并一键开启 AI 深度协作创作。
</p>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{STEPS.map((step) => (
<div
key={step.n}
className="flex flex-col gap-2 rounded-xl border border-slate-100/80 bg-slate-50/50 p-4 transition-colors hover:bg-slate-50 dark:border-white/5 dark:bg-white/[0.02] dark:hover:bg-white/5"
>
<div className={`flex size-8 items-center justify-center rounded-lg ${step.iconClass}`}>
<step.icon className="size-4" />
</div>
<h4 className="text-sm font-semibold text-slate-800 dark:text-white">
{step.n}. {step.title}
</h4>
<p className="text-xs leading-relaxed text-slate-500 dark:text-white/55">{step.desc}</p>
</div>
))}
</div>
</div>
</div>
</div>
);
}