hqweb端
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
export const apiBase = '/api/v1';
|
||||
export const CLIENT_APP = 'HQ_WEB';
|
||||
|
||||
export type HqProfile = {
|
||||
id: string;
|
||||
phone: string;
|
||||
name: string;
|
||||
adminRole: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem('accessToken');
|
||||
}
|
||||
|
||||
export function saveAuth(data: { accessToken: string; refreshToken?: string }) {
|
||||
localStorage.setItem('accessToken', data.accessToken);
|
||||
if (data.refreshToken) localStorage.setItem('refreshToken', data.refreshToken);
|
||||
}
|
||||
|
||||
export function clearAuth() {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
}
|
||||
|
||||
export async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-App': CLIENT_APP,
|
||||
...(options.headers as Record<string, string>),
|
||||
};
|
||||
const token = getToken();
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
|
||||
const res = await fetch(`${apiBase}${path}`, { ...options, headers });
|
||||
const json = await res.json();
|
||||
if (json.code === 401) {
|
||||
clearAuth();
|
||||
window.location.href = '/login';
|
||||
throw new Error('未登录');
|
||||
}
|
||||
if (json.code !== 0) throw new Error(json.message || '请求失败');
|
||||
return json.data as T;
|
||||
}
|
||||
|
||||
export type Paginated<T> = {
|
||||
items: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
export type DashboardStats = {
|
||||
usersTotal: number;
|
||||
guestUsers: number;
|
||||
verifiedUsers: number;
|
||||
mergedUsers: number;
|
||||
ordersToday: number;
|
||||
storesTotal: number;
|
||||
partnersTotal: number;
|
||||
redeemToday: number;
|
||||
deliveriesTotal: number;
|
||||
ordersByStatus: Array<{ status: string; count: number }>;
|
||||
};
|
||||
|
||||
export type AdminUserRow = {
|
||||
id: string;
|
||||
userNo: string;
|
||||
deviceKey: string | null;
|
||||
phone: string | null;
|
||||
phoneVerifiedAt: string | null;
|
||||
mergedIntoUserId: string | null;
|
||||
nickname: string | null;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
orderCount: number;
|
||||
};
|
||||
|
||||
export type AdminOrderRow = {
|
||||
id: string;
|
||||
orderNo: string;
|
||||
status: string;
|
||||
deliveryType: string;
|
||||
payAmount: number;
|
||||
receiverName: string;
|
||||
receiverPhone: string;
|
||||
createdAt: string;
|
||||
user?: { id: string; userNo: string; phone: string | null; nickname: string | null };
|
||||
delivery?: { provider: string; trackingNo: string | null; providerOrderNo: string | null };
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
export const ORDER_STATUS_LABELS: Record<string, string> = {
|
||||
PENDING_PAY: '待付款',
|
||||
PENDING_SHIP: '待发货',
|
||||
OUT_WAREHOUSE: '已出库',
|
||||
SHIPPING: '配送中',
|
||||
PENDING_RECEIVE: '待收货',
|
||||
COMPLETED: '已完成',
|
||||
CANCELLED: '已取消',
|
||||
REFUNDING: '退款中',
|
||||
REFUNDED: '已退款',
|
||||
};
|
||||
|
||||
export const STORE_STATUS_LABELS: Record<string, string> = {
|
||||
OPEN: '营业中',
|
||||
PAUSED: '暂停',
|
||||
CLOSED: '已关闭',
|
||||
};
|
||||
|
||||
export const ACCOUNT_STATUS_LABELS: Record<string, string> = {
|
||||
ACTIVE: '正常',
|
||||
DISABLED: '停用',
|
||||
};
|
||||
|
||||
export const COUPON_STATUS_LABELS: Record<string, string> = {
|
||||
ACTIVE: '可用',
|
||||
USED_UP: '已用完',
|
||||
VOID: '已作废',
|
||||
};
|
||||
|
||||
export const CITY_STATUS_LABELS: Record<string, string> = {
|
||||
PENDING: '待开城',
|
||||
ACTIVE: '已开城',
|
||||
PAUSED: '已暂停',
|
||||
};
|
||||
|
||||
export const PARTNER_BILL_STATUS_LABELS: Record<string, string> = {
|
||||
DRAFT: '草稿',
|
||||
CONFIRMED: '已确认',
|
||||
PAID: '已结算',
|
||||
};
|
||||
|
||||
export const MEDIA_TYPE_LABELS: Record<string, string> = {
|
||||
IMAGE: '图片',
|
||||
VIDEO: '视频',
|
||||
};
|
||||
|
||||
export const LEDGER_TYPE_LABELS: Record<string, string> = {
|
||||
GRANT: '发放',
|
||||
REDEEM: '核销',
|
||||
REFUND_VOID: '退款作废',
|
||||
ADJUST: '调整',
|
||||
};
|
||||
|
||||
export function fmtTime(v?: string | null) {
|
||||
return v ? new Date(v).toLocaleString('zh-CN') : '—';
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { request, type Paginated } from './api';
|
||||
|
||||
export function useAdminList<T>(path: string, buildQuery: () => URLSearchParams, deps: unknown[]) {
|
||||
const [data, setData] = useState<Paginated<T> | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const qs = buildQuery();
|
||||
qs.set('page', String(page));
|
||||
qs.set('pageSize', String(pageSize));
|
||||
const res = await request<Paginated<T>>(`${path}?${qs}`);
|
||||
setData(res);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [path, page, pageSize, ...deps]);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
return { data, loading, page, pageSize, setPage, setPageSize, reload: load };
|
||||
}
|
||||
Reference in New Issue
Block a user