320 lines
8.2 KiB
TypeScript
320 lines
8.2 KiB
TypeScript
import { request } from '../utils/request'
|
|
|
|
// 售后类型
|
|
export type AfterSaleType = 'refund' | 'return' | 'exchange' | 'repair'
|
|
|
|
// 售后状态
|
|
export type AfterSaleStatus =
|
|
| 'pending' // 待审核
|
|
| 'approved' // 已同意
|
|
| 'rejected' // 已拒绝
|
|
| 'processing' // 处理中
|
|
| 'completed' // 已完成
|
|
| 'cancelled' // 已取消
|
|
|
|
// 售后进度记录
|
|
export interface ProgressRecord {
|
|
id: string
|
|
time: string
|
|
status: string
|
|
description: string
|
|
operator?: string
|
|
remark?: string
|
|
images?: string[]
|
|
}
|
|
|
|
// 售后详情
|
|
export interface AfterSaleDetail {
|
|
id: string
|
|
orderId: string
|
|
orderNo: string
|
|
type: AfterSaleType
|
|
status: AfterSaleStatus
|
|
reason: string
|
|
description: string
|
|
amount: number
|
|
applyTime: string
|
|
processTime?: string
|
|
completeTime?: string
|
|
rejectReason?: string
|
|
contactPhone?: string
|
|
evidenceImages: string[]
|
|
progressRecords: ProgressRecord[]
|
|
}
|
|
|
|
// 售后申请参数
|
|
export interface AfterSaleApplyParams {
|
|
orderId: string
|
|
type: AfterSaleType
|
|
reason: string
|
|
description: string
|
|
amount: number
|
|
contactPhone?: string
|
|
evidenceImages?: string[]
|
|
goodsItems?: Array<{
|
|
goodsId: string
|
|
quantity: number
|
|
}>
|
|
}
|
|
|
|
// 售后列表查询参数
|
|
export interface AfterSaleListParams {
|
|
page?: number
|
|
pageSize?: number
|
|
status?: AfterSaleStatus
|
|
type?: AfterSaleType
|
|
startTime?: string
|
|
endTime?: string
|
|
}
|
|
|
|
// 售后列表响应
|
|
export interface AfterSaleListResponse {
|
|
success: boolean
|
|
data: {
|
|
list: AfterSaleDetail[]
|
|
total: number
|
|
page: number
|
|
pageSize: number
|
|
}
|
|
message?: string
|
|
}
|
|
|
|
// 售后详情响应
|
|
export interface AfterSaleDetailResponse {
|
|
success: boolean
|
|
data: AfterSaleDetail
|
|
message?: string
|
|
}
|
|
|
|
// 售后类型映射
|
|
export const AFTER_SALE_TYPE_MAP = {
|
|
'refund': '退款',
|
|
'return': '退货',
|
|
'exchange': '换货',
|
|
'repair': '维修'
|
|
}
|
|
|
|
// 售后状态映射
|
|
export const AFTER_SALE_STATUS_MAP = {
|
|
'pending': '待审核',
|
|
'approved': '已同意',
|
|
'rejected': '已拒绝',
|
|
'processing': '处理中',
|
|
'completed': '已完成',
|
|
'cancelled': '已取消'
|
|
}
|
|
|
|
// 状态颜色映射
|
|
export const STATUS_COLOR_MAP = {
|
|
'pending': '#faad14',
|
|
'approved': '#52c41a',
|
|
'rejected': '#ff4d4f',
|
|
'processing': '#1890ff',
|
|
'completed': '#52c41a',
|
|
'cancelled': '#999'
|
|
}
|
|
|
|
// 申请售后
|
|
export const applyAfterSale = async (params: AfterSaleApplyParams): Promise<AfterSaleDetailResponse> => {
|
|
try {
|
|
const response = await request<AfterSaleDetailResponse>({
|
|
url: '/api/after-sale/apply',
|
|
method: 'POST',
|
|
data: params
|
|
})
|
|
|
|
return response
|
|
} catch (error) {
|
|
console.error('申请售后失败:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// 查询售后详情
|
|
export const getAfterSaleDetail = async (params: {
|
|
orderId?: string
|
|
afterSaleId?: string
|
|
}): Promise<AfterSaleDetailResponse> => {
|
|
try {
|
|
const response = await request<AfterSaleDetailResponse>({
|
|
url: '/api/after-sale/detail',
|
|
method: 'GET',
|
|
data: params
|
|
})
|
|
|
|
return response
|
|
} catch (error) {
|
|
console.error('查询售后详情失败:', error)
|
|
|
|
// 返回模拟数据作为降级方案
|
|
return getMockAfterSaleDetail(params)
|
|
}
|
|
}
|
|
|
|
// 查询售后列表
|
|
export const getAfterSaleList = async (params: AfterSaleListParams): Promise<AfterSaleListResponse> => {
|
|
try {
|
|
const response = await request<AfterSaleListResponse>({
|
|
url: '/api/after-sale/list',
|
|
method: 'GET',
|
|
data: params
|
|
})
|
|
|
|
return response
|
|
} catch (error) {
|
|
console.error('查询售后列表失败:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// 撤销售后申请
|
|
export const cancelAfterSale = async (afterSaleId: string): Promise<{ success: boolean; message?: string }> => {
|
|
try {
|
|
const response = await request<{ success: boolean; message?: string }>({
|
|
url: '/api/after-sale/cancel',
|
|
method: 'POST',
|
|
data: { afterSaleId }
|
|
})
|
|
|
|
return response
|
|
} catch (error) {
|
|
console.error('撤销售后申请失败:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// 获取模拟售后详情数据
|
|
const getMockAfterSaleDetail = (params: {
|
|
orderId?: string
|
|
afterSaleId?: string
|
|
}): AfterSaleDetailResponse => {
|
|
const now = new Date()
|
|
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
|
const twoDaysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000)
|
|
|
|
const mockData: AfterSaleDetailResponse = {
|
|
success: true,
|
|
data: {
|
|
id: 'AS' + Date.now(),
|
|
orderId: params.orderId || '',
|
|
orderNo: 'ORD' + Date.now(),
|
|
type: 'refund',
|
|
status: 'processing',
|
|
reason: '商品质量问题',
|
|
description: '收到的商品有明显瑕疵,包装破损,希望申请退款处理',
|
|
amount: 9999,
|
|
applyTime: twoDaysAgo.toISOString(),
|
|
processTime: yesterday.toISOString(),
|
|
contactPhone: '138****5678',
|
|
evidenceImages: [
|
|
'https://via.placeholder.com/200x200?text=Evidence1',
|
|
'https://via.placeholder.com/200x200?text=Evidence2'
|
|
],
|
|
progressRecords: [
|
|
{
|
|
id: '1',
|
|
time: now.toISOString(),
|
|
status: '处理中',
|
|
description: '客服正在处理您的申请,请耐心等待',
|
|
operator: '客服小王',
|
|
remark: '预计1-2个工作日内完成处理'
|
|
},
|
|
{
|
|
id: '2',
|
|
time: new Date(now.getTime() - 4 * 60 * 60 * 1000).toISOString(),
|
|
status: '已审核',
|
|
description: '您的申请已通过审核,正在安排退款处理',
|
|
operator: '审核员张三'
|
|
},
|
|
{
|
|
id: '3',
|
|
time: yesterday.toISOString(),
|
|
status: '已受理',
|
|
description: '我们已收到您的申请,正在进行审核',
|
|
operator: '系统'
|
|
},
|
|
{
|
|
id: '4',
|
|
time: twoDaysAgo.toISOString(),
|
|
status: '已提交',
|
|
description: '您已成功提交售后申请',
|
|
operator: '用户'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
return mockData
|
|
}
|
|
|
|
// 格式化售后状态
|
|
export const formatAfterSaleStatus = (status: AfterSaleStatus): {
|
|
text: string
|
|
color: string
|
|
icon: string
|
|
} => {
|
|
const statusMap = {
|
|
'pending': { text: '待审核', color: '#faad14', icon: '⏳' },
|
|
'approved': { text: '已同意', color: '#52c41a', icon: '✅' },
|
|
'rejected': { text: '已拒绝', color: '#ff4d4f', icon: '❌' },
|
|
'processing': { text: '处理中', color: '#1890ff', icon: '🔄' },
|
|
'completed': { text: '已完成', color: '#52c41a', icon: '✅' },
|
|
'cancelled': { text: '已取消', color: '#999', icon: '⭕' }
|
|
}
|
|
|
|
return statusMap[status] || { text: status, color: '#666', icon: '📋' }
|
|
}
|
|
|
|
// 计算预计处理时间
|
|
export const calculateEstimatedTime = (
|
|
applyTime: string,
|
|
type: AfterSaleType,
|
|
status: AfterSaleStatus
|
|
): string => {
|
|
const applyDate = new Date(applyTime)
|
|
let estimatedDays = 3 // 默认3个工作日
|
|
|
|
// 根据售后类型调整预计时间
|
|
switch (type) {
|
|
case 'refund':
|
|
estimatedDays = 3 // 退款3个工作日
|
|
break
|
|
case 'return':
|
|
estimatedDays = 7 // 退货7个工作日
|
|
break
|
|
case 'exchange':
|
|
estimatedDays = 10 // 换货10个工作日
|
|
break
|
|
case 'repair':
|
|
estimatedDays = 15 // 维修15个工作日
|
|
break
|
|
}
|
|
|
|
// 根据当前状态调整
|
|
if (status === 'completed') {
|
|
return '已完成'
|
|
} else if (status === 'rejected' || status === 'cancelled') {
|
|
return '已结束'
|
|
}
|
|
|
|
const estimatedDate = new Date(applyDate.getTime() + estimatedDays * 24 * 60 * 60 * 1000)
|
|
return `预计${estimatedDate.getMonth() + 1}月${estimatedDate.getDate()}日前完成`
|
|
}
|
|
|
|
// 获取售后进度步骤
|
|
export const getAfterSaleSteps = (type: AfterSaleType, status: AfterSaleStatus) => {
|
|
const baseSteps = [
|
|
{ title: '提交申请', description: '用户提交售后申请' },
|
|
{ title: '审核中', description: '客服审核申请材料' },
|
|
{ title: '处理中', description: '正在处理您的申请' },
|
|
{ title: '完成', description: '售后处理完成' }
|
|
]
|
|
|
|
// 根据类型调整步骤
|
|
if (type === 'return' || type === 'exchange') {
|
|
baseSteps.splice(2, 0, { title: '等待收货', description: '等待用户寄回商品' })
|
|
baseSteps.splice(3, 0, { title: '确认收货', description: '商家确认收到退回商品' })
|
|
}
|
|
|
|
return baseSteps
|
|
} |