Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/composables/useTableLoader.ts
T
2026-06-20 15:38:11 +08:00

44 lines
1.7 KiB
TypeScript

import { computed, ref, type ComputedRef, type Ref } from "vue";
import { apiErrorContextFromUnknown } from "@/api/client";
import type { ApiError, ApiResult, ErrorDiagnostic } from "@/types";
type TableLoaderResult<T> = T[] | ApiResult<T[]>;
export function useTableLoader<T>(loader: () => Promise<TableLoaderResult<T>>): { rows: Ref<T[]>; loading: Ref<boolean>; error: Ref<string | null>; apiError: Ref<ApiError | null>; diagnostic: Ref<ErrorDiagnostic | null>; empty: ComputedRef<boolean>; reload: () => Promise<void> } {
const rows = ref([]) as Ref<T[]>;
const loading = ref(false);
const error = ref<string | null>(null);
const apiError = ref<ApiError | null>(null);
const diagnostic = ref<ErrorDiagnostic | null>(null);
const empty = computed(() => !loading.value && rows.value.length === 0);
async function reload(): Promise<void> {
loading.value = true;
error.value = null;
apiError.value = null;
diagnostic.value = null;
try {
const result = await loader();
if (isApiResult(result)) {
if (!result.ok) throw result;
rows.value = result.data ?? [];
} else {
rows.value = result;
}
} catch (err) {
const context = apiErrorContextFromUnknown(err);
error.value = context.error;
apiError.value = context.apiError;
diagnostic.value = context.diagnostic;
} finally {
loading.value = false;
}
}
return { rows, loading, error, apiError, diagnostic, empty, reload };
}
function isApiResult<T>(value: TableLoaderResult<T>): value is ApiResult<T[]> {
return Boolean(value && !Array.isArray(value) && typeof value === "object" && "ok" in value && "status" in value);
}