Add aftersales work order frontend pages
- Public scan-to-confirm page (/aftersales/:sn) with phone last-4 verification - Admin list + detail pages with state machine, QR generation, reassign, force-close - PublicLayout extracted from PublicQuery so both pages share logo + 备案 chrome - PublicQuery auto-redirects scanned zjbf-sh-* serials to the aftersales page - AdminLayout: new 售后工单 menu entry Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+118
-1
@@ -1,5 +1,16 @@
|
||||
import axios from 'axios';
|
||||
import type { ApiResponse, AuthResponse, User, EmployeeSerial, EmployeeSerialResponse } from '@/types';
|
||||
import type {
|
||||
User,
|
||||
EmployeeSerial,
|
||||
EmployeeSerialResponse,
|
||||
AftersalesOrder,
|
||||
AftersalesPublicView,
|
||||
AftersalesListFilter,
|
||||
AftersalesListResponse,
|
||||
CreateAftersalesRequest,
|
||||
UpdateAftersalesRequest,
|
||||
CustomerConfirmRequest,
|
||||
} from '@/types';
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
|
||||
|
||||
@@ -316,4 +327,110 @@ export const employeeSerialApi = {
|
||||
}
|
||||
throw new Error(response.data.error || '删除员工序列号失败');
|
||||
},
|
||||
};
|
||||
|
||||
export const aftersalesApi = {
|
||||
create: async (data: CreateAftersalesRequest) => {
|
||||
const response = await apiClient.post('/aftersales', data);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '创建工单失败');
|
||||
},
|
||||
|
||||
list: async (filter?: AftersalesListFilter) => {
|
||||
const params = new URLSearchParams();
|
||||
if (filter?.page && filter.page > 1) params.append('page', String(filter.page));
|
||||
if (filter?.limit && filter.limit !== 20) params.append('limit', String(filter.limit));
|
||||
if (filter?.search) params.append('search', filter.search);
|
||||
if (filter?.workOrderStatus) params.append('workOrderStatus', filter.workOrderStatus);
|
||||
if (filter?.serviceType) params.append('serviceType', filter.serviceType);
|
||||
if (filter?.technicianId) params.append('technicianId', String(filter.technicianId));
|
||||
if (filter?.mine) params.append('mine', 'true');
|
||||
|
||||
const url = params.toString() ? `/aftersales?${params.toString()}` : '/aftersales';
|
||||
const response = await apiClient.get(url);
|
||||
if (response.data) {
|
||||
return response.data as AftersalesListResponse;
|
||||
}
|
||||
throw new Error('获取售后工单列表失败');
|
||||
},
|
||||
|
||||
get: async (serialNumber: string) => {
|
||||
const response = await apiClient.get(`/aftersales/${encodeURIComponent(serialNumber)}`);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '查询工单失败');
|
||||
},
|
||||
|
||||
update: async (serialNumber: string, data: UpdateAftersalesRequest) => {
|
||||
const response = await apiClient.patch(`/aftersales/${encodeURIComponent(serialNumber)}`, data);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '更新工单失败');
|
||||
},
|
||||
|
||||
submit: async (serialNumber: string, resolutionNote: string) => {
|
||||
const response = await apiClient.post(`/aftersales/${encodeURIComponent(serialNumber)}/submit`, {
|
||||
resolutionNote,
|
||||
});
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '提交客户确认失败');
|
||||
},
|
||||
|
||||
generateQrCode: async (serialNumber: string, baseUrl?: string) => {
|
||||
const response = await apiClient.post(`/aftersales/${encodeURIComponent(serialNumber)}/qrcode`, {
|
||||
baseUrl,
|
||||
});
|
||||
if (response.data.qrCodeData) {
|
||||
return response.data as { qrCodeData: string; queryUrl: string };
|
||||
}
|
||||
throw new Error(response.data.error || '生成二维码失败');
|
||||
},
|
||||
|
||||
reassign: async (serialNumber: string, technicianId: number) => {
|
||||
const response = await apiClient.post(`/aftersales/${encodeURIComponent(serialNumber)}/reassign`, {
|
||||
technicianId,
|
||||
});
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '重新分配失败');
|
||||
},
|
||||
|
||||
forceClose: async (serialNumber: string) => {
|
||||
const response = await apiClient.post(`/aftersales/${encodeURIComponent(serialNumber)}/force-close`);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesOrder;
|
||||
}
|
||||
throw new Error(response.data.error || '强制关闭失败');
|
||||
},
|
||||
|
||||
delete: async (serialNumber: string) => {
|
||||
const response = await apiClient.delete(`/aftersales/${encodeURIComponent(serialNumber)}`);
|
||||
if (response.data.message) {
|
||||
return true;
|
||||
}
|
||||
throw new Error(response.data.error || '删除工单失败');
|
||||
},
|
||||
|
||||
publicQuery: async (serialNumber: string) => {
|
||||
const response = await apiClient.get(`/aftersales/${encodeURIComponent(serialNumber)}/query`);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesPublicView;
|
||||
}
|
||||
throw new Error(response.data.error || '查询工单失败');
|
||||
},
|
||||
|
||||
customerConfirm: async (serialNumber: string, data: CustomerConfirmRequest) => {
|
||||
const response = await apiClient.post(`/aftersales/${encodeURIComponent(serialNumber)}/confirm`, data);
|
||||
if (response.data.order) {
|
||||
return response.data.order as AftersalesPublicView;
|
||||
}
|
||||
throw new Error(response.data.error || '提交确认失败');
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user