Merge remote-tracking branch 'origin/v0.3' into feat/2891-linux-hwpod-qemu

This commit is contained in:
root
2026-07-23 05:42:43 +02:00
3 changed files with 55 additions and 11 deletions
@@ -0,0 +1,28 @@
import { describe, expect, it, vi } from "vitest";
import { loadTaskTreeRoute } from "./tasktree-route-loader";
describe("loadTaskTreeRoute", () => {
it("loads only the global overview for the TaskTree root route", async () => {
const overview = vi.fn().mockResolvedValue({ ok: true });
const timeline = vi.fn();
const result = await loadTaskTreeRoute({ overview, timeline }, "");
expect(result.view).toBe("global");
expect(overview).toHaveBeenCalledOnce();
expect(timeline).not.toHaveBeenCalled();
});
it("loads a group timeline without depending on the global overview", async () => {
const overview = vi.fn();
const timeline = vi.fn().mockResolvedValue({ ok: true });
const result = await loadTaskTreeRoute({ overview, timeline }, "tg_example");
expect(result.view).toBe("group");
expect(timeline).toHaveBeenCalledOnce();
expect(timeline).toHaveBeenCalledWith("tg_example");
expect(overview).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,10 @@
import type { tasktreeAPI } from "@/api/tasktree";
type TaskTreeReadApi = Pick<typeof tasktreeAPI, "overview" | "timeline">;
export async function loadTaskTreeRoute(api: TaskTreeReadApi, groupId: string) {
if (groupId) {
return { view: "group" as const, response: await api.timeline(groupId) };
}
return { view: "global" as const, response: await api.overview() };
}
@@ -9,6 +9,7 @@ import TaskTreeDetailPanel from "@/components/tasktree/TaskTreeDetailPanel.vue";
import MessageMarkdown from "@/components/workbench/MessageMarkdown.vue";
import { displayTimeConfig } from "@/config/runtime";
import { decideNestedWheelBoundary, type NestedWheelBoundaryState } from "@/utils/nested-wheel-boundary";
import { loadTaskTreeRoute } from "@/utils/tasktree-route-loader";
import { deriveEffectiveTaskTimeRanges } from "@/utils/tasktree-time-range";
const route = useRoute();
@@ -108,32 +109,37 @@ watch(requestedDetailMode, (mode) => { detailMode.value = mode; });
async function loadActiveView() {
loading.value = true; error.value = null;
const overviewResponse = await tasktreeAPI.overview();
if (!overviewResponse.ok || overviewResponse.data?.ok === false) {
loading.value = false;
error.value = overviewResponse.data?.error?.message || overviewResponse.error || "TaskTree 全局视图加载失败";
return;
}
overview.value = overviewResponse.data?.data?.groups ?? [];
groups.value = overview.value.map((item) => item.group);
if (!groupId.value) {
const activeGroupId = groupId.value;
const result = await loadTaskTreeRoute(tasktreeAPI, activeGroupId);
if (result.view === "global") {
const overviewResponse = result.response;
if (!overviewResponse.ok || overviewResponse.data?.ok === false) {
loading.value = false;
error.value = overviewResponse.data?.error?.message || overviewResponse.error || "TaskTree 全局视图加载失败";
return;
}
overview.value = overviewResponse.data?.data?.groups ?? [];
groups.value = overview.value.map((item) => item.group);
timeline.value = null;
loading.value = false;
await nextTick();
positionTimelineAtNow("global");
return;
}
const timelineResponse = await tasktreeAPI.timeline(groupId.value);
const timelineResponse = result.response;
loading.value = false;
if (!timelineResponse.ok || timelineResponse.data?.ok === false || !timelineResponse.data?.data) {
error.value = timelineResponse.data?.error?.message || timelineResponse.error || "TaskTree timeline 加载失败";
return;
}
timeline.value = timelineResponse.data.data;
if (!groups.value.some((group) => group.id === timeline.value?.group.id)) {
groups.value = [...groups.value, timeline.value.group];
}
initializedChildGroups.clear();
syncSelectedTaskFromRoute();
await nextTick();
positionTimelineAtNow(groupId.value);
positionTimelineAtNow(activeGroupId);
}
function selectGroup(event: Event) {
const value = (event.target as HTMLSelectElement).value;