From 488cf8a30bcce0a1ab0fc4960f17c11ceb48efd8 Mon Sep 17 00:00:00 2001 From: ymq Date: Wed, 16 Sep 2026 14:50:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(todos):=20=E4=B8=A4=E5=A4=84=E5=BE=85?= =?UTF-8?q?=E5=8A=9E=E4=BA=A4=E4=BA=92=E7=BC=BA=E9=99=B7(2026-09-16?= =?UTF-8?q?=E7=94=98=E8=82=83=E9=A1=B9=E7=9B=AE=E5=AE=9E=E6=B5=8B)?= =?UTF-8?q?=E2=80=94=E2=80=94=E2=91=A0bid=5Fdelivery=5Fconfirm=E5=88=86?= =?UTF-8?q?=E6=94=AF=E5=8F=AA=E6=B8=B2=E6=9F=93=E6=96=87=E4=BB=B6=E8=A1=8C?= =?UTF-8?q?=E5=BF=98=E4=BA=86action=E6=8C=89=E9=92=AE(=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E5=85=9C=E5=BA=95=E8=A2=ABelif=E6=8C=A1=E4=BD=8F)=E2=86=92?= =?UTF-8?q?=E4=BA=A4=E4=BB=98=E7=A1=AE=E8=AE=A4=E5=BE=85=E5=8A=9E=E6=88=90?= =?UTF-8?q?=E6=AD=BB=E5=8D=A1=E7=89=87=E6=97=A0=E6=B3=95=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?;=E8=A1=A5'=E7=A1=AE=E8=AE=A4=E4=BA=A4=E4=BB=98'=E6=8C=89?= =?UTF-8?q?=E9=92=AE(=E5=8F=AF=E9=80=89=E5=A4=87=E6=B3=A8+=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E7=A1=AE=E8=AE=A4,=E8=B5=B0human=5Ftask=5Fcomplete,do?= =?UTF-8?q?ne=E5=90=8E=E5=AF=B9=E8=B4=A6=E5=99=A8G=E6=AE=B5=E8=87=AA?= =?UTF-8?q?=E5=8A=A8delivered+completed)+=E6=A0=87=E4=B9=A6docx=E5=90=8C?= =?UTF-8?q?=E5=90=8D=E4=BA=A7=E7=89=A9=E4=B8=80=E5=B9=B6=E7=BB=99=E5=87=BA?= =?UTF-8?q?=20=E2=91=A1=5Ffile=5Flink=5Frow=20md/txt=E7=82=B9'=E6=89=93?= =?UTF-8?q?=E5=BC=80'=E6=9B=BEwindow.open(project=5Ffile.dspy)=E2=86=92?= =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E4=B8=8D=E8=83=BD=E6=B8=B2=E6=9F=93?= =?UTF-8?q?md=E8=90=BD=E6=88=90=E4=BB=A5URL=E5=90=8D=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E8=BD=BD;=E6=94=B9PopupWindow+widgetBuild(Md?= =?UTF-8?q?Widget=20md=5Furl)=E5=9C=A8=E7=BA=BF=E6=9F=A5=E7=9C=8B(?= =?UTF-8?q?=E5=AF=B9=E9=BD=90cockpit=5Fcards=E5=AE=9E=E8=AF=81=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8E2026-09-03=E4=BA=A4=E4=BB=98=E4=BB=B6?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E8=A7=84=E8=8C=83),office=E7=B1=BB=E5=A6=82?= =?UTF-8?q?=E5=AE=9E=E6=A0=87'=E4=B8=8B=E8=BD=BD=E6=9F=A5=E7=9C=8B'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wwwroot/api/todo_detail.dspy | 49 ++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/wwwroot/api/todo_detail.dspy b/wwwroot/api/todo_detail.dspy index b94d893..948ebc7 100644 --- a/wwwroot/api/todo_detail.dspy +++ b/wwwroot/api/todo_detail.dspy @@ -95,10 +95,32 @@ def _btn(label, css, script, conform=None): def _file_link_row(fname, project_id, fpath): - """待办中的输出文件行:文件名 + 打开/下载按钮(链接到项目空间内的真实文件)。""" + """待办中的输出文件行:文件名 + 打开/下载按钮(链接到项目空间内的真实文件)。 + + 打开(2026-09-16 修复):md/txt 点「打开」曾直接 window.open(project_file.dspy) + ——浏览器不能原生渲染 md,落成以 URL 名(project_file.dspy)命名的下载。 + 改为弹出内嵌 MdWidget(md_url 拉取渲染)的在线查看窗口(对齐 2026-09-03 + 交付件渲染规范:md/txt → MdWidget 渲染 + 下载链接)。 + office/pdf 等浏览器不可内联渲染的类型「打开」等同下载(如实标注按钮文案)。 + """ open_url = (entire_url("/pipeline-sdlc/api/project_file.dspy") + "?project_id=" + _quote(project_id) + "&path=" + _quote(fpath)) dl_url = open_url + "&download=1" + ext = os.path.splitext(fname or '')[1].lower() + if ext in ('.md', '.txt', '.markdown'): + # 在线查看:PopupWindow + widgetBuild(MdWidget)(对齐 cockpit_cards.dspy 实证模式: + # JS 构造器不认 subwidgets,必须 content_w.add_widget;md_url 拉取渲染, + # 超长 md 也不撑爆 widget JSON) + open_script = ( + "var pw=new bricks.PopupWindow({title:" + _json.dumps("查看 " + fname) + + ",cwidth:70,cheight:36,auto_open:true});" + "bricks.widgetBuild({widgettype:'MdWidget',options:{md_url:" + + _json.dumps(open_url) + ",width:'100%'}},pw.content_w)" + ".then(function(w){if(w)pw.content_w.add_widget(w);});") + open_btn = _btn("在线查看", "small", open_script) + else: + open_btn = _btn("下载查看", "small", + "window.open(" + _json.dumps(dl_url) + ",'_blank');") return {"widgettype": "HBox", "options": {"width": "100%", "gap": "8px", "padding": "6px 14px", "alignItems": "center", "bgcolor": "#f8fafc", @@ -107,7 +129,7 @@ def _file_link_row(fname, project_id, fpath): {"widgettype": "Text", "options": {"text": "📎 " + fname, "cfontsize": 0.85, "color": "#334155", "flex": "1 1 auto", "overflow": "hidden", "textOverflow": "ellipsis", "whiteSpace": "nowrap"}}, - _btn("打开", "small", "window.open(" + _json.dumps(open_url) + ",'_blank');"), + open_btn, _btn("下载", "small", "window.open(" + _json.dumps(dl_url) + ",'_blank');") ]} @@ -525,11 +547,34 @@ async with DBPools().sqlorContext(dbname) as sor: _bf = _s(getattr(b0, 'file_path', '')) if _bf: _file_rows.append(_file_link_row(os.path.basename(_bf), project_id, _bf)) + # 2026-09-16:md 有 docx 同名产物时一并给出(标书最终交付 docx) + _bf_docx = os.path.splitext(_bf)[0] + '.docx' + if _bf_docx != _bf: + _file_rows.append(_file_link_row(os.path.basename(_bf_docx), project_id, _bf_docx)) else: md.append('## 待确认标书') md.append('') md.append('未找到该项目的标书文件记录。') md.append('') + # 确认交付按钮(2026-09-16 修复:此分支曾只渲染文件行,忘了 action 按钮, + # 通用兜底分支又被 elif 挡住 → 待办成死卡片无法确认)。 + # 通道 = human_task_complete(同机构+assignee 校验);置 done 后对账器 + # G 段自动收尾:标书 delivered + 项目 completed。 + action_widgets.append(_input_box( + 'bid_confirm_note', '交付备注(可选:用印/密封/份数/递交方式核对说明)', '可选备注')) + _ok_script = (_read_input_js('bid_confirm_note') + + "var rd=JSON.stringify({content:(cv||'确认交付')});" + + _post_js(complete_url, [('human_task_id', _json.dumps(oid)), + ('result_data', 'rd')]) + _tail) + action_widgets.append({ + "widgettype": "HBox", + "options": {"width": "100%", "gap": "8px", "padding": "12px 14px", "halign": "right"}, + "subwidgets": [ + _btn('确认交付(项目完成)', 'primary', _ok_script, + conform={"title": "确认交付", "message": "确认标书已复核(用印/密封/份数/递交)?确认后标书置为已交付、项目完成。", + "conform": {"label": "确认交付"}, "discard": {"label": "再看看"}}) + ] + }) elif task_type == 'flow_plan_confirm': # 产线流程裁剪确认:正文(description)已含裁剪方案对照表(propose 时渲染),