diff --git a/frontend/app.js b/frontend/app.js
index c746273..eaf6426 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -1049,7 +1049,11 @@ function mountTaskBoard(queryParams) {
}
rowsEl.innerHTML = tasks.map((t) => (
'
'
- + "| " + t.task_id + " | "
+ // task_id links straight to Graph 控制台 (#/graph?task_id=...) so a
+ // task can be located there directly, without going through the row's
+ // own click-through to #/tasks/:task_id first -- stopPropagation on
+ // the anchor keeps that row click-through intact for the other cells.
+ + '' + t.task_id + " | "
+ "" + escapeHtml(t.goal_text) + " | "
+ '' + t.status + " | "
+ "" + t.assigned_org_node + " | "
@@ -2037,7 +2041,7 @@ function renderGraphConsole() {
`;
}
-function mountGraphConsole() {
+function mountGraphConsole(queryParams) {
const projectId = getCurrentProjectId();
if (!projectId) return;
@@ -2046,7 +2050,12 @@ function mountGraphConsole() {
const workGraphPanelsEl = document.getElementById("gc_work_graph_panels");
const artifactsOverviewEl = document.getElementById("gc_artifacts_overview");
- let currentTaskId = null;
+ // "#/graph?task_id="-style deep link (e.g. from v_task_board's task_id
+ // column) preselects this task on first load, same pattern as
+ // pickInitialTaskBoardStatus. populateTaskSelect below already falls back
+ // to the default "first non-done" task if this id doesn't match any task
+ // in the project, so an unknown/stale id is silently ignored.
+ let currentTaskId = (queryParams && queryParams.task_id) || null;
// Same "track open log viewers, close them all on re-render/route-leave"
// pattern as mountTaskDetail's activeLogViewers/closeAllLogViewers, so a
@@ -2083,6 +2092,13 @@ function mountGraphConsole() {
async function loadTaskAndRender(taskId) {
closeAllLogViewers();
currentTaskId = taskId;
+ // Keep the URL in sync with whichever task is actually showing --
+ // including the initial auto-preselect, not just explicit dropdown
+ // switches -- so the address bar is always a valid deep link back to
+ // this exact task (same history.replaceState/no-hashchange pattern
+ // v_task_detail's org-strip click-through already uses above).
+ const newHash = "#/graph?task_id=" + encodeURIComponent(taskId);
+ if (window.location.hash !== newHash) history.replaceState(null, "", newHash);
workGraphPanelsEl.innerHTML = '加载中…
';
const resp = await fetch("/api/tasks/" + encodeURIComponent(taskId));
if (resp.status !== 200) {