pipeline-sdlc/wwwroot/api/workspace_files.dspy

115 lines
4.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# workspace_files.dspy - 目录文件列表(拖拽上传区 + 可选中文件行)
import os
import json as _json
folder_id = (params_kw or {}).get('id', '').strip()
uid = await get_user()
if not uid:
uid = 'user-01'
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
if not folder_id or folder_id == '__root__':
full_dir = ws_dir
else:
full_dir = ws_dir + '/' + folder_id if ws_dir else folder_id
if not os.path.isdir(full_dir):
return {"widgettype": "Text", "options": {"text": "目录不可用: " + folder_id, "cfontsize": 0.9}}
upload_url = entire_url("/pipeline-sdlc/api/workspace_upload.dspy")
# 拖拽上传脚本:读取文件 → base64 → form-encoded POST → 刷新浏览器
upload_script = (
"var fs=event.params.files;"
"if(!fs||!fs.length)return;"
"for(var i=0;i<fs.length;i++){"
"var f=fs[i].file;"
"var b64=await new Promise(function(res){var r=new FileReader();r.onload=function(){res(r.result);};r.readAsDataURL(f);});"
"var body=new URLSearchParams();"
"body.append('id'," + _json.dumps(folder_id) + ");"
"body.append('filename',f.name);"
"body.append('filedata',b64);"
"var resp=await fetch(" + _json.dumps(upload_url) + ",{method:'POST',body:body});"
"var rj=await resp.json();"
"if(rj.status!='ok'){var m=new bricks.Message({title:'上传失败',message:rj.message||'未知错误'});m.open();}"
"}"
"var rb=bricks.getWidgetById('ws_rb',bricks.app);"
"if(rb){var cid=rb.current_id;rb.current_id=undefined;await rb.render_browser(cid);}"
)
# 拖拽上传区(固定高度)
droppable = {
"widgettype": "Droppable",
"options": {"accepts": ["*"], "padding": "10px"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "📥 拖拽文件到此处上传", "cfontsize": 0.85, "color": "#64748b", "halign": "middle"}}
],
"binds": [{"wid": "self", "event": "filedrop", "actiontype": "script", "target": "self", "script": upload_script}]
}
files = []
try:
entries = sorted(os.listdir(full_dir))
except Exception as e:
return {"widgettype": "Text", "options": {"text": "读取错误: " + str(e), "cfontsize": 0.9}}
for name in entries:
full = os.path.join(full_dir, name)
if name.startswith('.'):
continue
if os.path.isfile(full):
size = os.path.getsize(full)
ext = os.path.splitext(name)[1].lower()
is_text = ext in ['.md', '.py', '.js', '.html', '.css', '.json', '.txt', '.xml', '.yaml', '.yml',
'.toml', '.cfg', '.sh', '.sql', '.dspy', '.ui', '.csv', '.ts', '.tsx', '.jsx',
'.java', '.c', '.cpp', '.h', '.go', '.rs', '.rb', '.php', '.vue', '.scss', '.less']
icon = "📄" if is_text else "🎬" if ext in ['.png', '.jpg', '.jpeg', '.gif', '.mp4', '.mp3', '.wav'] else "📦"
if folder_id and folder_id != '__root__':
rel_path = folder_id + '/' + name
else:
rel_path = name
select_script = (
"if(bricks.app._ws_sel_el)bricks.app._ws_sel_el.classList.remove('selected');"
"this.dom_element.classList.add('selected');"
"bricks.app._ws_sel_el=this.dom_element;"
"bricks.app._ws_sel={id:" + _json.dumps(rel_path) + ",name:" + _json.dumps(name) + "};"
)
row = {
"widgettype": "HBox",
"options": {"padding": "6px 10px", "alignItems": "center", "gap": "8px", "cursor": "pointer"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": icon, "cfontsize": 1.2}},
{"widgettype": "Text", "options": {"text": name, "cfontsize": 0.9, "color": "#1e293b", "halign": "left", "css": "filler"}},
{"widgettype": "Text", "options": {"text": f"{size/1024:.1f}KB", "cfontsize": 0.75, "color": "#94a3b8"}},
],
"binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": select_script}]
}
files.append(row)
if not files:
file_list_content = [{"widgettype": "Text", "options": {"text": "(空目录)", "cfontsize": 0.85, "color": "#94a3b8", "padding": "8px"}}]
else:
file_list_content = files
# 文件列表区filler + VScrollPanel占满剩余高度并可滚动
file_list_panel = {
"widgettype": "VScrollPanel",
"options": {"css": "filler", "width": "100%"},
"subwidgets": file_list_content
}
return {
"widgettype": "VBox",
"options": {"width": "100%", "height": "100%", "padding": "8px", "gap": "6px"},
"subwidgets": [droppable, file_list_panel]
}