feat: add rider h5 MVP

This commit is contained in:
王鹏
2026-07-07 16:44:48 +08:00
parent 576c54ec9d
commit 9ad43005c4
17 changed files with 3195 additions and 0 deletions

45
rider-h5/src/api/http.ts Normal file
View File

@@ -0,0 +1,45 @@
import axios, { AxiosHeaders } from 'axios'
export interface ApiEnvelope<T> {
code: number
message: string
data: T
}
export const TOKEN_KEY = 'rider_token'
export const USER_KEY = 'rider_user'
export const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || ''
})
http.interceptors.request.use((config) => {
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
const headers = AxiosHeaders.from(config.headers)
headers.set('satoken', token)
headers.set('Authorization', token)
config.headers = headers
}
return config
})
async function unwrap<T>(request: Promise<{ data: ApiEnvelope<T> }>): Promise<T> {
const response = await request
if (response.data.code !== 0) {
throw new Error(response.data.message || '请求失败')
}
return response.data.data
}
export function apiGet<T>(url: string, params?: Record<string, unknown>) {
return unwrap<T>(http.get(url, { params }))
}
export function apiPost<T>(url: string, data?: unknown) {
return unwrap<T>(http.post(url, data))
}
export function apiPut<T>(url: string, data?: unknown) {
return unwrap<T>(http.put(url, data))
}

View File

@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest'
import { groupTasksByStatus } from './riderTaskApi'
describe('groupTasksByStatus', () => {
it('groups assigned delivering completed and exception tasks', () => {
const grouped = groupTasksByStatus([
{ id: 1, status: 'ASSIGNED' },
{ id: 2, status: 'DELIVERING' },
{ id: 3, status: 'DELIVERED' },
{ id: 4, status: 'EXCEPTION' }
])
expect(grouped.assigned).toHaveLength(1)
expect(grouped.delivering).toHaveLength(1)
expect(grouped.completed).toHaveLength(1)
expect(grouped.exception).toHaveLength(1)
})
})

View File

@@ -0,0 +1,58 @@
import type { CurrentUser, DeliveryTask, FileUploadResult, RiderIncomeSummary } from '../types'
import { apiGet, apiPost, apiPut, http } from './http'
export interface TaskGroups<T extends { status: string }> {
assigned: T[]
delivering: T[]
completed: T[]
exception: T[]
canceled: T[]
}
export function groupTasksByStatus<T extends { status: string }>(tasks: T[]): TaskGroups<T> {
return {
assigned: tasks.filter((task) => task.status === 'ASSIGNED' || task.status === 'ACCEPTED'),
delivering: tasks.filter((task) => task.status === 'DELIVERING'),
completed: tasks.filter((task) => task.status === 'DELIVERED' || task.status === 'COMPLETED'),
exception: tasks.filter((task) => task.status === 'EXCEPTION'),
canceled: tasks.filter((task) => task.status === 'CANCELED')
}
}
export function riderLogin(username: string, password: string) {
return apiPost<CurrentUser>('/api/auth/admin-login', { username, password })
}
export function listRiderTasks() {
return apiGet<DeliveryTask[]>('/api/rider/tasks')
}
export function getRiderTask(id: number) {
return apiGet<DeliveryTask>(`/api/rider/tasks/${id}`)
}
export function startRiderTask(id: number) {
return apiPut<DeliveryTask>(`/api/rider/tasks/${id}/start`)
}
export function deliverRiderTask(id: number, photoUrl?: string) {
return apiPut<DeliveryTask>(`/api/rider/tasks/${id}/delivered`, { photoUrl })
}
export function markRiderTaskException(id: number, exceptionType: string, exceptionNote: string) {
return apiPut<DeliveryTask>(`/api/rider/tasks/${id}/exception`, { exceptionType, exceptionNote })
}
export function getIncomeSummary() {
return apiGet<RiderIncomeSummary>('/api/rider/income-summary')
}
export async function uploadDeliveredPhoto(file: File) {
const data = new FormData()
data.append('file', file)
const response = await http.post<{ code: number; message: string; data: FileUploadResult }>('/api/files/upload', data)
if (response.data.code !== 0) {
throw new Error(response.data.message || '上传失败')
}
return response.data.data
}