- 将 App.vue 拆分为 AdminPage、ClientPage、QrCodeModal 三个独立组件 - 新增 BrowseDirectory 接口,基于 RelativePath 实现层级目录浏览 - 前端新增面包屑导航、文件夹网格、文件列表等目录浏览 UI - 新增对应 CSS 样式(breadcrumb、folder-grid、file-list 等) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import { apiUrl, request } from './http'
|
|
|
|
export type MediaType = 'all' | 'text' | 'video' | 'audio'
|
|
|
|
export interface DriveDto {
|
|
name: string
|
|
displayName: string
|
|
rootDirectory: string
|
|
driveType: string
|
|
totalSize: number | null
|
|
availableFreeSpace: number | null
|
|
isReady: boolean
|
|
}
|
|
|
|
export interface DirectoryDto {
|
|
name: string
|
|
fullPath: string
|
|
}
|
|
|
|
export interface LibraryRootDto {
|
|
id: number
|
|
path: string
|
|
displayName: string
|
|
isEnabled: boolean
|
|
isAvailable: boolean
|
|
scanIntervalMinutes: number
|
|
lastScanStartedAt: string | null
|
|
lastScanCompletedAt: string | null
|
|
lastScanError: string | null
|
|
fileCount: number
|
|
}
|
|
|
|
export interface FileRecordDto {
|
|
id: number
|
|
libraryRootId: number
|
|
fileName: string
|
|
relativePath: string
|
|
extension: string
|
|
sizeBytes: number
|
|
lastWriteTimeUtc: string
|
|
mediaType: 'text' | 'video' | 'audio'
|
|
contentType: string
|
|
streamUrl: string
|
|
textUrl: string | null
|
|
browserPlayable: boolean
|
|
}
|
|
|
|
export interface BrowseDirectoryResponse {
|
|
currentPath: string
|
|
subdirectories: string[]
|
|
files: FileRecordDto[]
|
|
}
|
|
|
|
export interface TextPreviewDto {
|
|
id: number
|
|
fileName: string
|
|
content: string
|
|
truncated: boolean
|
|
}
|
|
|
|
export interface PagedResponse<T> {
|
|
items: T[]
|
|
total: number
|
|
page: number
|
|
pageSize: number
|
|
totalPages: number
|
|
}
|
|
|
|
const qs = (params: Record<string, string | number | undefined | null>) => {
|
|
const search = new URLSearchParams()
|
|
for (const [key, value] of Object.entries(params)) {
|
|
if (value !== undefined && value !== null && value !== '') search.set(key, String(value))
|
|
}
|
|
const value = search.toString()
|
|
return value ? `?${value}` : ''
|
|
}
|
|
|
|
// 业务接口定义,新增接口在此处添加一行即可
|
|
export const api = {
|
|
getUser: () => request('getUser'),
|
|
processData: (input: string) => request('processData', { method: 'POST', body: { input } }),
|
|
wData: (input: string) => request('wData', { method: 'POST', body: { input } }),
|
|
getDrives: () => request<DriveDto[]>('library/drives'),
|
|
getDirectories: (path: string) => request<DirectoryDto[]>(`library/directories${qs({ path })}`),
|
|
getRoots: () => request<LibraryRootDto[]>('library/roots'),
|
|
addRoot: (body: { path: string; displayName?: string; scanIntervalMinutes?: number }) =>
|
|
request<LibraryRootDto>('library/roots', { method: 'POST', body }),
|
|
setRootEnabled: (id: number, isEnabled: boolean) =>
|
|
request<LibraryRootDto>('library/roots/enabled', { method: 'POST', body: { id, isEnabled } }),
|
|
deleteRoot: (id: number) =>
|
|
request('library/roots/delete', { method: 'POST', body: { id } }),
|
|
scanRoot: (id: number) =>
|
|
request<LibraryRootDto>('library/roots/scan', { method: 'POST', body: { id } }),
|
|
searchFiles: (params: { page: number; pageSize: number; mediaType?: MediaType; keyword?: string; rootId?: number }) =>
|
|
request<PagedResponse<FileRecordDto>>(`files${qs(params)}`),
|
|
browseDirectory: (rootId: number, path: string) =>
|
|
request<BrowseDirectoryResponse>(`files/browse${qs({ rootId, path })}`),
|
|
getTextPreview: (id: number) =>
|
|
request<TextPreviewDto>(`files/text${qs({ id })}`),
|
|
mediaUrl: (path: string) => apiUrl(path),
|
|
qrCode: () => request<{ url: string; qrCodeBase64: string }>('qrcode'),
|
|
}
|