refactor(ai): 稿件与大纲页文案去掉接口术语与多余提示

面向学生的页面不再暴露实现细节:

- 删除两页底部「提交说明」整块(含 `docType`/`inputs`、接口路径、
  WebSocket 流式说明及对应样式)
- 稿件页删除 `user_content`、`files` 字段说明;大纲页不再把
  conversationId/messageId 拼成「接口摘要」展示,随之删除
  buildSummary、resultSummary 及摘要渲染
- 加载态由三段式压成一行,空态与附件说明各留一句人话
This commit is contained in:
2026-10-23 12:23:32 +08:00
parent e210a9b4b7
commit 5073b2969a
2 changed files with 13 additions and 180 deletions
+8 -96
View File
@@ -4,10 +4,7 @@
<div>
<div class="hero-kicker">AI CONTENT STUDIO</div>
<h1>AI内容创作与宣传助手</h1>
<p>
面向团委宣传、活动报道、新闻稿和推文草稿的快速生成场景。输入创作条件后,通过
`/ai/manuscriptGen` 发起生成任务,并通过 WebSocket 实时接收返回内容,支持继续编辑与复制。
</p>
<p>写新闻稿、推文、发言稿,或直接说明你的需求,AI 先给出一份初稿,可以接着改。</p>
</div>
<div class="hero-actions">
<a-button type="primary" ghost @click="goChatPage">进入问答助手</a-button>
@@ -41,9 +38,6 @@
:auto-size="{ minRows: 6, maxRows: 12 }"
placeholder="例如:帮我写一段迎新晚会的主持人开场词,口语化一点,时长两分钟左右"
/>
<div class="upload-tip">
不套模板时,这段内容会作为 `user_content` 交给「AI 创意助手」;选上面的文稿模板后可填写活动信息等可选参数。
</div>
</a-form-item>
<template v-if="templateMode">
<a-form-item label="稿件标题">
@@ -111,9 +105,6 @@
上传附件
</a-button>
</a-upload>
<div class="upload-tip">
支持图片、文档等附件,上传后按 `files` 字段一并提交给接口。
</div>
<div v-if="uploadedFiles.length" class="file-list">
<div
v-for="(item, index) in uploadedFiles"
@@ -145,15 +136,6 @@
生成内容
</a-button>
</div>
<div class="hint-block">
<div class="hint-title">提交说明</div>
<div class="hint-text">
未选模板(自由创作)时只提交一段内容,后端转「AI 创意助手」;选了模板才提交
`docType` 等结构化条件。两种都会先 `POST /ai/manuscriptGen`,再按 `/ai` 页面的方式
通过 WebSocket 流式接收生成结果;如果后端仍然直接返回正文,页面也会自动兼容解析。
</div>
</div>
</a-card>
</a-col>
@@ -176,7 +158,7 @@
</a-space>
</template>
<a-empty v-if="!markdownContent && !loading" description="填写左侧创作条件后开始生成" />
<a-empty v-if="!markdownContent && !loading" description="填写创作条件后开始生成" />
<template v-else>
<a-form layout="vertical">
@@ -201,11 +183,6 @@
</div>
</a-form-item>
</a-form>
<!-- <div v-if="responseSummary" class="response-meta">-->
<!-- <div class="meta-label">接口摘要</div>-->
<!-- <div class="meta-value">{{ responseSummary }}</div>-->
<!-- </div>-->
</template>
</a-card>
</a-col>
@@ -257,7 +234,6 @@
interface ParsedResult {
title: string;
content: string;
summary: string;
}
type DemoType = 'news' | 'wechat' | 'speech' | 'free';
@@ -278,7 +254,6 @@
const wsConnected = ref(false);
const generatedTitle = ref('');
const generatedContent = ref('');
const responseSummary = ref('');
const uploadedFiles = ref<Partial<FileRecord>[]>([]);
const activeConversationId = ref<string>();
const webSocketRef = ref<WebSocket | null>(null);
@@ -458,8 +433,7 @@
if (typeof payload === 'string') {
return {
title: form.theme.trim(),
content: payload,
summary: '接口直接返回文本内容'
content: payload
};
}
@@ -468,8 +442,7 @@
if (isFailedPayload(payload)) {
return {
title: form.theme.trim(),
content: FAILED_TEXT,
summary: '上游工作流执行失败'
content: FAILED_TEXT
};
}
@@ -497,10 +470,6 @@
payload.choices?.[0]?.message?.content
);
const summary = Object.keys(payload).length
? `已解析返回字段:${Object.keys(payload).join('、')}`
: '返回对象为空';
return {
title: title || form.theme.trim(),
// 不再 stringifyUnknown(payload):整包会带出 taskId / workflowRunId 等
@@ -509,8 +478,7 @@
payload,
scope: 'gxmu/manuscriptGen.parseResult',
candidates: []
}),
summary
})
};
};
@@ -532,14 +500,10 @@
return payload;
};
const applyParsedResult = (
payload: ManuscriptGenResult,
defaultSummary = '已通过 WebSocket 接收生成结果'
) => {
const applyParsedResult = (payload: ManuscriptGenResult) => {
const parsed = parseResult(payload);
generatedTitle.value = parsed.title || generatedTitle.value || form.theme.trim();
generatedContent.value = parsed.content;
responseSummary.value = parsed.summary || defaultSummary;
};
const shouldTreatAsDirectResult = (payload: unknown) => {
@@ -620,7 +584,6 @@
} else {
generatedContent.value += chunk;
}
responseSummary.value = '正在通过 WebSocket 接收流式内容';
};
const finishStream = (conversationId?: string) => {
@@ -628,7 +591,7 @@
activeConversationId.value = conversationId;
}
const payload = parseSerializedResult(generatedContent.value);
applyParsedResult(payload, '已通过 WebSocket 接收生成结果');
applyParsedResult(payload);
loading.value = false;
resolveStream();
disconnectWebSocket();
@@ -741,7 +704,6 @@
const clearGenerated = () => {
generatedTitle.value = '';
generatedContent.value = '';
responseSummary.value = '';
};
const resetForm = () => {
@@ -850,7 +812,6 @@
loading.value = true;
clearGenerated();
generatedTitle.value = form.theme.trim();
responseSummary.value = '正在提交生成任务...';
try {
// 一律走 HTTP 流式(/stream),不再先连 WebSocket:
// 触发生成的那个 POST 在整段生成期间是零字节的,会被链路上的 60s 空闲
@@ -858,9 +819,6 @@
// /stream 每 15s 有后端心跳续命,正文逐段喂给原来的帧处理,
// 结构化结果从末帧 {type:'final'} 取。
const payload = buildPayload();
responseSummary.value = templateMode.value
? '已提交生成任务,等待流式返回...'
: '自由创作已提交给 AI 创意助手,生成完成后一次性返回...';
const waitStream = createStreamWaiter();
const result = await runManuscriptGenStream(
payload as unknown as Record<string, unknown>,
@@ -871,8 +829,7 @@
applyParsedResult(
typeof result === 'string'
? parseSerializedResult(result)
: (result as ManuscriptGenResult),
'接口直接返回生成结果'
: (result as ManuscriptGenResult)
);
}
// 兜底:流结束但没收到 __END__ 时也别把页面卡在加载中
@@ -1107,13 +1064,6 @@
gap: 12px;
}
.upload-tip {
margin-top: 10px;
color: #5f7f9f;
font-size: 12px;
line-height: 1.7;
}
.file-list {
display: flex;
flex-direction: column;
@@ -1173,26 +1123,6 @@
color: #1590ff;
}
.hint-block {
margin-top: 18px;
padding: 14px 16px;
border-radius: 14px;
background: #f5fbff;
border: 1px solid #c7e8ff;
}
.hint-title {
margin-bottom: 6px;
font-weight: 600;
color: #1590ff;
}
.hint-text {
color: #5f7f9f;
line-height: 1.7;
font-size: 13px;
}
.result-card {
min-height: 100%;
}
@@ -1203,12 +1133,6 @@
gap: 10px;
}
.response-meta {
padding: 12px 14px;
border-radius: 14px;
background: #f5fbff;
}
.markdown-shell {
min-height: 360px;
border: 1px solid #c7e8ff;
@@ -1244,18 +1168,6 @@
color: #1f2d3d;
}
.meta-label {
margin-bottom: 4px;
font-weight: 600;
color: #1f2d3d;
}
.meta-value {
color: #5f7f9f;
line-height: 1.7;
word-break: break-all;
}
.full-width {
width: 100%;
}