feat(paper): UI 优化(卡片文字颜色/去模式按钮) + 模板下拉加论文选项并接线渲染

This commit is contained in:
2026-10-08 15:17:44 +08:00
parent 8b228923bd
commit 43a7c6c132
8 changed files with 24 additions and 30 deletions
@@ -35,8 +35,10 @@ async def get_paper(paper_id: str, format: str = "md") -> dict[str, Any]:
{"n": c.get("final_n") or c.get("temp_n"), "text": c["ref_text"], "kind": c["kind"],
"source_doc_id": c["source_doc_id"]} for c in citations
]
brief = loads_json(paper.get("brief_json"), {})
return {
"ok": True, "paper_id": paper_id, "title": paper["title"], "status": paper["status"],
"template_key": brief.get("template_key") or "document",
"sections": [
{"section_id": s["section_id"], "title": s["title"], "status": s["status"],
"word_count": s["word_count"]} for s in sections
@@ -33,6 +33,7 @@ async def propose_paper_outline(
format_req: str = "",
style: str = "",
kb_hints: list[dict[str, Any]] | None = None,
template_key: str = "",
) -> dict[str, Any]:
# 1. 知识库线索:优先用调用方传入的 kb_hints,否则现场 search_refs(失败静默降级)
hints = list(kb_hints or [])
@@ -83,7 +84,7 @@ async def propose_paper_outline(
paper_id, title, paper_type, language, format_req, style,
dumps_json({
"topic": topic, "requirements": requirements, "length": length,
"kb_hints": hints,
"kb_hints": hints, "template_key": template_key,
}),
dumps_json({
"title": title,
+2 -1
View File
@@ -24,6 +24,7 @@ class PaperTurnRequest(BaseModel):
session_id: str
text: str
paper_id: str | None = None
template_key: str = ""
class PaperConfirmRequest(BaseModel):
@@ -50,7 +51,7 @@ async def paper_health() -> dict[str, Any]:
@router.post("/turns")
async def paper_turn(payload: PaperTurnRequest, db: Database = Depends(get_db)) -> dict[str, Any]:
blocks = await agent.handle_turn(db, payload.session_id, payload.text, payload.paper_id)
blocks = await agent.handle_turn(db, payload.session_id, payload.text, payload.paper_id, payload.template_key)
return {"ok": True, "blocks": blocks}
+9 -9
View File
@@ -92,15 +92,15 @@ def _missing_brief_keys(brief: dict[str, str]) -> list[str]:
# 状态机入口
# ---------------------------------------------------------------------------
async def handle_turn(db, session_id: str, text: str, paper_id: str | None = None) -> list[dict[str, Any]]:
async def handle_turn(db, session_id: str, text: str, paper_id: str | None = None, template_key: str = "") -> list[dict[str, Any]]:
"""一轮对话:推进状态机并落库助手消息,返回消息的 blocks 列表。"""
client = _new_client()
if not paper_id:
return await _trigger(db, session_id, text, client)
return await _followup(db, session_id, text, paper_id, client)
return await _trigger(db, session_id, text, client, template_key)
return await _followup(db, session_id, text, paper_id, client, template_key)
async def _trigger(db, session_id: str, text: str, client: PaperMcpClient) -> list[dict[str, Any]]:
async def _trigger(db, session_id: str, text: str, client: PaperMcpClient, template_key: str = "") -> list[dict[str, Any]]:
brief = await extract_brief(text)
missing = _missing_brief_keys(brief)
if missing:
@@ -112,7 +112,7 @@ async def _trigger(db, session_id: str, text: str, client: PaperMcpClient) -> li
await _write_message(db, session_id, blocks=blocks, text="请补充论文信息")
return blocks
outline = await client.call_tool("propose_paper_outline", brief)
outline = await client.call_tool("propose_paper_outline", {**brief, "template_key": template_key})
if not outline.get("ok"):
blocks = [_text_block(f"论文大纲生成失败:{outline.get('message', '服务未就绪')},请重试。")]
await _write_message(db, session_id, blocks=blocks, text=blocks[0]["html"])
@@ -147,7 +147,7 @@ def _is_cancel_request(text: str) -> bool:
return bool(s) and any(w in s for w in _CANCEL_WORDS)
async def _followup(db, session_id: str, text: str, paper_id: str, client: PaperMcpClient) -> list[dict[str, Any]]:
async def _followup(db, session_id: str, text: str, paper_id: str, client: PaperMcpClient, template_key: str = "") -> list[dict[str, Any]]:
prog = await client.call_tool("get_paper_progress", {"paper_id": paper_id})
if not prog.get("ok"):
blocks = [_text_block(f"未找到论文({paper_id}),请重新发起撰写。")]
@@ -172,7 +172,7 @@ async def _followup(db, session_id: str, text: str, paper_id: str, client: Paper
if status in ("outline", "writing"):
# 大纲/撰写阶段:把 text 视为补充要求,重新生成大纲(原大纲作废)
brief = await extract_brief(text)
outline = await client.call_tool("propose_paper_outline", {**brief, "topic": brief.get("topic") or text})
outline = await client.call_tool("propose_paper_outline", {**brief, "topic": brief.get("topic") or text, "template_key": template_key})
if outline.get("ok"):
await client.call_tool("cancel_paper", {"paper_id": paper_id})
return await _outline_message(db, session_id, outline)
@@ -289,10 +289,10 @@ async def _artifact_message(db, session_id: str, paper: dict[str, Any], nl: str)
refs = paper.get("references") or []
title = paper.get("title", "")
full_md = _build_full_markdown(paper)
html, css = templating.render_html(full_md, title=title, key="document")
html, css = templating.render_html(full_md, title=title, key=paper.get("template_key") or "document")
artifact_id = await insert_artifact(
db, session_id=session_id, title=title,
markdown=full_md, template_name="论文", template_key="document",
markdown=full_md, template_name="论文", template_key=paper.get("template_key") or "document",
template_css=css, html=html,
)
blocks = [
+2 -14
View File
@@ -13,7 +13,7 @@
// qa → 原 emit('send') 路径(附件注记进消息文本)。
// 路由收敛在本组件内,ChatPanel 等上游组件零改动。
import { ref, computed, onMounted } from 'vue';
import { MODES as MODE_LABELS, T } from '../../i18n/labels.js';
import { T } from '../../i18n/labels.js';
import { DG, BUILTIN_TEMPLATES } from '../../i18n/docgen.js';
import { BF } from '../../i18n/bioflow.js';
import { route, paperIntent } from '../../intent.js';
@@ -315,7 +315,7 @@ const submit = () => {
}
if (paperRoute) {
try {
useChatStore().sendPaper(text).catch(() => {
useChatStore().sendPaper(text, { templateId: selectedTemplateId.value || '' }).catch(() => {
/* sendPaper 内部已按失败可见原则标注错误 */
});
} catch {
@@ -390,18 +390,6 @@ defineExpose({ clear: () => { draft.value = ''; }, focus: () => textarea.value?.
@drop="onDrop"
>
<div class="composer">
<div class="mode-pills">
<button
v-for="pm in MODE_LABELS"
:key="pm.key"
type="button"
:class="['mode-pill', { on: mode === pm.key }]"
:disabled="busy || disabled"
@click="emit('change-mode', pm.key)"
>
{{ pm.label }}
</button>
</div>
<!-- 缺陷 #42:docgen 模式模板下拉(「自动推荐」+ 内置模板 + as_template 资料,懒加载) -->
<div v-if="isDocgenMode" class="template-row">
<label class="tpl-label" for="pa-template-select">{{ DG.docPaneTemplateLabel }}</label>
+3 -3
View File
@@ -54,10 +54,10 @@ function submit() {
<style scoped>
.paper-brief-card { border: 1px solid var(--border, #e5e7eb); border-radius: 8px; padding: 12px; margin: 8px 0; }
.pbc-title { font-weight: 600; margin-bottom: 4px; }
.pbc-hint { color: #6b7280; font-size: 12px; margin-bottom: 8px; }
.pbc-hint { color: #374151; font-size: 12px; margin-bottom: 8px; }
.pbc-field { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.pbc-label { min-width: 72px; font-size: 13px; color: #374151; }
.pbc-input { flex: 1; padding: 4px 8px; border: 1px solid #d1d5db; border-radius: 4px; }
.pbc-label { min-width: 72px; font-size: 13px; color: #111827; }
.pbc-input { flex: 1; padding: 4px 8px; border: 1px solid #d1d5db; border-radius: 4px; color: #111827; }
.pbc-footer { margin-top: 8px; }
.pbc-submit { padding: 6px 14px; border: none; border-radius: 6px; background: #2563eb; color: #fff; cursor: pointer; }
.pbc-done { color: #16a34a; }
+2
View File
@@ -30,6 +30,8 @@ export const BUILTIN_TEMPLATES = {
speech: '讲话稿',
plan: '方案计划',
document: '通用文档',
paper_review: '综述论文',
paper_research: '研究论文',
};
export const DG = {
+2 -2
View File
@@ -1580,7 +1580,7 @@ export const useChatStore = defineStore('chat', {
// 写稿期由 confirmPaper 触发后台循环,前端 2s 轮询 paperProgress 更新进度卡,
// complete 后重载会话取 agent 直写的 artifact 块并接管到文档 TAB。
// ===============================================================
async sendPaper(description, { userMsg } = {}) {
async sendPaper(description, { userMsg, templateId } = {}) {
const q = String(description == null ? '' : description).trim();
if (!q || this.busy) return;
const sessions = useSessionsStore();
@@ -1628,7 +1628,7 @@ export const useChatStore = defineStore('chat', {
// 2. POST turns(短同步)
let payload;
try {
payload = await paperTurn({ session_id: sessionId, text: q });
payload = await paperTurn({ session_id: sessionId, text: q, template_key: templateId || '' });
} catch (err) {
const marker = this._errorMarker(err.code);
const text = err.message || (marker && marker.text) || T.upstreamError;