myagent-ai 1.15.21 → 1.15.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web/ui/index.html +59 -34
package/package.json
CHANGED
package/web/ui/index.html
CHANGED
|
@@ -52,6 +52,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
|
|
|
52
52
|
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;margin-bottom:16px}
|
|
53
53
|
.card h3{font-size:15px;margin-bottom:12px;color:var(--text2);text-transform:uppercase;letter-spacing:.5px}
|
|
54
54
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
|
|
55
|
+
.grid-2{grid-template-columns:repeat(2,1fr)}
|
|
56
|
+
.grid-3{grid-template-columns:repeat(3,1fr)}
|
|
57
|
+
.grid-4{grid-template-columns:repeat(4,1fr)}
|
|
55
58
|
.stat{background:var(--surface2);padding:16px;border-radius:var(--radius)}
|
|
56
59
|
.stat .label{font-size:12px;color:var(--text2);margin-bottom:4px}
|
|
57
60
|
.stat .value{font-size:28px;font-weight:700}
|
|
@@ -184,7 +187,17 @@ tr:hover{background:var(--surface2)}
|
|
|
184
187
|
.content{padding:16px}
|
|
185
188
|
.grid{grid-template-columns:1fr}
|
|
186
189
|
.form-row{grid-template-columns:1fr}
|
|
187
|
-
.table-wrap{overflow-x:auto}
|
|
190
|
+
.table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
|
|
191
|
+
.card{padding:12px;margin-bottom:12px}
|
|
192
|
+
.card h3{font-size:14px;margin-bottom:8px}
|
|
193
|
+
.stat{padding:12px}
|
|
194
|
+
.stat .label{font-size:11px}
|
|
195
|
+
.stat .value{font-size:20px}
|
|
196
|
+
.grid,.grid-2,.grid-3,.grid-4{grid-template-columns:repeat(2,1fr)!important}
|
|
197
|
+
.btn-sm{padding:6px 12px;font-size:12px;min-height:32px}
|
|
198
|
+
th,td{padding:6px 8px;font-size:12px;white-space:nowrap}
|
|
199
|
+
th{font-size:11px}
|
|
200
|
+
.badge{padding:2px 6px;font-size:10px}
|
|
188
201
|
.modal{width:95%;max-height:90vh;padding:16px}
|
|
189
202
|
.modal-wide{max-width:95%}
|
|
190
203
|
.tabs{gap:0;overflow-x:auto}
|
|
@@ -192,6 +205,18 @@ tr:hover{background:var(--surface2)}
|
|
|
192
205
|
.agent-card{flex-direction:column;align-items:flex-start}
|
|
193
206
|
.agent-card .flex.flex-col{flex-direction:row;gap:4px}
|
|
194
207
|
}
|
|
208
|
+
@media(max-width:480px){
|
|
209
|
+
.content{padding:12px}
|
|
210
|
+
.card{padding:10px}
|
|
211
|
+
.grid,.grid-2,.grid-3,.grid-4{grid-template-columns:1fr!important}
|
|
212
|
+
.header h2{font-size:15px}
|
|
213
|
+
.btn-sm{padding:8px 12px;font-size:12px;min-height:36px}
|
|
214
|
+
.stat .value{font-size:18px}
|
|
215
|
+
.stat .label{font-size:10px}
|
|
216
|
+
.modal{width:98%;padding:12px}
|
|
217
|
+
.form-group{margin-bottom:10px}
|
|
218
|
+
.form-group label{font-size:12px}
|
|
219
|
+
}
|
|
195
220
|
</style>
|
|
196
221
|
</head>
|
|
197
222
|
<body>
|
|
@@ -635,9 +660,9 @@ async function loadAgentKB(){
|
|
|
635
660
|
<h4 style="font-size:14px;color:var(--text2)">知识库文件 (${files.length})</h4>
|
|
636
661
|
<button class="btn btn-sm btn-primary" onclick="uploadAgentKB('${escHtml(path)}',false)">上传文件</button> <button class="btn btn-sm btn-secondary" onclick="uploadAgentKB('${escHtml(path)}',true)">📁 上传文件夹</button></div>`;
|
|
637
662
|
if(files.length===0){html+='<div class="empty">暂无知识库文件</div>';}
|
|
638
|
-
else{html+='<table><tr><th>文件名</th><th>大小</th><th></th></tr>';
|
|
663
|
+
else{html+='<div class="table-wrap"><table><tr><th>文件名</th><th>大小</th><th></th></tr>';
|
|
639
664
|
for(const f of files){html+=`<tr><td>${escHtml(f.name||f.filename||'')}</td><td>${f.size||'-'}</td><td><button class="btn btn-sm btn-danger" onclick="deleteAgentKB('${escHtml(path)}','${escHtml(f.name||f.filename||'')}')">删除</button></td></tr>`}
|
|
640
|
-
html+='</table>';}
|
|
665
|
+
html+='</table></div>';}
|
|
641
666
|
$('kbContent').innerHTML=html;
|
|
642
667
|
}
|
|
643
668
|
|
|
@@ -672,9 +697,9 @@ async function loadAgentSessions(){
|
|
|
672
697
|
const sessions=Array.isArray(data)?data:(data?.sessions||[]);
|
|
673
698
|
let html=`<div class="flex justify-between items-center mb-16"><h4 style="font-size:14px;color:var(--text2)">会话 (${sessions.length})</h4></div>`;
|
|
674
699
|
if(sessions.length===0){html+='<div class="empty">暂无会话</div>';}
|
|
675
|
-
else{html+='<table><tr><th>会话</th><th>消息数</th><th>最后活动</th><th></th></tr>';
|
|
700
|
+
else{html+='<div class="table-wrap"><table><tr><th>会话</th><th>消息数</th><th>最后活动</th><th></th></tr>';
|
|
676
701
|
for(const s of sessions){const dn=s.display_name||s.id;html+=`<tr><td style="max-width:200px;overflow:hidden;text-overflow:ellipsis" title="${escHtml(s.id)}">${escHtml(dn.length>25?dn.slice(0,25)+'...':dn)}</td><td>${s.messages||0}</td><td>${fmtTimeAgo(s.last)}</td><td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}','${escHtml(path)}')">切入</button> <button class="btn btn-sm btn-ghost" onclick="viewSessionMsgs('${escHtml(s.id)}')">查看</button></td></tr>`}
|
|
677
|
-
html+='</table>';}
|
|
702
|
+
html+='</table></div>';}
|
|
678
703
|
$('sessionsContent').innerHTML=html;
|
|
679
704
|
}
|
|
680
705
|
|
|
@@ -745,7 +770,7 @@ async function loadAgentPerms(){
|
|
|
745
770
|
html+='<div class="card" style="margin-bottom:16px">';
|
|
746
771
|
html+='<h3 style="font-size:14px;color:var(--text2);margin-bottom:8px">🔑 功能权限</h3>';
|
|
747
772
|
html+='<p style="color:var(--text2);font-size:12px;margin-bottom:12px">精细控制 Agent 的各项能力。未设置的项目将使用全局默认值。</p>';
|
|
748
|
-
html+='<div class="grid" style="
|
|
773
|
+
html+='<div class="grid grid-3" style="gap:12px">';
|
|
749
774
|
for(const p of perms){
|
|
750
775
|
const label=labels[p]||p;
|
|
751
776
|
const defVal=defaults[p]!==false;
|
|
@@ -891,7 +916,7 @@ async function doDeletePlatform(name){
|
|
|
891
916
|
// ========== Sessions ==========
|
|
892
917
|
async function renderSessions(){
|
|
893
918
|
const ss=await api('/api/sessions');
|
|
894
|
-
let html='<table><tr><th>会话</th><th>Agent</th><th>消息数</th><th>最后活动</th><th>操作</th></tr>';
|
|
919
|
+
let html='<div class="table-wrap"><table><tr><th>会话</th><th>Agent</th><th>消息数</th><th>最后活动</th><th>操作</th></tr>';
|
|
895
920
|
for(const s of (ss||[])){
|
|
896
921
|
// 从 session_id 提取 agent 名 (格式: agent_web_timestamp)
|
|
897
922
|
const parts=(s.id||'').split('_web_');
|
|
@@ -901,7 +926,7 @@ async function renderSessions(){
|
|
|
901
926
|
<td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}','${escHtml(agentName)}')">切入</button>
|
|
902
927
|
<button class="btn btn-sm btn-ghost" onclick="viewSession('${s.id}')">查看</button>
|
|
903
928
|
<button class="btn btn-sm btn-danger" onclick="clearSession('${s.id}')">清除</button></td></tr>`;}
|
|
904
|
-
html+='</table>';if(!ss||!ss.length)html='<div class="empty">暂无会话</div>';
|
|
929
|
+
html+='</table></div>';if(!ss||!ss.length)html='<div class="empty">暂无会话</div>';
|
|
905
930
|
$('content').innerHTML=html;
|
|
906
931
|
}
|
|
907
932
|
async function viewSession(sid){
|
|
@@ -975,7 +1000,7 @@ async function renderMemory(){
|
|
|
975
1000
|
tabHtml+=`<button class="btn ${active}" onclick="_memCategory='${c.k}';renderMemory()">${c.l} (${count})</button>`;
|
|
976
1001
|
}
|
|
977
1002
|
tabHtml+='</div>';
|
|
978
|
-
let html=`<div class="grid" style="
|
|
1003
|
+
let html=`<div class="grid grid-3" style="margin-bottom:16px">
|
|
979
1004
|
<div class="stat"><div class="label">总计</div><div class="value">${stats.total_count||0}</div></div>
|
|
980
1005
|
<div class="stat"><div class="label">全局记忆</div><div class="value">${stats.global_count||0}</div></div>
|
|
981
1006
|
<div class="stat"><div class="label">会话记忆</div><div class="value">${stats.session_count||0}</div></div></div>`;
|
|
@@ -989,7 +1014,7 @@ async function renderMemory(){
|
|
|
989
1014
|
thHtml+='<th>会话</th>';
|
|
990
1015
|
if(!isSession)thHtml+='<th>重要性</th>';
|
|
991
1016
|
thHtml+='<th></th></tr>';
|
|
992
|
-
html+='<table>'+thHtml;
|
|
1017
|
+
html+='<div class="table-wrap"><table>'+thHtml;
|
|
993
1018
|
for(const e of lt){
|
|
994
1019
|
const content=(e.content||e.summary||'')||(e.role==='user'?'[用户消息]':e.role==='assistant'?'[助手回复]':'[系统]');
|
|
995
1020
|
|
|
@@ -1002,7 +1027,7 @@ async function renderMemory(){
|
|
|
1002
1027
|
if(!isSession)html+='<td>'+(e.importance!=null?e.importance.toFixed(2):'')+'</td>';
|
|
1003
1028
|
html+='<td><button class="btn btn-sm btn-danger" onclick="deleteMemory(\''+e.id+'\')">删除</button></td></tr>';
|
|
1004
1029
|
}
|
|
1005
|
-
html+='</table>';
|
|
1030
|
+
html+='</table></div>';
|
|
1006
1031
|
}else{
|
|
1007
1032
|
html+='<div class="empty">暂无'+(_memCategory==='session'?'会话':'全局')+'记忆</div>';
|
|
1008
1033
|
}
|
|
@@ -1013,12 +1038,12 @@ async function searchMemory(){
|
|
|
1013
1038
|
const r=await api('/api/memory/search?q='+encodeURIComponent(q));
|
|
1014
1039
|
let html='<h3>搜索结果: '+(r.length||0)+' 条</h3>';
|
|
1015
1040
|
if(r&&r.length){
|
|
1016
|
-
html+='<table><tr><th>Key</th><th>内容</th><th>分类</th><th>角色</th><th>会话</th></tr>';
|
|
1041
|
+
html+='<div class="table-wrap"><table><tr><th>Key</th><th>内容</th><th>分类</th><th>角色</th><th>会话</th></tr>';
|
|
1017
1042
|
for(const e of r){
|
|
1018
1043
|
const content=(e.content||'').slice(0,300);
|
|
1019
1044
|
html+=`<tr><td style="white-space:nowrap">${escHtml(e.key||'')}</td><td style="max-width:600px;word-break:break-word;font-size:13px">${escHtml(content)}</td><td>${e.category||''}</td><td>${escHtml(e.role||'')}</td><td style="font-size:12px;color:var(--text3)">${escHtml((e.session_id||'').split('_web_')[0])}</td></tr>`;
|
|
1020
1045
|
}
|
|
1021
|
-
html+='</table>';
|
|
1046
|
+
html+='</table></div>';
|
|
1022
1047
|
}else{
|
|
1023
1048
|
html+='<div class="empty">未找到匹配的记忆</div>';
|
|
1024
1049
|
}
|
|
@@ -1040,7 +1065,7 @@ async function renderPermissions(){
|
|
|
1040
1065
|
|
|
1041
1066
|
// 全局默认权限
|
|
1042
1067
|
let html='<div class="card"><h3>全局默认权限</h3><p style="color:var(--text2);font-size:13px;margin-bottom:12px">新 Agent 将继承这些默认权限设置</p>';
|
|
1043
|
-
html+='<div class="grid" style="
|
|
1068
|
+
html+='<div class="grid grid-3" style="gap:12px">';
|
|
1044
1069
|
for(const p of perms){
|
|
1045
1070
|
const label=labels[p]||p;
|
|
1046
1071
|
const val=defaults[p]!==false?'checked':'';
|
|
@@ -1051,7 +1076,7 @@ async function renderPermissions(){
|
|
|
1051
1076
|
// Agent 权限覆盖
|
|
1052
1077
|
const agentKeys=Object.keys(agents);
|
|
1053
1078
|
if(agentKeys.length>0){
|
|
1054
|
-
html+='<div class="card"><h3>Agent 权限覆盖</h3><p style="color:var(--text2);font-size:13px;margin-bottom:12px">以下 Agent 使用自定义权限(覆盖默认值)</p><table><tr><th>Agent</th>';
|
|
1079
|
+
html+='<div class="card"><h3>Agent 权限覆盖</h3><p style="color:var(--text2);font-size:13px;margin-bottom:12px">以下 Agent 使用自定义权限(覆盖默认值)</p><div class="table-wrap"><table><tr><th>Agent</th>';
|
|
1055
1080
|
for(const p of perms){html+=`<th>${labels[p]||p}</th>`;}
|
|
1056
1081
|
html+='<th>操作</th></tr>';
|
|
1057
1082
|
for(const name of agentKeys){
|
|
@@ -1063,7 +1088,7 @@ async function renderPermissions(){
|
|
|
1063
1088
|
}
|
|
1064
1089
|
html+=`<td><button class="btn btn-sm btn-ghost" onclick="editAgentPerms('${escHtml(name)}')">编辑</button> <button class="btn btn-sm btn-danger" onclick="resetAgentPerms('${escHtml(name)}')">重置</button></td></tr>`;
|
|
1065
1090
|
}
|
|
1066
|
-
html+='</table></div>';
|
|
1091
|
+
html+='</table></div></div>';
|
|
1067
1092
|
}
|
|
1068
1093
|
|
|
1069
1094
|
$('content').innerHTML=html;
|
|
@@ -1114,7 +1139,7 @@ async function editAgentPerms(name){
|
|
|
1114
1139
|
|
|
1115
1140
|
// 功能权限
|
|
1116
1141
|
html+='<div style="font-size:13px;color:var(--text2);margin-bottom:8px;font-weight:600">🔑 功能权限</div>';
|
|
1117
|
-
html+='<div class="grid" style="
|
|
1142
|
+
html+='<div class="grid grid-3" style="gap:12px">';
|
|
1118
1143
|
for(const p of perms){
|
|
1119
1144
|
const label=labels[p]||p;
|
|
1120
1145
|
const defVal=defaults[p]!==false;
|
|
@@ -1174,7 +1199,7 @@ async function renderLLM(){
|
|
|
1174
1199
|
allModelsCache=Array.isArray(models)?models:[];
|
|
1175
1200
|
let html='';
|
|
1176
1201
|
// 用量统计
|
|
1177
|
-
html+=`<div class="card"><h3>用量统计</h3><div class="grid
|
|
1202
|
+
html+=`<div class="card"><h3>用量统计</h3><div class="grid grid-4">
|
|
1178
1203
|
<div class="stat"><div class="label">调用次数</div><div class="value">${u.call_count||0}</div></div>
|
|
1179
1204
|
<div class="stat"><div class="label">Prompt Tokens</div><div class="value">${u.total_prompt_tokens||0}</div></div>
|
|
1180
1205
|
<div class="stat"><div class="label">Completion Tokens</div><div class="value">${u.total_completion_tokens||0}</div></div>
|
|
@@ -1191,7 +1216,7 @@ async function renderLLM(){
|
|
|
1191
1216
|
if(!modelList.length){
|
|
1192
1217
|
html+='<div class="empty">暂无自定义模型,点击上方按钮添加。</div>';
|
|
1193
1218
|
}else{
|
|
1194
|
-
html+='<table style="font-size:12px"><tr><th>ID</th><th>名称</th><th>Provider</th><th>模型</th><th>上下文</th><th>输入</th><th>推理</th><th>兜底</th><th>状态</th><th>操作</th></tr>';
|
|
1219
|
+
html+='<div class="table-wrap"><table style="font-size:12px"><tr><th>ID</th><th>名称</th><th>Provider</th><th>模型</th><th>上下文</th><th>输入</th><th>推理</th><th>兜底</th><th>状态</th><th>操作</th></tr>';
|
|
1195
1220
|
const providerColors={openai:'badge-green',anthropic:'badge-yellow',ollama:'badge-purple',zhipu:'badge-blue',custom:'badge-red',deepseek:'badge-blue',moonshot:'badge-purple',qwen:'badge-yellow',modelscope:'badge-purple'};
|
|
1196
1221
|
for(const m of modelList){
|
|
1197
1222
|
const badgeClass=providerColors[m.provider]||'badge-green';
|
|
@@ -1211,7 +1236,7 @@ async function renderLLM(){
|
|
|
1211
1236
|
<button class="btn btn-sm btn-success" onclick="testModel(encodeURIComponent('${escHtml(m.id)}'))">测试</button>
|
|
1212
1237
|
<button class="btn btn-sm btn-danger" onclick="deleteModel('${escHtml(m.id)}','${escHtml(String(m.name||'').replace(/'/g,"\\'"))}')">删除</button></td></tr>`;
|
|
1213
1238
|
}
|
|
1214
|
-
html+='</table>';
|
|
1239
|
+
html+='</table></div>';
|
|
1215
1240
|
}
|
|
1216
1241
|
html+='</div>';
|
|
1217
1242
|
$('content').innerHTML=html;
|
|
@@ -1343,12 +1368,12 @@ async function renderExecutor(){
|
|
|
1343
1368
|
<input type="radio" name="execMode" value="sandbox" ${isSandbox?'checked':''} onchange="switchMode('sandbox')" ${!dockerOk?'disabled':''}>
|
|
1344
1369
|
<div><strong>📦 沙盒执行 (Docker)</strong><br><span style="font-size:12px;color:var(--text2)">在隔离容器中运行,更安全${!dockerOk?' (Docker 不可用)':''}</span></div></label></div>
|
|
1345
1370
|
<div style="font-size:13px;color:var(--text2)">当前模式: <span class="badge ${isSandbox?'badge-yellow':'badge-green'}">${isSandbox?'沙盒 (Docker)':'本机'}</span> Docker 状态: <span class="badge ${dockerOk?'badge-green':'badge-red'}">${dockerOk?'可用':'不可用'}</span> 累计执行: <span class="tag">${e.execution_count||0} 次</span></div></div>`;
|
|
1346
|
-
html+=`<div class="card"><h3>沙盒设置</h3><div class="
|
|
1371
|
+
html+=`<div class="card"><h3>沙盒设置</h3><div class="form-row">
|
|
1347
1372
|
<div class="form-group"><label>Docker 镜像</label><input id="sbImage" value="${e.sandbox_image||'python:3.12-slim'}"></div>
|
|
1348
1373
|
<div class="form-group"><label>内存限制</label><input id="sbMemory" value="${e.sandbox_memory||'512m'}" placeholder="512m"></div>
|
|
1349
1374
|
<div class="form-group"><label>网络访问</label><select id="sbNetwork"><option ${!e.sandbox_network?'selected':''} value="false">禁止 (更安全)</option><option ${e.sandbox_network?'selected':''} value="true">允许</option></select></div></div>
|
|
1350
1375
|
<div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveExecutor()">保存设置</button></div></div>`;
|
|
1351
|
-
html+=`<div class="card"><h3>执行参数</h3><div class="
|
|
1376
|
+
html+=`<div class="card"><h3>执行参数</h3><div class="form-row">
|
|
1352
1377
|
<div class="form-group"><label>超时时间 (秒)</label><input id="exTimeout" type="number" value="${e.timeout||300}"></div>
|
|
1353
1378
|
<div class="form-group"><label>自动重试</label><input id="exRetries" type="number" value="2"></div>
|
|
1354
1379
|
<div class="form-group"><label>自动修复</label><select id="exAutoFix"><option ${e.auto_fix?'selected':''} value="true">开启</option><option ${!e.auto_fix?'selected':''} value="false">关闭</option></select></div></div></div>`;
|
|
@@ -1420,9 +1445,9 @@ async function viewSkillDetail(name){
|
|
|
1420
1445
|
</div>
|
|
1421
1446
|
<div class="form-group"><label>危险操作</label><div>${s.dangerous?'<span class="badge badge-red">是</span>':'<span class="badge badge-green">否</span>'}</div></div>
|
|
1422
1447
|
<div class="form-group"><label>参数 (${(s.parameters||[]).length})</label>
|
|
1423
|
-
<table><tr><th>名称</th><th>类型</th><th>必需</th><th>描述</th></tr>
|
|
1448
|
+
<div class="table-wrap"><table><tr><th>名称</th><th>类型</th><th>必需</th><th>描述</th></tr>
|
|
1424
1449
|
${(s.parameters||[]).map(p=>`<tr><td><code>${escHtml(p.name)}</code></td><td>${p.type||'string'}</td><td>${p.required?'✅':'❌'}</td><td>${escHtml(p.description||'')}</td></tr>`).join('')}
|
|
1425
|
-
</table></div>`;
|
|
1450
|
+
</table></div></div>`;
|
|
1426
1451
|
$('modalContainer').innerHTML=`<div class="modal-overlay" onclick="closeModal()"><div class="modal" onclick="event.stopPropagation()">${html}<div class="flex gap-8 mt-16"><button class="btn btn-ghost" onclick="closeModal()">关闭</button></div></div></div>`;
|
|
1427
1452
|
}
|
|
1428
1453
|
|
|
@@ -1433,12 +1458,12 @@ async function renderFiles(){
|
|
|
1433
1458
|
<span style="font-size:14px;color:var(--text2)">工作目录: ${wd.path}</span>
|
|
1434
1459
|
<button class="btn btn-sm btn-ghost" onclick="changeWorkdir()">更改</button>
|
|
1435
1460
|
<button class="btn btn-sm btn-ghost" onclick="renderFiles()">刷新</button></div>`;
|
|
1436
|
-
html+='<table><tr><th>名称</th><th>类型</th><th>大小</th></tr>';
|
|
1461
|
+
html+='<div class="table-wrap"><table><tr><th>名称</th><th>类型</th><th>大小</th></tr>';
|
|
1437
1462
|
for(const f of (files||[])){
|
|
1438
1463
|
const icon=f.type==='dir'?'📁':'📄';const size=f.type==='file'?(f.size>1024?(f.size/1024).toFixed(1)+'KB':f.size+'B'):'-';
|
|
1439
1464
|
html+=`<tr><td>${icon} ${escHtml(f.name)}</td><td>${f.type}</td><td>${size}</td></tr>`;}
|
|
1440
1465
|
if(!files||!files.length)html+='<tr><td colspan="3" class="empty">目录为空</td></tr>';
|
|
1441
|
-
html+='</table>';$('content').innerHTML=html;
|
|
1466
|
+
html+='</table></div>';$('content').innerHTML=html;
|
|
1442
1467
|
}
|
|
1443
1468
|
async function changeWorkdir(){const p=prompt('新路径:');if(!p)return;await api('/api/workdir',{method:'PUT',body:JSON.stringify({path:p})});renderFiles();}
|
|
1444
1469
|
|
|
@@ -1471,7 +1496,7 @@ async function renderTasks(){
|
|
|
1471
1496
|
const tasks=r.tasks||[];
|
|
1472
1497
|
const statusCounts={pending:0,running:0,completed:0,failed:0};
|
|
1473
1498
|
for(const t of tasks){statusCounts[t.status]=(statusCounts[t.status]||0)+1;}
|
|
1474
|
-
let html=`<div class="grid" style="
|
|
1499
|
+
let html=`<div class="grid grid-4" style="margin-bottom:16px">
|
|
1475
1500
|
<div class="stat"><div class="label">待处理</div><div class="value">${statusCounts.pending}</div></div>
|
|
1476
1501
|
<div class="stat"><div class="label">运行中</div><div class="value">${statusCounts.running}</div></div>
|
|
1477
1502
|
<div class="stat"><div class="label">已完成</div><div class="value" style="color:var(--success)">${statusCounts.completed}</div></div>
|
|
@@ -1489,7 +1514,7 @@ async function renderTasks(){
|
|
|
1489
1514
|
if(tasks.length===0){html+='<div class="empty">暂无任务记录</div>';}
|
|
1490
1515
|
else{
|
|
1491
1516
|
html+='<div style="max-height:calc(100vh - 340px);overflow-y:auto" id="taskList">';
|
|
1492
|
-
html+='<table><tr><th>任务ID</th><th>描述</th><th>群聊</th><th>状态</th><th>时间</th><th>操作</th></tr>';
|
|
1517
|
+
html+='<div class="table-wrap"><table><tr><th>任务ID</th><th>描述</th><th>群聊</th><th>状态</th><th>时间</th><th>操作</th></tr>';
|
|
1493
1518
|
for(const t of tasks){
|
|
1494
1519
|
const statusBadge=t.status==='completed'?'<span class="badge badge-green">已完成</span>':
|
|
1495
1520
|
t.status==='running'?'<span class="badge badge-blue">运行中</span>':
|
|
@@ -1511,7 +1536,7 @@ async function renderTasks(){
|
|
|
1511
1536
|
<button class="btn btn-sm btn-danger" onclick="deleteTask('${escHtml(t.task_id)}')">删除</button>
|
|
1512
1537
|
</td></tr>`;
|
|
1513
1538
|
}
|
|
1514
|
-
html+='</table></div>';
|
|
1539
|
+
html+='</table></div></div>';
|
|
1515
1540
|
}
|
|
1516
1541
|
$('content').innerHTML=html;
|
|
1517
1542
|
}
|
|
@@ -1554,7 +1579,7 @@ async function renderOrganization(){
|
|
|
1554
1579
|
</div>
|
|
1555
1580
|
<div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveOrgConfig()">保存配置</button></div></div>`;
|
|
1556
1581
|
html+=`<div class="card"><h3>组织信息</h3>
|
|
1557
|
-
<div class="
|
|
1582
|
+
<div class="form-row">
|
|
1558
1583
|
<div class="form-group"><label>组织名称</label><input id="orgName" value="${escHtml(inf.name||'')}" placeholder="我的组织"></div>
|
|
1559
1584
|
<div class="form-group"><label>组织描述</label><input id="orgDesc" value="${escHtml(inf.description||'')}" placeholder="组织简介"></div>
|
|
1560
1585
|
<div class="form-group"><label>联系方式</label><input id="orgContact" value="${escHtml(inf.contact||'')}" placeholder="联系邮箱或电话"></div>
|
|
@@ -1581,13 +1606,13 @@ async function loadOrgKnowledge(){
|
|
|
1581
1606
|
const files=await api('/api/organization/knowledge');
|
|
1582
1607
|
const el=document.getElementById('orgKBList');if(!el)return;
|
|
1583
1608
|
if(!files||!files.length){el.innerHTML='<div class="empty">暂无知识库文件</div>';return}
|
|
1584
|
-
let html='<table><tr><th>文件名</th><th>大小</th><th>操作</th></tr>';
|
|
1609
|
+
let html='<div class="table-wrap"><table><tr><th>文件名</th><th>大小</th><th>操作</th></tr>';
|
|
1585
1610
|
for(const f of files){
|
|
1586
1611
|
html+=`<tr><td>${escHtml(f.name||f.path)}</td><td>${f.size||'-'}</td>
|
|
1587
1612
|
<td><button class="btn btn-sm btn-ghost" onclick="viewOrgKBFile('${escHtml(f.path||f.name)}')">查看</button>
|
|
1588
1613
|
<button class="btn btn-sm btn-danger" onclick="deleteOrgKBFile('${escHtml(f.path||f.name)}')">删除</button></td></tr>`;
|
|
1589
1614
|
}
|
|
1590
|
-
html+='</table>';el.innerHTML=html;
|
|
1615
|
+
html+='</table></div>';el.innerHTML=html;
|
|
1591
1616
|
}
|
|
1592
1617
|
function uploadOrgKnowledge(folderMode){
|
|
1593
1618
|
const input=document.createElement('input');input.type='file';input.multiple=true;
|
|
@@ -1773,13 +1798,13 @@ async function loadDeptKB(path){
|
|
|
1773
1798
|
let html='<h4 style="margin-bottom:8px">部门知识库</h4>';
|
|
1774
1799
|
html+='<div style="margin-bottom:8px"><button class="btn btn-sm btn-primary" onclick="uploadDeptKB(\''+escHtml(path)+'\',false)">上传文件</button> <button class="btn btn-sm btn-secondary" onclick="uploadDeptKB(\''+escHtml(path)+'\',true)">📁 上传文件夹</button></div>';
|
|
1775
1800
|
if(!files||!files.length){html+='<div class="empty" style="margin-top:12px">暂无知识库文件</div>';el.innerHTML=html;return}
|
|
1776
|
-
html+='<table><tr><th>文件</th><th>操作</th></tr>';
|
|
1801
|
+
html+='<div class="table-wrap"><table><tr><th>文件</th><th>操作</th></tr>';
|
|
1777
1802
|
for(const f of files){
|
|
1778
1803
|
html+=`<tr><td>${escHtml(f.name||f.path)}</td>
|
|
1779
1804
|
<td><button class="btn btn-sm btn-ghost" onclick="viewDeptKBFile('${escHtml(path)}','${escHtml(f.path||f.name)}')">查看</button>
|
|
1780
1805
|
<button class="btn btn-sm btn-danger" onclick="deleteDeptKBFile('${escHtml(path)}','${escHtml(f.path||f.name)}')">删除</button></td></tr>`;
|
|
1781
1806
|
}
|
|
1782
|
-
html+='</table>';el.innerHTML=html;
|
|
1807
|
+
html+='</table></div>';el.innerHTML=html;
|
|
1783
1808
|
}
|
|
1784
1809
|
async function uploadDeptKB(path,folderMode){
|
|
1785
1810
|
const input=document.createElement('input');input.type='file';input.multiple=true;
|