feat(admin-portal): 完成参考项目差距闭环 - 4 批次补齐 + Vitest 测试
差距分析闭环(workline §7-§8): - P0:审计子模块 3 页 + 学校组织 7 页 - P1:系统设置 4 Card + 公告管理 + 邀请码管理 - P2:文件管理 + AI 配置 - P6:5 测试文件 / 69 用例全过 共享基础设施: - permissions +11 权限点 +14 路由 - view-models +20 接口 - i18n +100 key - graphql-client +25 operation - fixtures/handlers +15 mock +20 handler 质量:typecheck + lint 零错误,vitest 69/69 通过,arch.db 已更新
This commit is contained in:
102
apps/admin-portal/src/hooks/use-files.ts
Normal file
102
apps/admin-portal/src/hooks/use-files.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 文件管理 Hooks
|
||||
*
|
||||
* contract §2.4:adminFiles / adminFileStats / deleteFiles
|
||||
*/
|
||||
import { useCallback, useState } from "react";
|
||||
import { useGraphMutation, useGraphQuery } from "./use-graphql";
|
||||
import {
|
||||
ADMIN_FILES_QUERY,
|
||||
ADMIN_FILE_STATS_QUERY,
|
||||
DELETE_FILES_MUTATION,
|
||||
} from "@/lib/graphql-client";
|
||||
import type {
|
||||
FileViewModel,
|
||||
FileStats,
|
||||
PaginatedResult,
|
||||
} from "@/types/view-models";
|
||||
|
||||
/** 文件列表筛选参数 */
|
||||
export interface FileFilter {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
search?: string;
|
||||
mimeType?: string;
|
||||
entityType?: string;
|
||||
}
|
||||
|
||||
interface AdminFilesResponse {
|
||||
adminFiles: PaginatedResult<FileViewModel>;
|
||||
}
|
||||
|
||||
interface AdminFileStatsResponse {
|
||||
adminFileStats: FileStats;
|
||||
}
|
||||
|
||||
interface DeleteFilesResponse {
|
||||
deleteFiles: boolean;
|
||||
}
|
||||
|
||||
export function useFiles(filter: FileFilter) {
|
||||
const result = useGraphQuery<AdminFilesResponse>(ADMIN_FILES_QUERY, {
|
||||
filter,
|
||||
});
|
||||
return {
|
||||
...result,
|
||||
data: result.data?.adminFiles ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export function useFileStats() {
|
||||
const result = useGraphQuery<AdminFileStatsResponse>(ADMIN_FILE_STATS_QUERY);
|
||||
return {
|
||||
...result,
|
||||
data: result.data?.adminFileStats ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export function useDeleteFiles() {
|
||||
const [run, state] = useGraphMutation<DeleteFilesResponse, { ids: string[] }>(
|
||||
DELETE_FILES_MUTATION,
|
||||
);
|
||||
const deleteFiles = useCallback(
|
||||
async (ids: string[]) => {
|
||||
const res = await run({ ids });
|
||||
return res.data?.deleteFiles ?? false;
|
||||
},
|
||||
[run],
|
||||
);
|
||||
return [deleteFiles, state] as const;
|
||||
}
|
||||
|
||||
/** 文件筛选状态管理 */
|
||||
export function useFileFilter(initial?: FileFilter) {
|
||||
const [filter, setFilter] = useState<FileFilter>({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
...initial,
|
||||
});
|
||||
const setPage = useCallback(
|
||||
(page: number) => setFilter((f) => ({ ...f, page })),
|
||||
[],
|
||||
);
|
||||
const setSearch = useCallback(
|
||||
(search: string) => setFilter((f) => ({ ...f, search, page: 1 })),
|
||||
[],
|
||||
);
|
||||
const setMimeType = useCallback(
|
||||
(mimeType: string) =>
|
||||
setFilter((f) => ({ ...f, mimeType: mimeType || undefined, page: 1 })),
|
||||
[],
|
||||
);
|
||||
const setEntityType = useCallback(
|
||||
(entityType: string) =>
|
||||
setFilter((f) => ({
|
||||
...f,
|
||||
entityType: entityType || undefined,
|
||||
page: 1,
|
||||
})),
|
||||
[],
|
||||
);
|
||||
return { filter, setFilter, setPage, setSearch, setMimeType, setEntityType };
|
||||
}
|
||||
Reference in New Issue
Block a user