性能优化: 项目页/看板/DAG/知识库/交付 加载提速
- /api/tasks 列表接口改用轻量序列化,去掉 output_text/error 大字段 (300KB -> 6KB, 详情接口 /api/tasks/<id> 仍返回完整数据) - /api/tasks/trash 与 /api/projects/<id>/dag 同步轻量化 - 前端同一项目内切换 tab 不再重复拉取项目/任务/Worker/事件, 复用 projCtx 缓存; 离开项目页时自动清缓存 - /api/projects/<id>/events 各来源加 LIMIT, 避免全表加载
This commit is contained in:
@@ -708,7 +708,7 @@ def tasks():
|
||||
visible = _visible_project_ids()
|
||||
if visible is not None:
|
||||
rows = [r for r in rows if r['project_id'] in visible]
|
||||
return jsonify({'ok': True, 'data': [db.serialize_task(t) for t in rows]})
|
||||
return jsonify({'ok': True, 'data': [db.serialize_task_light(t) for t in rows]})
|
||||
|
||||
|
||||
@app.route('/api/tasks/<int:tid>', methods=['GET', 'PUT', 'DELETE'])
|
||||
@@ -779,7 +779,7 @@ def tasks_trash():
|
||||
visible = _visible_project_ids()
|
||||
if visible is not None:
|
||||
rows = [r for r in rows if r['project_id'] in visible]
|
||||
return jsonify({'ok': True, 'data': [db.serialize_task(t) for t in rows]})
|
||||
return jsonify({'ok': True, 'data': [db.serialize_task_light(t) for t in rows]})
|
||||
|
||||
|
||||
@app.route('/api/tasks/<int:tid>/trash', methods=['POST'])
|
||||
@@ -1104,18 +1104,21 @@ def project_events(pid):
|
||||
limit = min(int(request.args.get('limit', 200)), 500)
|
||||
rows = []
|
||||
# AI 主管动态
|
||||
for r in db.q('SELECT * FROM project_logs WHERE project_id=?', (pid,)):
|
||||
for r in db.q('SELECT * FROM project_logs WHERE project_id=? ORDER BY id DESC LIMIT ?',
|
||||
(pid, limit)):
|
||||
rows.append({'id': f'p{r["id"]}', 'kind': 'autolog', 'level': r['level'],
|
||||
'message': r['message'], 'created_at': r['created_at'], 'task_id': None,
|
||||
'task_title': None})
|
||||
# 任务日志(带任务标题,可定位)
|
||||
for r in db.q('SELECT l.*, t.title AS task_title FROM task_logs l '
|
||||
'JOIN tasks t ON t.id=l.task_id WHERE t.project_id=? AND t.deleted=0', (pid,)):
|
||||
'JOIN tasks t ON t.id=l.task_id WHERE t.project_id=? AND t.deleted=0 '
|
||||
'ORDER BY l.id DESC LIMIT ?', (pid, limit)):
|
||||
rows.append({'id': f'l{r["id"]}', 'kind': 'task', 'level': r['level'],
|
||||
'message': r['message'], 'created_at': r['created_at'],
|
||||
'task_id': r['task_id'], 'task_title': r['task_title']})
|
||||
# 交付记录
|
||||
for r in db.q('SELECT * FROM project_deliverables WHERE project_id=?', (pid,)):
|
||||
for r in db.q('SELECT * FROM project_deliverables WHERE project_id=? ORDER BY id DESC LIMIT ?',
|
||||
(pid, limit)):
|
||||
rows.append({'id': f'd{r["id"]}', 'kind': 'deliver', 'level': 'success',
|
||||
'message': f'📦 {r["note"]}({r["name"]})', 'created_at': r['created_at'],
|
||||
'task_id': None, 'task_title': None})
|
||||
@@ -1275,7 +1278,7 @@ def project_dag(pid):
|
||||
rows = db.q('SELECT * FROM tasks WHERE project_id=? AND deleted=0 ORDER BY id', (pid,))
|
||||
nodes, edges, id_map = [], [], {}
|
||||
for t in rows:
|
||||
node = db.serialize_task(t)
|
||||
node = db.serialize_task_light(t)
|
||||
nodes.append(node)
|
||||
id_map[t['id']] = node
|
||||
for t in nodes:
|
||||
|
||||
@@ -631,6 +631,16 @@ def serialize_task(t):
|
||||
return t
|
||||
|
||||
|
||||
def serialize_task_light(t):
|
||||
"""轻量序列化(列表接口用):去掉大字段 output_text / error,
|
||||
看板、DAG、回收站等列表视图不需要它们,可显著减小传输体积。
|
||||
详情接口 /api/tasks/<id> 仍返回完整数据。"""
|
||||
t = serialize_task(t)
|
||||
t.pop('output_text', None)
|
||||
t.pop('error', None)
|
||||
return t
|
||||
|
||||
|
||||
def get_setting(key, default=''):
|
||||
r = q('SELECT value FROM settings WHERE key=?', (key,), one=True)
|
||||
return r['value'] if r else default
|
||||
|
||||
+12
-1
@@ -100,6 +100,8 @@ function router() {
|
||||
const hash = location.hash.replace(/^#\//, '') || 'dashboard';
|
||||
const parts = hash.split('/');
|
||||
const name = parts[0];
|
||||
// 离开项目页时丢弃项目缓存,避免下次进入用旧数据
|
||||
if (name !== 'project') projCtx = null;
|
||||
const fn = routes[name] || pageDashboard;
|
||||
const navMap = {project:'projects', api:'api', settings:'settings', alerts:'alerts'};
|
||||
$$('#sidebar nav a').forEach(a => a.classList.toggle('active', a.dataset.route === (navMap[name] || name)));
|
||||
@@ -274,11 +276,20 @@ let projCtx = null; // {pid, p, tasks, workers, tab, autoLogs}
|
||||
let projAutoTimer = null;
|
||||
|
||||
async function pageProject([pid, tab]) {
|
||||
tab = tab || 'kanban';
|
||||
// 同一项目内切 tab:复用已加载的数据,不再重新拉项目/任务/Worker/事件
|
||||
if (projCtx && String(projCtx.pid) === String(pid)) {
|
||||
projCtx.tab = tab;
|
||||
renderProjectShell();
|
||||
renderActiveTab();
|
||||
startProjAutoPoll(); // 重启轮询(router 已清掉定时器)
|
||||
return;
|
||||
}
|
||||
const p = (await api(`/api/projects/${pid}`)).data;
|
||||
const tasks = (await api(`/api/tasks?project_id=${pid}`)).data;
|
||||
const workers = (await api('/api/workers')).data;
|
||||
const events = (await api(`/api/projects/${pid}/events`)).data;
|
||||
projCtx = {pid, p, tasks, workers, tab: tab || 'kanban', events};
|
||||
projCtx = {pid, p, tasks, workers, tab, events};
|
||||
renderProjectShell();
|
||||
renderActiveTab();
|
||||
startProjAutoPoll();
|
||||
|
||||
Reference in New Issue
Block a user