| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import type { AdminStats, Course, CourseStatus, User } from './types'
- const token = () => localStorage.getItem('xinghen_token')
- async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
- const headers = new Headers(init.headers)
- if (token()) headers.set('Authorization', `Bearer ${token()}`)
- const response = await fetch(`/api${path}`, { ...init, headers })
- if (!response.ok) {
- const body = await response.json().catch(() => ({ message: '请求失败,请稍后重试' }))
- if (response.status === 401) {
- localStorage.removeItem('xinghen_token')
- window.dispatchEvent(new Event('auth-expired'))
- }
- throw new Error(body.message || '请求失败,请稍后重试')
- }
- return response.json() as Promise<T>
- }
- export const api = {
- // 用户鉴权与信息
- register: (phone: string, password: string) =>
- request<{ token: string; user: User }>('/auth/register', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, password })
- }),
- login: (phone: string, password: string) =>
- request<{ token: string; user: User }>('/auth/login', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, password })
- }),
- logout: () => request<{ ok: boolean }>('/auth/logout', { method: 'POST' }),
- me: () => request<{ user: User }>('/me'),
- updateMe: (body: Pick<User, 'organization' | 'wechat' | 'contactName' | 'bio'>) =>
- request<{ user: User }>('/me', {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(body)
- }),
- changePassword: (currentPassword: string, newPassword: string) =>
- request<{ ok: boolean }>('/me/password', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ currentPassword, newPassword })
- }),
- // 普通用户课程操作
- listCourses: () => request<{ courses: Course[] }>('/courses'),
- createCourse: (body: Pick<Course, 'name' | 'category' | 'audience' | 'description'>) =>
- request<{ course: Course }>('/courses', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(body)
- }),
- getCourse: (id: string) => request<{ course: Course }>(`/courses/${id}`),
- uploadAssets: (id: string, files: File[]) => {
- const form = new FormData()
- files.forEach((f) => form.append('assets', f))
- return request<{ course: Course }>(`/courses/${id}/assets`, { method: 'POST', body: form })
- },
- deleteAsset: (id: string, assetId: string) =>
- request<{ course: Course }>(`/courses/${id}/assets/${assetId}`, { method: 'DELETE' }),
- addInstructor: (
- id: string,
- data: { name: string; organization: string; introduction: string; image?: File }
- ) => {
- const f = new FormData()
- f.append('name', data.name)
- f.append('organization', data.organization)
- f.append('introduction', data.introduction)
- if (data.image) f.append('image', data.image)
- return request<{ course: Course }>(`/courses/${id}/instructors`, { method: 'POST', body: f })
- },
- deleteInstructor: (id: string, instructorId: string) =>
- request<{ course: Course }>(`/courses/${id}/instructors/${instructorId}`, { method: 'DELETE' }),
- submitCourse: (id: string) => request<{ course: Course }>(`/courses/${id}/submit`, { method: 'POST' }),
- // 管理员后台专区 API
- admin: {
- login: (phone: string, password: string) =>
- request<{ token: string; user: User }>('/admin/auth/login', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, password })
- }),
- getStats: () => request<AdminStats>('/admin/stats'),
- listCourses: (params?: { status?: string; keyword?: string }) => {
- const q = new URLSearchParams()
- if (params?.status) q.set('status', params.status)
- if (params?.keyword) q.set('keyword', params.keyword)
- const qs = q.toString() ? `?${q.toString()}` : ''
- return request<{ courses: Course[]; total: number }>(`/admin/courses${qs}`)
- },
- getCourse: (id: string) => request<{ course: Course }>(`/admin/courses/${id}`),
- updateStatus: (id: string, status: CourseStatus) =>
- request<{ course: Course }>(`/admin/courses/${id}/status`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ status })
- }),
- uploadDeliverables: (id: string, files: File[]) => {
- const form = new FormData()
- files.forEach((f) => form.append('deliverables', f))
- return request<{ course: Course }>(`/admin/courses/${id}/deliverables`, { method: 'POST', body: form })
- },
- deleteDeliverable: (id: string, delivId: string) =>
- request<{ course: Course }>(`/admin/courses/${id}/deliverables/${delivId}`, { method: 'DELETE' }),
- completeCourse: (id: string, productionNotes: string) =>
- request<{ course: Course }>(`/admin/courses/${id}/complete`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ productionNotes })
- }),
- listUsers: (keyword?: string) => {
- const qs = keyword ? `?keyword=${encodeURIComponent(keyword)}` : ''
- return request<{ users: User[] }>(`/admin/users${qs}`)
- },
- updateUserRole: (id: string, role: 'USER' | 'ADMIN') =>
- request<{ user: User }>(`/admin/users/${id}/role`, {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ role })
- })
- }
- }
|