diff --git a/wwwroot/api/cockpit_project_tasks.dspy b/wwwroot/api/cockpit_project_tasks.dspy new file mode 100644 index 0000000..59a9e4f --- /dev/null +++ b/wwwroot/api/cockpit_project_tasks.dspy @@ -0,0 +1,60 @@ +# cockpit_project_tasks.dspy - Return clean task list for current project +# Used by cockpit project button to show task tree + +import json + +uid = await get_user() +if not uid: + return json.dumps({"tasks": [], "error": "请先登录"}, ensure_ascii=False) + +dbname = get_module_dbname('pipeline-sdlc') + +# Get current project from agent_settings +async with DBPools().sqlorContext(dbname) as sor: + recs = await sor.sqlExe( + "SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${uid}$", + {"uid": uid}) + pid = getattr(recs[0], 'current_project_id', '') if recs else '' + +if not pid: + return json.dumps({"tasks": [], "error": "没有当前项目"}, ensure_ascii=False) + +# Get project name +pname = '' +precs = await sor.sqlExe( + "SELECT name FROM sd_projects WHERE id=${pid}$", {"pid": pid}) +if precs and len(precs) > 0: + pname = getattr(precs[0], 'name', '') or '' + +# Get tasks - filter by tenant_id matching project's org +# Use pipeline_task's DB or shared pipeline DB +tasks = await sor.sqlExe( + "SELECT id, title, state, current_version, created_at, params, role " + "FROM pipeline_tasks WHERE tenant_id='0' ORDER BY created_at DESC LIMIT 50", + {}) + +task_list = [] +for t in tasks: + d = {} + # Only copy real data fields, skip methods + raw = vars(t) if hasattr(t, '__dict__') else {} + for k, v in raw.items(): + if not callable(v) and not k.startswith('_'): + # Extract short description from params JSON + if k == 'params' and v: + try: + p = json.loads(v) if isinstance(v, str) else v + desc = p.get('description', '') or p.get('input_text', '') or '' + d['description'] = desc[:60] + except: + d['description'] = '' + elif k not in ('clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'to_dict'): + v_str = str(v) if v is not None else '' + d[k] = v_str + task_list.append(d) + +return json.dumps({ + "project_name": pname, + "project_id": pid, + "tasks": task_list +}, ensure_ascii=False) diff --git a/wwwroot/index.ui b/wwwroot/index.ui index 57d21c4..7b4289b 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -61,7 +61,7 @@ "event": "click", "actiontype": "script", "target": "self", - "script": "var pw=new bricks.PopupWindow({title:'选择项目',cwidth:32,cheight:16,auto_open:true});var form=new bricks.Form({name:'project_picker',cols:1,fields:[{name:'project_id',uitype:'code',label:'项目',placeholder:'请选择项目',cwidth:26,dataurl:'/pipeline-sdlc/api/cockpit_project_picker.dspy',valueField:'value',textField:'text'}]});var vb=new bricks.VBox({padding:'12px',gap:'12px'});vb.add_widget(new bricks.Text({text:'请选择项目:',cfontsize:0.95,color:'#64748b'}));vb.add_widget(form);pw.content_w.add_widget(vb);setTimeout(function(){var sel=pw.content_w.dom_element.querySelector('select');if(sel)sel.addEventListener('change',function(){var v=sel.value;if(!v)return;fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'project_id='+encodeURIComponent(v)}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var pl=bricks.getWidgetById('project_label',bricks.app);var il=bricks.getWidgetById('iteration_label',bricks.app);if(pl)pl.text_w.set_text(r.project_name?'📁 当前项目:'+r.project_name:'📁 当前项目:--');if(il)il.text_w.set_text('🔄 当前迭代:'+(r.iteration_name||'--'));var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.setValue(r);}});});},500);" + "script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var pid=d.project_id||'';if(!pid){var pw=new bricks.PopupWindow({title:'选择项目',cwidth:32,cheight:16,auto_open:true});var form=new bricks.Form({name:'project_picker',cols:1,fields:[{name:'project_id',uitype:'code',label:'项目',placeholder:'请选择项目',cwidth:26,dataurl:'/pipeline-sdlc/api/cockpit_project_picker.dspy',valueField:'value',textField:'text'}]});var vb=new bricks.VBox({padding:'12px',gap:'12px'});vb.add_widget(new bricks.Text({text:'请选择项目:',cfontsize:0.95,color:'#64748b'}));vb.add_widget(form);pw.content_w.add_widget(vb);setTimeout(function(){var sel=pw.content_w.dom_element.querySelector('select');if(sel)sel.addEventListener('change',function(){var v=sel.value;if(!v)return;fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'project_id='+encodeURIComponent(v)}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var pl=bricks.getWidgetById('project_label',bricks.app);var il=bricks.getWidgetById('iteration_label',bricks.app);if(pl)pl.text_w.set_text(r.project_name?'\\ud83d\\udcc1 \\u5f53\\u524d\\u9879\\u76ee\\uff1a'+r.project_name:'\\ud83d\\udcc1 \\u5f53\\u524d\\u9879\\u76ee\\uff1a--');if(il)il.text_w.set_text('\\ud83d\\udd04 \\u5f53\\u524d\\u8fed\\u4ee3\\uff1a'+(r.iteration_name||'--'));var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.setValue(r);}});});},500);return;}var upw=new bricks.PopupWindow({title:d.project_name+' - 任务列表',cwidth:60,cheight:30,auto_open:true});var vs=new bricks.VScrollPanel({css:'filler',padding:'12px',gap:'8px'});upw.content_w.add_widget(vs);fetch('/pipeline-sdlc/api/cockpit_project_tasks.dspy').then(function(r){return r.json()}).then(function(data){var tasks=data.tasks||[];if(tasks.length===0){vs.add_widget(new bricks.Text({text:'暂无任务',cfontsize:0.9,color:'#94a3b8'}));return;}for(var i=0;i