feat: add admin web MVP
This commit is contained in:
10
admin-web/src/api/authApi.ts
Normal file
10
admin-web/src/api/authApi.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { CurrentUser } from '../types'
|
||||
import { apiPost } from './http'
|
||||
|
||||
export function adminLogin(username: string, password: string) {
|
||||
return apiPost<CurrentUser>('/api/auth/admin-login', { username, password })
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return apiPost<void>('/api/auth/logout')
|
||||
}
|
||||
34
admin-web/src/api/deliveryApi.ts
Normal file
34
admin-web/src/api/deliveryApi.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { DeliveryOrder, DeliveryUser } from '../types'
|
||||
import { apiGet, apiPost, apiPut } from './http'
|
||||
|
||||
export function listDeliveryUsers() {
|
||||
return apiGet<DeliveryUser[]>('/api/admin/delivery-users')
|
||||
}
|
||||
|
||||
export function createDeliveryUser(data: Partial<DeliveryUser>) {
|
||||
return apiPost<DeliveryUser>('/api/admin/delivery-users', data)
|
||||
}
|
||||
|
||||
export function enableDeliveryUser(id: number) {
|
||||
return apiPut<DeliveryUser>(`/api/admin/delivery-users/${id}/enable`)
|
||||
}
|
||||
|
||||
export function disableDeliveryUser(id: number) {
|
||||
return apiPut<DeliveryUser>(`/api/admin/delivery-users/${id}/disable`)
|
||||
}
|
||||
|
||||
export function listDeliveryOrders(status?: string) {
|
||||
return apiGet<DeliveryOrder[]>('/api/admin/delivery-orders', status ? { status } : undefined)
|
||||
}
|
||||
|
||||
export function assignGoodsOrder(orderId: number, riderId: number) {
|
||||
return apiPost<DeliveryOrder>('/api/admin/delivery-orders/assign-goods-order', { orderId, riderId })
|
||||
}
|
||||
|
||||
export function assignExpressOrder(orderId: number, riderId: number) {
|
||||
return apiPost<DeliveryOrder>('/api/admin/delivery-orders/assign-express-order', { orderId, riderId })
|
||||
}
|
||||
|
||||
export function assignGroupBuyOrder(orderId: number, riderId: number) {
|
||||
return apiPost<DeliveryOrder>('/api/admin/delivery-orders/assign-group-buy-order', { orderId, riderId })
|
||||
}
|
||||
26
admin-web/src/api/groupBuyApi.ts
Normal file
26
admin-web/src/api/groupBuyApi.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { GroupBuy } from '../types'
|
||||
import { apiGet, apiPost, apiPut } from './http'
|
||||
|
||||
export function listGroupBuys() {
|
||||
return apiGet<GroupBuy[]>('/api/admin/group-buys')
|
||||
}
|
||||
|
||||
export function createGroupBuy(data: Partial<GroupBuy>) {
|
||||
return apiPost<GroupBuy>('/api/admin/group-buys', data)
|
||||
}
|
||||
|
||||
export function updateGroupBuy(id: number, data: Partial<GroupBuy>) {
|
||||
return apiPut<GroupBuy>(`/api/admin/group-buys/${id}`, data)
|
||||
}
|
||||
|
||||
export function startGroupBuy(id: number) {
|
||||
return apiPut<GroupBuy>(`/api/admin/group-buys/${id}/start`)
|
||||
}
|
||||
|
||||
export function closeGroupBuy(id: number) {
|
||||
return apiPut<GroupBuy>(`/api/admin/group-buys/${id}/close`)
|
||||
}
|
||||
|
||||
export function completeGroupBuy(id: number) {
|
||||
return apiPut<GroupBuy>(`/api/admin/group-buys/${id}/complete`)
|
||||
}
|
||||
46
admin-web/src/api/http.ts
Normal file
46
admin-web/src/api/http.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import axios, { AxiosHeaders } from 'axios'
|
||||
|
||||
export interface ApiEnvelope<T> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export const http = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || ''
|
||||
})
|
||||
|
||||
http.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('admin_token')
|
||||
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))
|
||||
}
|
||||
|
||||
export function apiDelete<T>(url: string) {
|
||||
return unwrap<T>(http.delete(url))
|
||||
}
|
||||
26
admin-web/src/api/noticeApi.ts
Normal file
26
admin-web/src/api/noticeApi.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Notice } from '../types'
|
||||
import { apiDelete, apiGet, apiPost, apiPut } from './http'
|
||||
|
||||
export function listNotices() {
|
||||
return apiGet<Notice[]>('/api/admin/notices')
|
||||
}
|
||||
|
||||
export function createNotice(data: Partial<Notice>) {
|
||||
return apiPost<Notice>('/api/admin/notices', data)
|
||||
}
|
||||
|
||||
export function updateNotice(id: number, data: Partial<Notice>) {
|
||||
return apiPut<Notice>(`/api/admin/notices/${id}`, data)
|
||||
}
|
||||
|
||||
export function publishNotice(id: number) {
|
||||
return apiPut<Notice>(`/api/admin/notices/${id}/publish`)
|
||||
}
|
||||
|
||||
export function offlineNotice(id: number) {
|
||||
return apiPut<Notice>(`/api/admin/notices/${id}/offline`)
|
||||
}
|
||||
|
||||
export function deleteNotice(id: number) {
|
||||
return apiDelete<void>(`/api/admin/notices/${id}`)
|
||||
}
|
||||
16
admin-web/src/api/orderApi.test.ts
Normal file
16
admin-web/src/api/orderApi.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { buildGoodsOrderQuery } from './orderApi'
|
||||
|
||||
describe('buildGoodsOrderQuery', () => {
|
||||
it('keeps status and date range filters stable', () => {
|
||||
expect(buildGoodsOrderQuery({
|
||||
status: 'PENDING_DELIVERY',
|
||||
startDate: '2026-07-01',
|
||||
endDate: '2026-07-07'
|
||||
})).toEqual({
|
||||
status: 'PENDING_DELIVERY',
|
||||
startDate: '2026-07-01',
|
||||
endDate: '2026-07-07'
|
||||
})
|
||||
})
|
||||
})
|
||||
56
admin-web/src/api/orderApi.ts
Normal file
56
admin-web/src/api/orderApi.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { ExpressOrder, GoodsOrder } from '../types'
|
||||
import { apiGet, apiPut } from './http'
|
||||
|
||||
export interface GoodsOrderQuery {
|
||||
status?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
}
|
||||
|
||||
export function buildGoodsOrderQuery(query: GoodsOrderQuery) {
|
||||
const params: Record<string, string> = {}
|
||||
if (query.status) params.status = query.status
|
||||
if (query.startDate) params.startDate = query.startDate
|
||||
if (query.endDate) params.endDate = query.endDate
|
||||
return params
|
||||
}
|
||||
|
||||
export function listGoodsOrders(query: GoodsOrderQuery = {}) {
|
||||
return apiGet<GoodsOrder[]>('/api/admin/goods-orders', buildGoodsOrderQuery(query))
|
||||
}
|
||||
|
||||
export function confirmGoodsOrder(id: number) {
|
||||
return apiPut<GoodsOrder>(`/api/admin/goods-orders/${id}/confirm`)
|
||||
}
|
||||
|
||||
export function markGoodsOrderPrepared(id: number) {
|
||||
return apiPut<GoodsOrder>(`/api/admin/goods-orders/${id}/prepared`)
|
||||
}
|
||||
|
||||
export function cancelGoodsOrder(id: number) {
|
||||
return apiPut<GoodsOrder>(`/api/mini/goods-orders/${id}/cancel`)
|
||||
}
|
||||
|
||||
export function markGoodsOrderPaid(id: number) {
|
||||
return apiPut<GoodsOrder>(`/api/admin/goods-orders/${id}/paid`)
|
||||
}
|
||||
|
||||
export function listExpressOrders(status?: string) {
|
||||
return apiGet<ExpressOrder[]>('/api/admin/express-orders', status ? { status } : undefined)
|
||||
}
|
||||
|
||||
export function confirmExpressOrder(id: number) {
|
||||
return apiPut<ExpressOrder>(`/api/admin/express-orders/${id}/confirm`)
|
||||
}
|
||||
|
||||
export function adjustExpressFee(id: number, feeCent: number, reason: string) {
|
||||
return apiPut<ExpressOrder>(`/api/admin/express-orders/${id}/adjust-fee`, { feeCent, reason })
|
||||
}
|
||||
|
||||
export function cancelExpressOrder(id: number, reason?: string) {
|
||||
return apiPut<ExpressOrder>(`/api/admin/express-orders/${id}/cancel`, { reason })
|
||||
}
|
||||
|
||||
export function updateExpressPayStatus(id: number, status: string) {
|
||||
return apiPut<ExpressOrder>(`/api/admin/express-orders/${id}/offline-pay-status`, { status })
|
||||
}
|
||||
38
admin-web/src/api/productApi.ts
Normal file
38
admin-web/src/api/productApi.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Product, ProductCategory, ProductSku } from '../types'
|
||||
import { apiGet, apiPost, apiPut } from './http'
|
||||
|
||||
export function listProducts() {
|
||||
return apiGet<Product[]>('/api/admin/products')
|
||||
}
|
||||
|
||||
export function createProduct(data: Partial<Product>) {
|
||||
return apiPost<Product>('/api/admin/products', data)
|
||||
}
|
||||
|
||||
export function updateProduct(id: number, data: Partial<Product>) {
|
||||
return apiPut<Product>(`/api/admin/products/${id}`, data)
|
||||
}
|
||||
|
||||
export function onShelfProduct(id: number) {
|
||||
return apiPut<Product>(`/api/admin/products/${id}/on-shelf`)
|
||||
}
|
||||
|
||||
export function offShelfProduct(id: number) {
|
||||
return apiPut<Product>(`/api/admin/products/${id}/off-shelf`)
|
||||
}
|
||||
|
||||
export function listProductCategories() {
|
||||
return apiGet<ProductCategory[]>('/api/admin/product-categories')
|
||||
}
|
||||
|
||||
export function createProductCategory(data: Partial<ProductCategory>) {
|
||||
return apiPost<ProductCategory>('/api/admin/product-categories', data)
|
||||
}
|
||||
|
||||
export function addSku(productId: number, data: Partial<ProductSku>) {
|
||||
return apiPost<ProductSku>(`/api/admin/products/${productId}/skus`, data)
|
||||
}
|
||||
|
||||
export function updateSku(productId: number, skuId: number, data: Partial<ProductSku>) {
|
||||
return apiPut<ProductSku>(`/api/admin/products/${productId}/skus/${skuId}`, data)
|
||||
}
|
||||
18
admin-web/src/api/secondGoodsApi.ts
Normal file
18
admin-web/src/api/secondGoodsApi.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { SecondGoods } from '../types'
|
||||
import { apiGet, apiPut } from './http'
|
||||
|
||||
export function listSecondGoods(status?: string) {
|
||||
return apiGet<SecondGoods[]>('/api/admin/second-goods', status ? { status } : undefined)
|
||||
}
|
||||
|
||||
export function approveSecondGoods(id: number) {
|
||||
return apiPut<SecondGoods>(`/api/admin/second-goods/${id}/approve`)
|
||||
}
|
||||
|
||||
export function rejectSecondGoods(id: number, reason: string) {
|
||||
return apiPut<SecondGoods>(`/api/admin/second-goods/${id}/reject`, { reason })
|
||||
}
|
||||
|
||||
export function offShelfSecondGoods(id: number) {
|
||||
return apiPut<SecondGoods>(`/api/admin/second-goods/${id}/off-shelf`)
|
||||
}
|
||||
Reference in New Issue
Block a user