重构项目,方便开发

This commit is contained in:
super
2026-03-19 13:15:47 +08:00
parent fb2e5ccea5
commit 1e63ab291b
38 changed files with 5940 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
import { requestDeleteJson, requestGetJson, requestPostJson, requestPutJson } from '@/shared/api/http'
export interface AdminUserItem {
id: number
username?: string
role?: 'super_admin' | 'admin' | 'normal' | string
creator_username?: string
created_at?: string
}
export interface AdminSimpleUser {
id: number
username: string
}
export interface AdminUsersResponse {
success: boolean
current_user_role?: 'super_admin' | 'admin' | 'normal' | string
admins?: AdminSimpleUser[]
items?: AdminUserItem[]
total?: number
page?: number
page_size?: number
error?: string
}
export interface AdminMutationResponse {
success: boolean
msg?: string
error?: string
}
export interface AdminHistoryItem {
id: number
username?: string
panel_type?: string
created_at?: string
long_image_url?: string | null
result_urls?: string[]
}
export interface AdminHistoryResponse {
success: boolean
items?: AdminHistoryItem[]
total?: number
page?: number
page_size?: number
error?: string
}
export interface AdminColumnPermissionItem {
column_key?: string
}
export interface AdminColumnPermissionResponse {
success: boolean
items?: AdminColumnPermissionItem[]
error?: string
}
export function getAdminUsers(query = '') {
const suffix = query ? `?${query}` : ''
return requestGetJson<AdminUsersResponse>(`/api/admin/users${suffix}`)
}
export function createAdminUser(payload: {
username: string
password: string
role: string
created_by_id?: number
}) {
return requestPostJson<AdminMutationResponse>('/api/admin/user', payload)
}
export function updateAdminUser(
userId: number | string,
payload: {
password?: string
role?: string
},
) {
return requestPutJson<AdminMutationResponse>(`/api/admin/user/${userId}`, payload)
}
export function deleteAdminUser(userId: number | string) {
return requestDeleteJson<AdminMutationResponse>(`/api/admin/user/${userId}`)
}
export function getAdminHistory(query = '') {
const suffix = query ? `?${query}` : ''
return requestGetJson<AdminHistoryResponse>(`/api/admin/history${suffix}`)
}
export function getUserColumnPermissions(userId: string | number) {
return requestGetJson<AdminColumnPermissionResponse>(`/api/admin/user/${userId}/column-permissions`)
}

View File

@@ -0,0 +1,38 @@
import { requestGetJson, requestPostJson } from '@/shared/api/http'
export interface LoginResponse {
success: boolean
redirect?: string
error?: string
}
export interface AuthCheckResponse {
logged_in: boolean
redirect?: string
}
export async function checkAuth() {
return requestGetJson<AuthCheckResponse>('/api/auth/check')
}
export async function login(username: string, password: string) {
const formData = new FormData()
formData.set('username', username)
formData.set('password', password)
return requestPostJson<LoginResponse>('/login', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
}
export async function logout() {
try {
await requestGetJson('/logout', {
validateStatus: () => true,
})
} catch {
// logout should still continue on the client even if backend responds abnormally
}
}

View File

@@ -0,0 +1,88 @@
import { requestDeleteJson, requestGetJson, requestPostJson } from '@/shared/api/http'
export interface BrandExpandFolderResponse {
success: boolean
paths?: string[]
error?: string
}
export interface BrandTaskResultPaths {
zip_url?: string
}
export interface BrandTaskItem {
id: number | string
status?: 'pending' | 'running' | 'success' | 'failed' | 'cancelled' | string
desc?: string
file_paths?: string[]
created_at?: string
progress_total?: number
progress_current?: number
result_paths?: BrandTaskResultPaths
}
export interface BrandTaskListResponse {
success: boolean
items?: BrandTaskItem[]
error?: string
}
export interface BrandTaskDetailResponse {
success: boolean
task?: BrandTaskItem
error?: string
}
export interface BrandTaskMutationResponse {
success: boolean
task_id?: string
error?: string
}
export function getBrandTaskEventsUrl(taskId: string | number) {
return `/api/brand/tasks/${taskId}/events`
}
export function getBrandTaskDownloadUrl(taskId: string | number) {
return `/api/brand/download/${taskId}`
}
export function getBrandTemplateXlsxUrl() {
return '/static/品牌文档格式_模板.xlsx'
}
export function getBrandTemplateZipUrl() {
return '/static/模板2-以文件夹方式上传.zip'
}
export function expandBrandFolder(folder: string) {
return requestPostJson<BrandExpandFolderResponse>('/api/brand/expand-folder', { folder })
}
export function runBrandNow(paths: string[], strategy: string) {
return requestPostJson<BrandTaskMutationResponse>('/api/brand/run', { paths, strategy })
}
export function createBrandTask(paths: string[], strategy: string) {
return requestPostJson<BrandTaskMutationResponse>('/api/brand/tasks', { paths, strategy })
}
export function getBrandTasks() {
return requestGetJson<BrandTaskListResponse>('/api/brand/tasks')
}
export function getBrandTask(taskId: string | number) {
return requestGetJson<BrandTaskDetailResponse>(`/api/brand/tasks/${taskId}`)
}
export function cancelBrandTask(taskId: string | number) {
return requestPostJson<BrandTaskMutationResponse>(`/api/brand/tasks/${taskId}/cancel`)
}
export function deleteBrandTask(taskId: string | number) {
return requestDeleteJson<BrandTaskMutationResponse>(`/api/brand/tasks/${taskId}`)
}
export function createBrandTaskEvents(taskId: string | number) {
return new EventSource(getBrandTaskEventsUrl(taskId))
}

View File

@@ -0,0 +1,107 @@
import axios, {
AxiosError,
type AxiosInstance,
type AxiosRequestConfig,
type AxiosResponse,
type InternalAxiosRequestConfig,
} from 'axios'
export interface ApiSuccess<T> {
success: true
msg?: string
data?: T
}
export interface ApiFailure {
success: false
error?: string
}
export type ApiResponse<T> = ApiSuccess<T> | ApiFailure
export type RequestOptions<D = unknown> = AxiosRequestConfig<D>
function extractErrorMessage(error: unknown) {
if (error instanceof AxiosError) {
const data = error.response?.data
if (data && typeof data === 'object' && 'error' in data) {
return String(data.error)
}
if (typeof data === 'string' && data.trim()) {
return data
}
return error.message || '请求失败'
}
return error instanceof Error ? error.message : '请求失败'
}
function createHttpClient(): AxiosInstance {
const instance = axios.create({
withCredentials: true,
timeout: 30000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
})
instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
return config
})
instance.interceptors.response.use(
(response: AxiosResponse) => response,
(error: unknown) => Promise.reject(new Error(extractErrorMessage(error))),
)
return instance
}
export const http = createHttpClient()
export async function request<T = unknown, D = unknown>(config: RequestOptions<D>): Promise<T> {
const response = await http.request<T, AxiosResponse<T>, D>(config)
return response.data
}
export function get<T = unknown>(url: string, config?: RequestOptions) {
return request<T>({
url,
method: 'GET',
...config,
})
}
export function post<T = unknown, D = unknown>(url: string, data?: D, config?: RequestOptions<D>) {
return request<T, D>({
url,
method: 'POST',
data,
...config,
})
}
export function put<T = unknown, D = unknown>(url: string, data?: D, config?: RequestOptions<D>) {
return request<T, D>({
url,
method: 'PUT',
data,
...config,
})
}
export function del<T = unknown>(url: string, config?: RequestOptions) {
return request<T>({
url,
method: 'DELETE',
...config,
})
}
export const requestJson = request
export const requestGetJson = get
export const requestPostJson = post
export const requestPutJson = put
export const requestDeleteJson = del

View File

@@ -0,0 +1,22 @@
import { requestGetJson, requestPostJson } from '@/shared/api/http'
export interface VersionResponse {
version?: string
has_update?: boolean
latest_version?: string
desc?: string
file_url?: string
}
export interface UpdateStartResponse {
success: boolean
error?: string
}
export function fetchVersion() {
return requestGetJson<VersionResponse>('/api/version')
}
export function startUpdate(fileUrl: string) {
return requestPostJson<UpdateStartResponse>('/api/update/do', { file_url: fileUrl })
}