wujie应用联调
This commit is contained in:
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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user