feat(canvas): 条件读取与历史 - 减少画布同步传输并校准版本

This commit is contained in:
ddcat666
2026-09-29 08:26:29 +08:00
parent bcccddb4e9
commit 6ad1adc197
17 changed files with 783 additions and 25 deletions

View File

@@ -46,9 +46,10 @@ export const apiClient = axios.create({ baseURL: apiBaseURL, withCredentials: tr
* 取消请求必须继续抛出 AbortError,不能被包装成普通失败,否则页面切换会被误报为错误。
*/
export async function request<T>(promise: Promise<{ data: BackendEnvelope<T>; status?: number; headers?: unknown }>) {
export async function request<T>(promise: Promise<{ data: BackendEnvelope<T>; status?: number; headers?: unknown }>, options: { allowNotModified?: boolean } = {}) {
try {
const response = await promise;
if (response.status === 304 && options.allowNotModified) return { notModified: true } as T;
if (response.data.code !== 0) {
throw new ApiError(response.data.msg || "请求失败", {
status: response.status,
@@ -122,10 +123,13 @@ function retryAfterMilliseconds(headers: unknown) {
return Math.max(0, retryAt - Date.now());
}
export type HttpRequestConfig = Omit<AxiosRequestConfig, "method" | "url" | "data" | "baseURL">;
export type HttpRequestConfig = Omit<AxiosRequestConfig, "method" | "url" | "data" | "baseURL"> & {
allowNotModified?: boolean;
};
async function send<T>(method: string, url: string, data?: unknown, config?: HttpRequestConfig) {
return request<T>(apiClient.request<BackendEnvelope<T>>({ method, url, data, ...config }));
const { allowNotModified, ...axiosConfig } = config || {};
return request<T>(apiClient.request<BackendEnvelope<T>>({ method, url, data, ...axiosConfig }), { allowNotModified });
}
/**

View File

@@ -1,7 +1,6 @@
import type { Asset } from "@/stores/use-asset-store";
import type { CanvasProject } from "@/stores/canvas/use-canvas-store";
import { http, compactApiParams } from "@/services/api/request";
import { compactApiParams, http } from "@/services/api/request";
export type RemoteUserDataSummary = {
id: string;
@@ -117,8 +116,18 @@ export function listRemoteCanvasProjects() {
return http.get<{ projects: RemoteUserDataSummary[] }>("/canvas-projects");
}
export function getRemoteCanvasProject(id: string) {
return http.get<{ project: CanvasProject }>(`/canvas-projects/${encodeURIComponent(id)}`);
export function getRemoteCanvasProject(id: string, knownProject?: CanvasProject) {
if (!knownProject || knownProject.id !== id || !Number.isSafeInteger(knownProject.revision)) {
return http.get<{ project: CanvasProject }>(`/canvas-projects/${encodeURIComponent(id)}`).then((result) => ({ ...result, notModified: false }));
}
const etag = `"canvas-${knownProject.revision}"`;
return http
.get<{ project: CanvasProject } | { notModified: true }>(`/canvas-projects/${encodeURIComponent(id)}`, {
headers: { "If-None-Match": etag },
validateStatus: (status) => (status >= 200 && status < 300) || status === 304,
allowNotModified: true,
})
.then((result) => ("notModified" in result ? { project: knownProject, notModified: true } : { ...result, notModified: false }));
}
export function upsertRemoteCanvasProject(project: CanvasProject, options?: { repairMissingResources?: boolean }) {