feat(ai-writing): 支持用户粘贴已有大纲并按结构生成

表单可提交 userOutline,后端解析/归一用户大纲并关联素材;补充大纲测试用例与右侧编辑器滑词/段落菜单文档。
This commit is contained in:
fengxiangdebaozi committed 2026-06-03 11:43:44 +08:00
1 parent 3b5525a0a5
commit ab876fc7d8
10 files changed
+944 -3

No files matched your search

@@ -50,6 +50,8 @@ export function AIWritingForm({
hideAdvancedFields = false,
}: AIWritingFormProps) {
const [intent, setIntent] = useState('');
const [showOutline, setShowOutline] = useState(false);
const [outlineText, setOutlineText] = useState('');
const [articleType, setArticleType] = useState('analysis');
const [audience, setAudience] = useState('普通读者');
const [wordCountInput, setWordCountInput] = useState('800');
@@ -115,6 +117,7 @@ export function AIWritingForm({
const wordCount = Math.max(100, parseInt(wordCountInput, 10) || 800);
onSubmit({
userIntent: intent.trim(),
userOutline: outlineText.trim() || undefined,
articleType,
targetAudience: hideAdvancedFields
? DEFAULT_AUDIENCE
@@ -155,6 +158,43 @@ export function AIWritingForm({
/>
</div>
<div className="mt-4 space-y-2">
<button
type="button"
onClick={() => setShowOutline((v) => !v)}
disabled={disabled}
className="flex w-full items-center justify-between text-left"
>
<span className={FIELD_LABEL_CLASS}>
已有大纲
<span className="ml-1 text-sm font-normal text-muted-foreground dark:text-white/70">
(可选)
</span>
</span>
<span className="text-sm text-muted-foreground dark:text-white/70">
{showOutline ? '收起 ▴' : '我已有大纲,直接粘贴 ▾'}
</span>
</button>
{showOutline && (
<>
<Textarea
placeholder={
'粘贴你已有的大纲,将完全按你的大纲生成(支持一/二/三级目录)。例如:\n' +
'一、引言\n(一)背景\n(二)意义\n二、现状分析\n…'
}
value={outlineText}
onChange={(e) => setOutlineText(e.target.value)}
disabled={disabled}
rows={6}
className="resize-none rounded-2xl border-[#e8dedd] bg-white px-4 py-3 text-sm leading-6 shadow-sm"
/>
<p className="text-xs leading-relaxed text-muted-foreground/80">
填写后将跳过自动大纲规划,按你粘贴的大纲逐章生成;留空则由 AI 自动规划大纲。
</p>
</>
)}
</div>
<div className="mt-4 grid grid-cols-2 gap-4">
<div className="min-w-0 space-y-2">
<label className={FIELD_LABEL_CLASS}>文章类型</label>
@@ -259,6 +259,8 @@ export function AIWritingProvider({ children }: { children: ReactNode }) {
await stream.submit(
{
user_intent: request.userIntent,
// 用户自带大纲原文(留空则后端走自动规划)
user_outline_raw: request.userOutline?.trim() || '',
article_type: request.articleType,
target_audience: request.targetAudience,
word_count_target: request.wordCountTarget,
@@ -623,6 +623,7 @@ export function interruptToEvent(value: unknown, sessionId: string): ProgressEve
export type AIWritingValues = {
user_intent?: string;
user_outline_raw?: string;
article_type?: string;
status?: string;
material_package?: unknown;
@@ -220,6 +220,8 @@ export interface UserInterventionPayload {
export interface AIWritingRequest {
userIntent: string;
/** 用户粘贴的「已有大纲」原文(可选)。非空时后端按此大纲生成,不再自动规划 */
userOutline?: string;
articleType: string;
targetAudience: string;
wordCountTarget: number;