@qnote/q-ai-note 1.0.9 → 1.0.11
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/dist/web/app.js +99 -1
- package/dist/web/index.html +5 -0
- package/dist/web/styles.css +17 -8
- package/dist/web/vueRenderers.js +3 -10
- package/package.json +1 -1
package/dist/web/app.js
CHANGED
|
@@ -45,6 +45,7 @@ const state = {
|
|
|
45
45
|
changesSandboxFilter: '',
|
|
46
46
|
changesTypeFilter: 'all',
|
|
47
47
|
changesQuickFilter: 'all',
|
|
48
|
+
workTreeExpandInitialized: false,
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
const expandedNodes = new Set();
|
|
@@ -331,6 +332,91 @@ function expandAllNodes() {
|
|
|
331
332
|
items.forEach(item => expandedNodes.add(item.id));
|
|
332
333
|
}
|
|
333
334
|
|
|
335
|
+
function buildWorkItemDepthMap(items) {
|
|
336
|
+
const byId = new Map(items.map((item) => [item.id, item]));
|
|
337
|
+
const memo = new Map();
|
|
338
|
+
const visiting = new Set();
|
|
339
|
+
const walk = (id) => {
|
|
340
|
+
if (memo.has(id)) return memo.get(id);
|
|
341
|
+
if (visiting.has(id)) return 0;
|
|
342
|
+
visiting.add(id);
|
|
343
|
+
const item = byId.get(id);
|
|
344
|
+
if (!item) {
|
|
345
|
+
visiting.delete(id);
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
const parentId = item.parent_id;
|
|
349
|
+
const depth = parentId && byId.has(parentId) ? walk(parentId) + 1 : 0;
|
|
350
|
+
memo.set(id, depth);
|
|
351
|
+
visiting.delete(id);
|
|
352
|
+
return depth;
|
|
353
|
+
};
|
|
354
|
+
items.forEach((item) => walk(item.id));
|
|
355
|
+
return memo;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function getWorkTreeMaxDepth(items) {
|
|
359
|
+
const depthMap = buildWorkItemDepthMap(items || []);
|
|
360
|
+
let maxDepth = 0;
|
|
361
|
+
depthMap.forEach((depth) => {
|
|
362
|
+
if (Number.isFinite(depth)) maxDepth = Math.max(maxDepth, Number(depth));
|
|
363
|
+
});
|
|
364
|
+
return maxDepth;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function expandNodesToLevel(level, items) {
|
|
368
|
+
const safeLevel = Math.max(1, Number(level) || 1);
|
|
369
|
+
const depthMap = buildWorkItemDepthMap(items || []);
|
|
370
|
+
expandedNodes.clear();
|
|
371
|
+
(items || []).forEach((item) => {
|
|
372
|
+
const depth = Number(depthMap.get(item.id) || 0);
|
|
373
|
+
if (depth < safeLevel) expandedNodes.add(item.id);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function applyWorkTreeCollapseAction(action) {
|
|
378
|
+
if (!state.currentSandbox) return;
|
|
379
|
+
const items = state.currentSandbox.items || [];
|
|
380
|
+
if (!items.length) return;
|
|
381
|
+
if (action === 'expand-all') {
|
|
382
|
+
expandedNodes.clear();
|
|
383
|
+
expandAllNodes();
|
|
384
|
+
} else if (action === 'collapse-all') {
|
|
385
|
+
expandedNodes.clear();
|
|
386
|
+
} else if (String(action || '').startsWith('collapse-level:')) {
|
|
387
|
+
const level = Number(String(action).split(':')[1] || '1');
|
|
388
|
+
expandNodesToLevel(level, items);
|
|
389
|
+
}
|
|
390
|
+
state.workTreeExpandInitialized = true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function updateWorkTreeCollapseMenu(items) {
|
|
394
|
+
const selector = document.getElementById('work-tree-collapse-action');
|
|
395
|
+
if (!(selector instanceof HTMLSelectElement)) return;
|
|
396
|
+
const denseMode = state.workTreeViewMode === 'dense';
|
|
397
|
+
const hasItems = (items || []).length > 0;
|
|
398
|
+
selector.classList.toggle('hidden', !denseMode);
|
|
399
|
+
selector.disabled = !denseMode || !hasItems;
|
|
400
|
+
if (!denseMode || !hasItems) {
|
|
401
|
+
selector.innerHTML = '<option value="">层级操作</option>';
|
|
402
|
+
selector.value = '';
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const maxDepth = getWorkTreeMaxDepth(items);
|
|
406
|
+
const maxLevel = Math.max(1, maxDepth + 1);
|
|
407
|
+
const levelOptions = Array.from({ length: maxLevel }, (_v, idx) => {
|
|
408
|
+
const level = idx + 1;
|
|
409
|
+
return `<option value="collapse-level:${level}">折叠到 L${level}</option>`;
|
|
410
|
+
}).join('');
|
|
411
|
+
selector.innerHTML = `
|
|
412
|
+
<option value="">层级操作</option>
|
|
413
|
+
<option value="expand-all">全部展开</option>
|
|
414
|
+
<option value="collapse-all">全部折叠</option>
|
|
415
|
+
${levelOptions}
|
|
416
|
+
`;
|
|
417
|
+
selector.value = '';
|
|
418
|
+
}
|
|
419
|
+
|
|
334
420
|
function getRootNodeIds(items) {
|
|
335
421
|
return items.filter((item) => !item.parent_id).map((item) => item.id);
|
|
336
422
|
}
|
|
@@ -348,6 +434,8 @@ function applyWorkTreeViewMode(mode) {
|
|
|
348
434
|
} else {
|
|
349
435
|
expandAllNodes();
|
|
350
436
|
}
|
|
437
|
+
state.workTreeExpandInitialized = true;
|
|
438
|
+
updateWorkTreeCollapseMenu(items);
|
|
351
439
|
}
|
|
352
440
|
|
|
353
441
|
function applyWorkItemAssigneeToggle() {
|
|
@@ -374,13 +462,15 @@ function renderWorkTree() {
|
|
|
374
462
|
const entitySummaryByNodeId = buildNodeEntitySummaryByNodeId();
|
|
375
463
|
const entityRowsByNodeId = buildNodeEntityRowsByNodeId();
|
|
376
464
|
|
|
377
|
-
if (
|
|
465
|
+
if (!state.workTreeExpandInitialized && allItems.length > 0) {
|
|
378
466
|
if (state.workTreeViewMode === 'report') {
|
|
379
467
|
getRootNodeIds(allItems).forEach((id) => expandedNodes.add(id));
|
|
380
468
|
} else {
|
|
381
469
|
expandAllNodes();
|
|
382
470
|
}
|
|
471
|
+
state.workTreeExpandInitialized = true;
|
|
383
472
|
}
|
|
473
|
+
updateWorkTreeCollapseMenu(allItems);
|
|
384
474
|
|
|
385
475
|
if (allItems.length === 0) {
|
|
386
476
|
tree.innerHTML = `<div class="empty-state"><p>${treeReadonly ? '暂无任务' : '点击上方"添加"按钮创建第一个任务'}</p></div>`;
|
|
@@ -1453,6 +1543,7 @@ async function loadSandbox(id) {
|
|
|
1453
1543
|
apiRequest(`${API_BASE}/sandboxes/${id}/entities/stats`),
|
|
1454
1544
|
]);
|
|
1455
1545
|
state.currentSandbox = { ...sandbox, items, diaries };
|
|
1546
|
+
state.workTreeExpandInitialized = false;
|
|
1456
1547
|
state.nodeEntities = entities;
|
|
1457
1548
|
state.nodeEntityStats = entityStats;
|
|
1458
1549
|
|
|
@@ -3012,6 +3103,13 @@ async function initApp() {
|
|
|
3012
3103
|
renderWorkTree();
|
|
3013
3104
|
});
|
|
3014
3105
|
|
|
3106
|
+
document.getElementById('work-tree-collapse-action')?.addEventListener('change', (e) => {
|
|
3107
|
+
const action = e.target.value || '';
|
|
3108
|
+
if (!action) return;
|
|
3109
|
+
applyWorkTreeCollapseAction(action);
|
|
3110
|
+
renderWorkTree();
|
|
3111
|
+
});
|
|
3112
|
+
|
|
3015
3113
|
document.getElementById('work-item-element-preview-mode')?.addEventListener('change', (e) => {
|
|
3016
3114
|
state.workItemElementPreviewMode = e.target.value || 'none';
|
|
3017
3115
|
renderWorkTree();
|
package/dist/web/index.html
CHANGED
|
@@ -63,6 +63,11 @@
|
|
|
63
63
|
<option value="report">汇报折叠模式</option>
|
|
64
64
|
<option value="dense">树模式</option>
|
|
65
65
|
</select>
|
|
66
|
+
<select id="work-tree-collapse-action" title="树模式层级操作">
|
|
67
|
+
<option value="">层级操作</option>
|
|
68
|
+
<option value="expand-all">全部展开</option>
|
|
69
|
+
<option value="collapse-all">全部折叠</option>
|
|
70
|
+
</select>
|
|
66
71
|
<select id="work-item-element-preview-mode">
|
|
67
72
|
<option value="none">能力要素:不显示</option>
|
|
68
73
|
<option value="issue">能力要素:Issue</option>
|
package/dist/web/styles.css
CHANGED
|
@@ -221,6 +221,10 @@ h2 {
|
|
|
221
221
|
gap: 8px;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
#work-tree-collapse-action {
|
|
225
|
+
min-width: 132px;
|
|
226
|
+
}
|
|
227
|
+
|
|
224
228
|
.inline-checkbox {
|
|
225
229
|
display: inline-flex;
|
|
226
230
|
align-items: center;
|
|
@@ -770,21 +774,26 @@ h2 {
|
|
|
770
774
|
}
|
|
771
775
|
|
|
772
776
|
.lane-tree-title-line {
|
|
773
|
-
display:
|
|
777
|
+
display: flex;
|
|
778
|
+
flex-wrap: wrap;
|
|
774
779
|
align-items: center;
|
|
775
780
|
gap: 4px;
|
|
776
781
|
min-width: 0;
|
|
777
782
|
}
|
|
778
783
|
|
|
779
|
-
.lane-tree-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
+
.lane-tree-title-line .dense-node-name {
|
|
785
|
+
flex: 1 1 auto;
|
|
786
|
+
min-width: 96px;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.lane-tree-title-line .node-entity-mini-badges,
|
|
790
|
+
.lane-tree-title-line .node-meta {
|
|
791
|
+
flex: 0 0 auto;
|
|
792
|
+
max-width: 100%;
|
|
784
793
|
}
|
|
785
794
|
|
|
786
|
-
.lane-tree-
|
|
787
|
-
|
|
795
|
+
.lane-tree-title-line .node-meta {
|
|
796
|
+
white-space: nowrap;
|
|
788
797
|
}
|
|
789
798
|
|
|
790
799
|
.lane-tree-node {
|
package/dist/web/vueRenderers.js
CHANGED
|
@@ -318,7 +318,6 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
|
|
|
318
318
|
const hasChildren = children.length > 0;
|
|
319
319
|
const isExpanded = expandedIdSet.has(node.id);
|
|
320
320
|
const nodeSummary = entitySummaryByNodeId?.[node.id] || { issue: 0, knowledge: 0, capability: 0 };
|
|
321
|
-
const useStackSummary = depth >= 3;
|
|
322
321
|
const summaryBadgeHtml = renderEntityBadges(nodeSummary);
|
|
323
322
|
const summaryAssigneeHtml = showAssignee && node.assignee ? `<span class="node-meta">@${esc(node.assignee)}</span>` : '';
|
|
324
323
|
const previewHtml = renderEntityPreviewBoxes(node.id, entityRowsByNodeId, elementPreviewMode);
|
|
@@ -342,18 +341,12 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
|
|
|
342
341
|
</button>
|
|
343
342
|
` : ''}
|
|
344
343
|
<span class="node-status ${esc(node.status)}"></span>
|
|
345
|
-
<div class="lane-tree-node-main
|
|
344
|
+
<div class="lane-tree-node-main">
|
|
346
345
|
<div class="lane-tree-title-line">
|
|
347
346
|
<span class="dense-node-name">${esc(node.name)}</span>
|
|
348
|
-
${
|
|
349
|
-
${
|
|
347
|
+
${summaryBadgeHtml}
|
|
348
|
+
${summaryAssigneeHtml}
|
|
350
349
|
</div>
|
|
351
|
-
${useStackSummary && (summaryBadgeHtml || summaryAssigneeHtml) ? `
|
|
352
|
-
<div class="lane-tree-summary-line">
|
|
353
|
-
${summaryBadgeHtml}
|
|
354
|
-
${summaryAssigneeHtml}
|
|
355
|
-
</div>
|
|
356
|
-
` : ''}
|
|
357
350
|
</div>
|
|
358
351
|
${readonly ? '' : `
|
|
359
352
|
<div class="node-actions">
|