From 56c7e227afb654948a305fe6451ffbf9f68475e8 Mon Sep 17 00:00:00 2001 From: Bifang <915779419@qq.com> Date: Mon, 11 May 2026 12:32:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E8=A1=8C=E7=89=88=E6=9C=AC1.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/assets/app.js | 107 ++++++++++++++++++++----- template_guides/template2.md | 19 ++--- web/__pycache__/server.cpython-314.pyc | Bin 51292 -> 51582 bytes web/server.py | 7 +- 4 files changed, 103 insertions(+), 30 deletions(-) diff --git a/frontend/assets/app.js b/frontend/assets/app.js index 878557f..4ca5365 100644 --- a/frontend/assets/app.js +++ b/frontend/assets/app.js @@ -160,8 +160,15 @@ function renderMeetingStatus(meeting) { function refreshActionButtons() { const canProcess = Boolean(state.meetingId) && !state.processing && !state.guideBusy; + const currentMeeting = meetingById(state.meetingId); $("#btn-process").disabled = !canProcess; - $("#btn-process").textContent = state.processing ? "总结中" : state.guideBusy ? "等待" : "总结"; + $("#btn-process").textContent = state.processing + ? "总结中" + : state.guideBusy + ? "等待" + : currentMeeting?.has_summary + ? "重总结" + : "总结"; const canEditResult = Boolean(state.meetingId) && !state.processing && !state.guideBusy; $("#btn-toggle-result-edit").disabled = !canEditResult; @@ -268,6 +275,64 @@ function pushStandaloneReparseLine(text) { box.textContent = lines.slice(-6).join("\n"); } +async function streamTemplateGuideReparse(templateName, userNotes = "", handlers = {}) { + const params = new URLSearchParams(); + if (userNotes.trim()) { + params.set("user_notes", userNotes.trim()); + } + const suffix = params.toString() ? `?${params.toString()}` : ""; + const source = new EventSource( + `/api/templates/${encodeURIComponent(templateName)}/guide/reparse/stream${suffix}`, + ); + + try { + const result = await new Promise((resolve, reject) => { + let contentAcc = ""; + + source.onmessage = (event) => { + if (!event.data) { + return; + } + const payload = JSON.parse(event.data); + if (payload.type === "status") { + handlers.onStatus?.(payload.data); + return; + } + if (payload.type === "chunk") { + const chunk = payload.data?.text || ""; + if (chunk) { + contentAcc += chunk; + handlers.onChunk?.(chunk, contentAcc, payload.data); + } + return; + } + if (payload.type === "done") { + resolve(payload.data || { name: templateName, content: contentAcc }); + return; + } + if (payload.type === "error") { + reject(new Error(payload.data || "解析失败")); + } + }; + + source.onerror = () => { + reject(new Error("解析连接中断")); + }; + }); + + state.templates = state.templates.map((item) => ( + item.name === templateName ? { ...item, has_guide: true } : item + )); + if (state.rightResource?.name === templateName && state.rightResource.type === "template") { + state.rightResource.hasGuide = true; + } + await refreshRightResourceAfterGuideReparse(templateName, result.content || ""); + return result; + } finally { + source.close(); + } +} + async function runStandaloneGuideReparseFlow() { const templateName = state.templateName; const userNotes = $("#reparse-modal-notes").value || ""; @@ -1084,24 +1149,14 @@ async function reparseTemplateGuideFromModal(userNotes = "") { } async function runTemplateGuideReparse(templateName, userNotes = "") { - const params = new URLSearchParams(); - if (userNotes.trim()) { - params.set("user_notes", userNotes.trim()); - } - const suffix = params.toString() ? `?${params.toString()}` : ""; - const result = await api(`/api/templates/${encodeURIComponent(templateName)}/guide/reparse${suffix}`, { - method: "POST", + return streamTemplateGuideReparse(templateName, userNotes, { + onStatus() { + $("#guide-modal-subtitle").textContent = "正在结合补充说明重新解析模板..."; + }, + onChunk(_chunk, contentAcc) { + $("#process-guide-editor").value = contentAcc; + }, }); - - state.templates = state.templates.map((item) => ( - item.name === templateName ? { ...item, has_guide: true } : item - )); - - if (state.rightResource?.name === templateName && state.rightResource.type === "template") { - state.rightResource.hasGuide = true; - } - await refreshRightResourceAfterGuideReparse(templateName, result.content || ""); - return result; } function startMeetingProcess(userNotes = "") { @@ -1225,12 +1280,24 @@ $("#btn-process-guide-edit").addEventListener("click", async () => { } }); -$("#btn-process-guide-reparse").addEventListener("click", () => { +$("#btn-process-guide-reparse").addEventListener("click", async () => { if (state.processing || state.guideBusy) { return; } - showReparseGuidePrompt(); + $("#btn-process-guide-reparse").disabled = true; + try { + if (state.processGuideEditMode) { + await saveProcessGuideFromModal(); + } + hideReparseGuidePrompt(); + closeModal("modal-process-guide"); + openStandaloneReparseModal(); + } catch (error) { + toast(error.message, "err"); + } finally { + $("#btn-process-guide-reparse").disabled = false; + } }); $("#btn-reparse-guide-cancel").addEventListener("click", () => { diff --git a/template_guides/template2.md b/template_guides/template2.md index 0538f63..e6a6c06 100644 --- a/template_guides/template2.md +++ b/template_guides/template2.md @@ -1,9 +1,10 @@ -- 严格保留模板的Markdown标题层级体系(# 总标题 > ## 内容主体 > ### 一级板块 > #### 发言人/细分板块),后续生成时不得随意更改层级关系。 -- 顶部元数据(议题、时间、地点、主持人、参加人)必须基于实际会议信息填充,所有占位符(X、XX、XXX等)需替换为真实数据,严禁原样输出。 -- “议程”区块需保留标题,其下列表项数量与内容严格按实际会议流程动态调整,无实际议程时省略该区块。 -- “汇报通报”区块需替换具体部门名称与量化数据,若会议记录中无汇报环节,则整体省略该小节,不强行补齐。 -- “领导部署/强调”区块需保留结构框架,将占位姓名替换为实际发言人;该区块下的发言人数量、业务分类标题(**XX方面:**)及下级详情列表需与真实讲话内容严格对应,动态增减,无依据时直接省略。 -- 所有示例性文字、演示性标题及占位内容必须在生成前全部替换为实际业务内容,禁止保留模板中的示例字符。 -- 使用水平分割线(---)作为区块分隔符,依次分隔头部信息、议程、汇报板块与部署板块,保持文档结构清晰。 -- 严格保留格式规范:领导发言使用有序列表(1. 2. 3.),业务分类使用加粗文本(**分类名称:**),具体指示或要点使用无序列表(- ),有内容时必须完整套用此层级格式。 -- 遵循“无依据不填充”原则,模板中未被实际会议内容支撑的空白项、占位项或示例项必须直接删除,不得虚构或强行对齐模板结构。 \ No newline at end of file +- 标题层级固定为 `# 会议记录` → `## 会议内容` → `### 章节标题` → `#### 发言人强调` → `1. **方面名称**` → `- 具体条目`,共六级层级,后续生成须严格保持该层级结构 +- 会议基本信息(议题、时间、地点、主持人、参加人、议程)为固定区块,信息必须完整保留,不可省略 +- 议程列表项为固定结构(各部门汇报、领导指示部署),顺序不可调整,但具体部门名称可按实际参会情况替换 +- 所有 `X`、`XX`、`XXX` 形式占位符须替换为真实内容,禁止原样输出 +- 所有数字占位符(如 `X项`、`XX项`)须替换为具体统计数字,禁止原样输出 +- 领导强调部分的结构(多个 `#### X总强调` 依次排列,每个下面分多个 `1. **方面**` 条目)为固定格式,每个领导强调至少包含一个方面,最多不超过四个 +- 方面名称(如“政企方面”“市场方面”“云数方面”等)使用 `**加粗**` 格式,后续生成须保持加粗 +- 具体部署内容使用 `-` 列表项,每方面至少一条,至多三条 +- 若某位领导未对某些方面作指示,该方面标题及条目应整体省略,不保留空结构 +- 模板中“详见汇报材料”等提示性文字在无对应材料时应删除,不强行保留 \ No newline at end of file diff --git a/web/__pycache__/server.cpython-314.pyc b/web/__pycache__/server.cpython-314.pyc index f48711100ca239ec369a8248b0fd33d8609f43fa..0d5370093a8c729df06dfc64c436f9dd49c3ff2d 100644 GIT binary patch delta 821 zcmX|9Ye-XJ82;Y#?QC9;=H@WWwk)g_8bE)dFd*1hXuirUgFW&HB>420N zhnRe-fcoC{ap_%xCMn0(EMKuHiwW1dj%grg6)`p9j__j|M=fGvDK!F5+!75tyV(1C zjHygyKsQl3ZTIfHAX<@GZ50j*yO9yu{->ys9V4`Z1=b;(YO4rh-Yo8QVH;yN#Vpu( z@u*laHEPp4@ZqgGR$M2TRW6*59pFGmaRzcg9>XS$)g+K5mP0->UN zi@l@Jrfl@Cs6^7uW~RUT4vD0PBU67BbSn4zOR(C{Z1RvU4|u>wErTZ^p!5xn12fJJ zl|!o%7;+0RMRkF7m{YbrItMUIFFpPOA1T~URq}_`03T?@om|>HG7bxh>B(_`d0P8) z6~aozGnD{K%Hb1(14LZ5Q}wHeZ)qYOspVXDNGL~ii- z0Xi{p1iI|B{{XhK>0|%^ delta 522 zcmX9*O-NKx6u#%)H^Kb)q?k;IG%O}wp`zvBrcERgmXU}OZH9)S{I@qVG@%2dLJ@(y z=14tt5H0(`S~Ohhxo9Mikv5r&P?=h4;BG`*1VeS+co*My&Ubz}_is}CGK6|gEpah= zH=Cf_8cwTOkE7&%=yt8ld3wf9aH!d31oPp%O`k>4(={pxa=aJwI_t@-VwEg`BSyt_ z-oe^L2e0L)ScB*SO=;J-{*zMlZ#GogFh-z({@W&TAQgkQ9u@>X-%0%pajpzU%(6y( z92o286s#g~hb7pSdV03~gZInXAL_M4^QPyBaoN;nJJ;ORYfAN>utI60M~`73nT4qI z;f~_YIh$g^&|su%pdYmYS84bzTV7uT zSjES0c40#fy;V4@%fvzh95(EEi`4SqFi!!~@e0_M+wUGZVM6wOY=iRRPE*Cj2wJsL zeb_EAapRL8CQ-F+z!U~7FHB?9iUX5-R<8q^F_4*{#rHE0U`q}x848%m