refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared

- consoles/admin/ → layouts/admin-layout/
- consoles/workbench/ → layouts/workbench-layout/ + features/chat/
- pages/ → features/ (dashboard, models, projects, not-found)
- components/ → shared/components/
- hooks/ → shared/hooks/
- utils/ → shared/utils/
- 更新所有 import 路径 (src/web/ + tests/web/)
- 更新开发文档 (README.md, frontend.md, architecture.md)
This commit is contained in:
2026-06-02 23:17:28 +08:00
parent 1f05f259d0
commit b1dec691e9
76 changed files with 249 additions and 111 deletions

View File

@@ -0,0 +1,145 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type {
CreateProjectRequest,
Project,
ProjectListResponse,
ProjectResponse,
ProjectStatus,
UpdateProjectRequest,
} from "../../../shared/api";
import { handleResponse, handleVoidResponse } from "../utils/api";
import { createConsoleLogger } from "../utils/logger";
const PROJECTS_KEY = ["projects"] as const;
const logger = createConsoleLogger();
export async function archiveProject(id: string): Promise<Project> {
const response = await fetch(`/api/projects/${id}/archive`, { method: "POST" });
return handleResponse(response, (data) => (data as ProjectResponse).project);
}
export async function createProject(data: CreateProjectRequest): Promise<Project> {
const response = await fetch("/api/projects", {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
method: "POST",
});
return handleResponse(response, (data) => (data as ProjectResponse).project);
}
export async function deleteProject(id: string): Promise<void> {
const response = await fetch(`/api/projects/${id}`, { method: "DELETE" });
return handleVoidResponse(response);
}
export async function fetchProject(id: string): Promise<Project> {
const response = await fetch(`/api/projects/${id}`);
return handleResponse(response, (data) => (data as ProjectResponse).project);
}
export async function fetchProjectList(params: {
keyword?: string;
page?: number;
pageSize?: number;
status?: ProjectStatus;
}): Promise<ProjectListResponse> {
const searchParams = new URLSearchParams();
if (params.page) searchParams.set("page", String(params.page));
if (params.pageSize) searchParams.set("pageSize", String(params.pageSize));
if (params.keyword) searchParams.set("keyword", params.keyword);
if (params.status) searchParams.set("status", params.status);
const qs = searchParams.toString();
const url = `/api/projects${qs ? `?${qs}` : ""}`;
const response = await fetch(url);
if (!response.ok) {
const body = (await response.json().catch(() => null)) as null | { error?: string };
throw new Error(body?.error ?? `HTTP ${response.status}`);
}
return response.json() as Promise<ProjectListResponse>;
}
export async function restoreProject(id: string): Promise<Project> {
const response = await fetch(`/api/projects/${id}/restore`, { method: "POST" });
return handleResponse(response, (data) => (data as ProjectResponse).project);
}
export async function updateProject(id: string, data: UpdateProjectRequest): Promise<Project> {
const response = await fetch(`/api/projects/${id}`, {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
method: "PATCH",
});
return handleResponse(response, (data) => (data as ProjectResponse).project);
}
export function useArchiveProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: archiveProject,
onSuccess: (data) => {
logger.info("项目归档成功", { name: data.name, projectId: data.id });
void queryClient.invalidateQueries({ queryKey: PROJECTS_KEY });
},
});
}
export function useCreateProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createProject,
onSuccess: (data) => {
logger.info("项目创建成功", { name: data.name, projectId: data.id });
void queryClient.invalidateQueries({ queryKey: PROJECTS_KEY });
},
});
}
export function useDeleteProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: deleteProject,
onSuccess: (_data, variables) => {
logger.info("项目删除成功", { projectId: variables });
void queryClient.invalidateQueries({ queryKey: PROJECTS_KEY });
},
});
}
export function useProject(id: string) {
return useQuery({
enabled: !!id,
queryFn: () => fetchProject(id),
queryKey: [...PROJECTS_KEY, "detail", id],
});
}
export function useProjectList(params: { keyword?: string; page?: number; pageSize?: number; status?: ProjectStatus }) {
return useQuery({
queryFn: () => fetchProjectList(params),
queryKey: [...PROJECTS_KEY, "list", params],
});
}
export function useRestoreProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: restoreProject,
onSuccess: (data) => {
logger.info("项目恢复成功", { name: data.name, projectId: data.id });
void queryClient.invalidateQueries({ queryKey: PROJECTS_KEY });
},
});
}
export function useUpdateProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (args: { data: UpdateProjectRequest; id: string }) => updateProject(args.id, args.data),
onSuccess: (data) => {
logger.info("项目更新成功", { name: data.name, projectId: data.id });
void queryClient.invalidateQueries({ queryKey: PROJECTS_KEY });
},
});
}