# workspace_popup.dspy - 返回 PopupWindow,内含 ResourceBrowser(含打开/删除 tools) import json as _json uid = await get_user() if not uid: uid = 'user-01' dbname = get_module_dbname('pipeline-sdlc') import os session_id = (params_kw or {}).get('session_id', '') or '' pipeline_id = (params_kw or {}).get('pipeline_id', '') or '' # 查询串:session_id + pipeline_id 一并透传给树/文件/查看/编辑/删除子端点 # (子端点各自用 get_project_dir_pl 做产线隔离,跨产线项目一律视为无项目) _qs = [] if session_id: _qs.append("session_id=" + session_id) if pipeline_id: _qs.append("pipeline_id=" + pipeline_id) _sid_q = ("?" + "&".join(_qs)) if _qs else "" _sid_js = _json.dumps(("&" + "&".join(_qs)) if _qs else "") async with DBPools().sqlorContext(dbname) as sor: # 产线隔离:传了 pipeline_id 时,会话项目跨产线则视为无项目 # (商机/投标产线入口不显示开发产线项目的空间) project_dir, _ = await get_project_dir_pl(sor, uid, session_id, pipeline_id) # 通用会话(pipeline_id=_generic):锁用户专属目录 _general/{uid}, # 不进入任何项目的工作空间(2026-09-08 用户要求) if pipeline_id == '_generic': _gd = generic_workspace_dir(uid) os.makedirs(_gd, exist_ok=True) project_dir = _gd space_dir = _gd pname = os.path.basename(project_dir) if project_dir else '' if not project_dir: # 2026-09-11 用户要求:没当前项目 → 提示选择或新建项目(给直达按钮,不给死胡同) _proj_base = entire_url("/pipeline_core/api/agent_project_popup.dspy") _proj_q = [] if session_id: _proj_q.append("session_id=" + session_id) if pipeline_id: _proj_q.append("pipeline_id=" + pipeline_id) def _pbtn(label, css, op): _js = ("var r=await fetch(" + _json.dumps(_proj_base + "?op=" + op + (("&" + "&".join(_proj_q)) if _proj_q else "")) + ");" "var d=await r.json();if(d){bricks.widgetBuild(d,bricks.app);}") return {"widgettype": "Button", "options": {"label": label, "css": css}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": _js}]} resp = { "widgettype": "PopupWindow", "id": "ws_no_project_pw", "options": {"title": "提示", "cwidth": 38, "cheight": 10, "auto_open": True}, "subwidgets": [{ "widgettype": "VBox", "options": {"width": "100%", "gap": "12px", "padding": "18px 22px"}, "subwidgets": [ {"widgettype": "Text", "options": {"otext": "当前会话还没有项目,请先选择或新建项目", "text": "当前会话还没有项目,请先选择或新建项目", "i18n": True, "cfontsize": 1, "color": "#64748b"}}, {"widgettype": "HBox", "options": {"width": "100%", "gap": "10px"}, "subwidgets": [_pbtn("✨ 新建项目", "primary", "new"), _pbtn("🔀 切换项目", "small", "switch")]} ] }] } else: view_url = entire_url("/pipeline-sdlc/api/workspace_view.dspy") edit_url = entire_url("/pipeline-sdlc/api/workspace_open.dspy") delete_url = entire_url("/pipeline-sdlc/api/workspace_delete.dspy") + _sid_q # 查看:txt/md 用 MdWidget 渲染,其他下载查看 view_script = ( "if(!bricks.app._ws_sel)return;" "var r=await fetch(" + _json.dumps(view_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id)+" + _sid_js + ");" "var d=await r.json();" "bricks.widgetBuild(d,bricks.app);" ) # 编辑:文本 vi 编辑、媒体播放、图片预览、其他下载 edit_script = ( "if(!bricks.app._ws_sel)return;" "var r=await fetch(" + _json.dumps(edit_url) + "+'?id='+encodeURIComponent(bricks.app._ws_sel.id)+" + _sid_js + ");" "var d=await r.json();" "bricks.widgetBuild(d,bricks.app);" ) # 删除:删除选中文件并刷新 delete_script = ( "if(!bricks.app._ws_sel)return;" "var body=new URLSearchParams();" "body.append('id',bricks.app._ws_sel.id);" "var resp=await fetch(" + _json.dumps(delete_url) + ",{method:'POST',body:body});" "var rj=await resp.json();" "if(rj.status=='ok'){" "bricks.app._ws_sel=null;bricks.app._ws_sel_el=null;" "var rb=bricks.getWidgetById('ws_rb',bricks.app);" "if(rb){var cid=rb.current_id;rb.current_id=undefined;await rb.render_browser(cid);}" "}else{var m=new bricks.Message({title:'删除失败',message:rj.message||'未知错误'});m.open();}" ) resp = { "widgettype": "PopupWindow", "options": { "title": pname + " - 工作空间", "width": "85%", "height": "80%", "auto_open": True }, "subwidgets": [{ "widgettype": "ResourceBrowser", "id": "ws_rb", "options": { "tree_options": { "widgettype": "Tree", "options": { "dataurl": entire_url("/pipeline-sdlc/api/workspace_tree.dspy") + _sid_q, "textField": "label", "idField": "id", "cfontsize": 1.0 } }, "browser_options": { "widgettype": "urlwidget", "options": { "url": entire_url("/pipeline-sdlc/api/workspace_files.dspy") + _sid_q }, "tools": [ { "widgettype": "Button", "options": {"label": "查看", "css": "small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": view_script}] }, { "widgettype": "Button", "options": {"label": "编辑", "css": "small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": edit_script}] }, { "widgettype": "Button", "options": {"label": "删除", "css": "small"}, "binds": [{ "wid": "self", "event": "click", "actiontype": "script", "target": "self", "conform": { "title": "删除确认", "message": "确认删除选中的文件?", "conform": {"label": "删除"}, "discard": {"label": "取消"} }, "script": delete_script }] } ] }, "tree_width": "280px" } }] } return _json.dumps(resp, ensure_ascii=False)