fix: 当前项目/迭代按钮改为选择器弹窗,无选中时显示列表供选择
- 当前项目按钮:无项目时弹出选择器,有项目时显示详情 - 当前迭代按钮:无迭代时弹出选择器(按项目过滤),有迭代时显示详情 - 新增 cockpit_project_picker.dspy:返回项目列表 - 新增 cockpit_iteration_picker.dspy:返回迭代列表(支持project_id过滤) - 新增 cockpit_context_update.dspy:保存选择到 pipeline_agent_settings
This commit is contained in:
parent
3f91752d06
commit
82f9fb6764
72
wwwroot/api/cockpit_context_update.dspy
Normal file
72
wwwroot/api/cockpit_context_update.dspy
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# cockpit_context_update.dspy - Save user's selected project/iteration to pipeline_agent_settings
|
||||||
|
# POST params: project_id, iteration_id (at least one required)
|
||||||
|
# Also looks up names for the newly set values
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
uid = await get_user()
|
||||||
|
pid = (params_kw or {}).get('project_id', '')
|
||||||
|
iid = (params_kw or {}).get('iteration_id', '')
|
||||||
|
|
||||||
|
if not pid and not iid:
|
||||||
|
return json.dumps({"success": False, "error": "请提供 project_id 或 iteration_id"}, ensure_ascii=False)
|
||||||
|
|
||||||
|
dbname = get_module_dbname('pipeline-sdlc')
|
||||||
|
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
# Upsert agent_settings
|
||||||
|
existing = await sor.sqlExe(
|
||||||
|
"SELECT id, current_project_id, current_iteration_id FROM pipeline_agent_settings WHERE user_id=${uid}$",
|
||||||
|
{"uid": uid}
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
cur_pid = getattr(existing[0], 'current_project_id', '') or ''
|
||||||
|
cur_iid = getattr(existing[0], 'current_iteration_id', '') or ''
|
||||||
|
new_pid = pid if pid else cur_pid
|
||||||
|
new_iid = iid if iid else ('' if pid else cur_iid)
|
||||||
|
# If project changed but iteration not specified, clear iteration
|
||||||
|
if pid and pid != cur_pid and not iid:
|
||||||
|
new_iid = ''
|
||||||
|
await sor.sqlExe(
|
||||||
|
"UPDATE pipeline_agent_settings SET current_project_id=${pid}$, current_iteration_id=${iid}$ WHERE user_id=${uid}$",
|
||||||
|
{"pid": new_pid, "iid": new_iid, "uid": uid}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_pid = pid
|
||||||
|
new_iid = iid
|
||||||
|
await sor.C('pipeline_agent_settings', {
|
||||||
|
'id': getID(),
|
||||||
|
'user_id': uid,
|
||||||
|
'current_project_id': new_pid,
|
||||||
|
'current_iteration_id': new_iid,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Look up names
|
||||||
|
pname = ''
|
||||||
|
iname = ''
|
||||||
|
ws_dir = ''
|
||||||
|
if new_pid:
|
||||||
|
precs = await sor.sqlExe(
|
||||||
|
"SELECT name, workspace_dir FROM sd_projects WHERE id=${pid}$",
|
||||||
|
{"pid": new_pid}
|
||||||
|
)
|
||||||
|
if precs and len(precs) > 0:
|
||||||
|
pname = getattr(precs[0], 'name', '') or ''
|
||||||
|
ws_dir = getattr(precs[0], 'workspace_dir', '') or ''
|
||||||
|
if new_iid:
|
||||||
|
irecs = await sor.sqlExe(
|
||||||
|
"SELECT iteration_name FROM sd_iterations WHERE id=${iid}$",
|
||||||
|
{"iid": new_iid}
|
||||||
|
)
|
||||||
|
if irecs and len(irecs) > 0:
|
||||||
|
iname = getattr(irecs[0], 'iteration_name', '') or ''
|
||||||
|
|
||||||
|
return json.dumps({
|
||||||
|
"success": True,
|
||||||
|
"project_id": new_pid,
|
||||||
|
"iteration_id": new_iid,
|
||||||
|
"project_name": pname,
|
||||||
|
"iteration_name": iname,
|
||||||
|
"workspace_dir": ws_dir,
|
||||||
|
}, ensure_ascii=False)
|
||||||
29
wwwroot/api/cockpit_iteration_picker.dspy
Normal file
29
wwwroot/api/cockpit_iteration_picker.dspy
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# cockpit_iteration_picker.dspy - Returns iterations as {value, text} for dropdown
|
||||||
|
# Optional query param: project_id - filter iterations by project
|
||||||
|
# Used by cockpit context bar iteration picker
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
pid = (params_kw or {}).get('project_id', '')
|
||||||
|
dbname = get_module_dbname('pipeline-sdlc')
|
||||||
|
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
if pid:
|
||||||
|
recs = await sor.sqlExe(
|
||||||
|
"SELECT id, iteration_name FROM sd_iterations WHERE project_id=${pid}$ ORDER BY iteration_name",
|
||||||
|
{"pid": pid}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
recs = await sor.sqlExe(
|
||||||
|
"SELECT id, iteration_name FROM sd_iterations ORDER BY iteration_name",
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for r in recs:
|
||||||
|
rows.append({
|
||||||
|
'value': r.id,
|
||||||
|
'text': r.iteration_name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return rows
|
||||||
19
wwwroot/api/cockpit_project_picker.dspy
Normal file
19
wwwroot/api/cockpit_project_picker.dspy
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# cockpit_project_picker.dspy - Returns all projects as {value, text} for dropdown
|
||||||
|
# Used by cockpit context bar project picker
|
||||||
|
|
||||||
|
dbname = get_module_dbname('pipeline-sdlc')
|
||||||
|
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
recs = await sor.sqlExe(
|
||||||
|
"SELECT id, name FROM sd_projects ORDER BY name",
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for r in recs:
|
||||||
|
rows.append({
|
||||||
|
'value': r.id,
|
||||||
|
'text': r.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return rows
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"event": "click",
|
"event": "click",
|
||||||
"actiontype": "script",
|
"actiontype": "script",
|
||||||
"target": "self",
|
"target": "self",
|
||||||
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var pid=d.project_id||'';if(!pid){bricks.show_error({title:'提示', message:'请先选择项目'});return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'项目名称:'+(d.project_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'项目ID:'+pid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'工作目录:'+(d.workspace_dir||'/d/pipeline/workspaces/'+pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'项目详情',cwidth:36,cheight:16,auto_open:true});pw.content_w.add_widget(box);"
|
"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);var btn=new bricks.Button({label:'确定',css:'primary'});btn.bind('click',function(){var v=form.getValue();var spid=(v||{}).project_id||'';if(!spid){bricks.show_error({title:'提示',message:'请选择一个项目'});return;}fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'project_id='+encodeURIComponent(spid)}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.render({});}else{bricks.show_error({title:'失败',message:r.error||'保存失败'});}});});vb.add_widget(btn);pw.content_w.add_widget(vb);return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'项目名称:'+(d.project_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'项目ID:'+pid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'工作目录:'+(d.workspace_dir||'/d/pipeline/workspaces/'+pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'项目详情',cwidth:36,cheight:16,auto_open:true});pw.content_w.add_widget(box);"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -79,7 +79,7 @@
|
|||||||
"event": "click",
|
"event": "click",
|
||||||
"actiontype": "script",
|
"actiontype": "script",
|
||||||
"target": "self",
|
"target": "self",
|
||||||
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var iid=d.iteration_id||'';var pid=d.project_id||'';if(!iid){bricks.show_error({title:'提示', message:'请先选择迭代'});return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'迭代名称:'+(d.iteration_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'迭代ID:'+iid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'所属项目:'+(d.project_name||pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'迭代详情',cwidth:36,cheight:14,auto_open:true});pw.content_w.add_widget(box);"
|
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var iid=d.iteration_id||'';var pid=d.project_id||'';if(!iid){var iter_url='/pipeline-sdlc/api/cockpit_iteration_picker.dspy';if(pid)iter_url+='?project_id='+encodeURIComponent(pid);var pw=new bricks.PopupWindow({title:'选择迭代',cwidth:32,cheight:16,auto_open:true});var form=new bricks.Form({name:'iteration_picker',cols:1,fields:[{name:'iteration_id',uitype:'code',label:'迭代',placeholder:'请选择迭代',cwidth:26,dataurl:iter_url,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);var btn=new bricks.Button({label:'确定',css:'primary'});btn.bind('click',function(){var v=form.getValue();var siid=(v||{}).iteration_id||'';if(!siid){bricks.show_error({title:'提示',message:'请选择一个迭代'});return;}var body='iteration_id='+encodeURIComponent(siid);if(pid)body+='&project_id='+encodeURIComponent(pid);fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.render({});}else{bricks.show_error({title:'失败',message:r.error||'保存失败'});}});});vb.add_widget(btn);pw.content_w.add_widget(vb);return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'迭代名称:'+(d.iteration_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'迭代ID:'+iid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'所属项目:'+(d.project_name||pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'迭代详情',cwidth:36,cheight:14,auto_open:true});pw.content_w.add_widget(box);"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -80,7 +80,7 @@
|
|||||||
"event": "click",
|
"event": "click",
|
||||||
"actiontype": "script",
|
"actiontype": "script",
|
||||||
"target": "self",
|
"target": "self",
|
||||||
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var pid=d.project_id||'';if(!pid){bricks.show_error({title:'提示', message:'请先选择项目'});return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'项目名称:'+(d.project_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'项目ID:'+pid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'工作目录:'+(d.workspace_dir||'/d/pipeline/workspaces/'+pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'项目详情',cwidth:36,cheight:16,auto_open:true});pw.content_w.add_widget(box);"
|
"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);var btn=new bricks.Button({label:'确定',css:'primary'});btn.bind('click',function(){var v=form.getValue();var spid=(v||{}).project_id||'';if(!spid){bricks.show_error({title:'提示',message:'请选择一个项目'});return;}fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'project_id='+encodeURIComponent(spid)}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.render({});}else{bricks.show_error({title:'失败',message:r.error||'保存失败'});}});});vb.add_widget(btn);pw.content_w.add_widget(vb);return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'项目名称:'+(d.project_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'项目ID:'+pid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'工作目录:'+(d.workspace_dir||'/d/pipeline/workspaces/'+pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'项目详情',cwidth:36,cheight:16,auto_open:true});pw.content_w.add_widget(box);"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -98,7 +98,7 @@
|
|||||||
"event": "click",
|
"event": "click",
|
||||||
"actiontype": "script",
|
"actiontype": "script",
|
||||||
"target": "self",
|
"target": "self",
|
||||||
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var iid=d.iteration_id||'';var pid=d.project_id||'';if(!iid){bricks.show_error({title:'提示', message:'请先选择迭代'});return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'迭代名称:'+(d.iteration_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'迭代ID:'+iid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'所属项目:'+(d.project_name||pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'迭代详情',cwidth:36,cheight:14,auto_open:true});pw.content_w.add_widget(box);"
|
"script": "var ctx=bricks.getWidgetById('context_data',bricks.app);var d=ctx?ctx.getValue():{};var iid=d.iteration_id||'';var pid=d.project_id||'';if(!iid){var iter_url='/pipeline-sdlc/api/cockpit_iteration_picker.dspy';if(pid)iter_url+='?project_id='+encodeURIComponent(pid);var pw=new bricks.PopupWindow({title:'选择迭代',cwidth:32,cheight:16,auto_open:true});var form=new bricks.Form({name:'iteration_picker',cols:1,fields:[{name:'iteration_id',uitype:'code',label:'迭代',placeholder:'请选择迭代',cwidth:26,dataurl:iter_url,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);var btn=new bricks.Button({label:'确定',css:'primary'});btn.bind('click',function(){var v=form.getValue();var siid=(v||{}).iteration_id||'';if(!siid){bricks.show_error({title:'提示',message:'请选择一个迭代'});return;}var body='iteration_id='+encodeURIComponent(siid);if(pid)body+='&project_id='+encodeURIComponent(pid);fetch('/pipeline-sdlc/api/cockpit_context_update.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){pw.destroy();var cd=bricks.getWidgetById('context_data',bricks.app);if(cd)cd.render({});}else{bricks.show_error({title:'失败',message:r.error||'保存失败'});}});});vb.add_widget(btn);pw.content_w.add_widget(vb);return;}var box=new bricks.VBox();box.add_widget(new bricks.Text({text:'迭代名称:'+(d.iteration_name||'--'),cfontsize:1.1,fontWeight:'bold',marginBottom:'8px'}));box.add_widget(new bricks.Text({text:'迭代ID:'+iid,cfontsize:0.85,color:'#64748b',marginBottom:'4px'}));box.add_widget(new bricks.Text({text:'所属项目:'+(d.project_name||pid),cfontsize:0.85,color:'#64748b'}));var pw=new bricks.PopupWindow({title:'迭代详情',cwidth:36,cheight:14,auto_open:true});pw.content_w.add_widget(box);"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user