fix(api): 替换所有接口请求的域名为 guilixu-api.websoft.top

- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api
- 更新图片上传接口地址为新的 guilixu-api 域名
- 修改用户推广页面中邀请码链接和二维码接口的域名
- 更改注册页微信登录接口请求的域名为 guilixu-api
This commit is contained in:
2026-06-16 17:15:59 +08:00
commit f3886664f7
617 changed files with 77059 additions and 0 deletions

View File

@@ -0,0 +1,340 @@
import React, { useState, useEffect } from 'react'
import { View, Text, Input, Image, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { Button, Uploader } from '@nutui/nutui-react-taro'
import { getShopOrder } from '@/api/shop/shopOrder'
import type { ShopOrder } from '@/api/shop/shopOrder/model'
import Price from '@/components/common/Price'
definePageConfig({
navigationBarTitleText: '申请售后',
})
// 售后类型
const AFTER_SALE_TYPES = [
{ value: 'refund', label: '退款', desc: '仅退款,商品无需退回' },
{ value: 'return', label: '退货退款', desc: '退款并退货,商品需退回' },
]
// 退款原因选项
const REASON_OPTIONS = [
'商品损坏/有瑕疵',
'商品与描述不符',
'收到错误商品',
'商品少发/漏发',
'不想要了',
'其他原因',
]
const AfterSaleApplyPage: React.FC = () => {
const { orderId, type } = Taro.getCurrentInstance().router?.params || {}
const [order, setOrder] = useState<ShopOrder | null>(null)
const [loading, setLoading] = useState(false)
// 售后类型
const [afterSaleType, setAfterSaleType] = useState<'refund' | 'return'>(
(type as 'refund' | 'return') || 'refund'
)
// 退款原因
const [selectedReason, setSelectedReason] = useState('')
const [reasonDesc, setReasonDesc] = useState('')
// 图片上传
const [images, setImages] = useState<string[]>([])
// 选中的商品
const [selectedItems, setSelectedItems] = useState<number[]>([])
// 可退金额
const [refundAmount, setRefundAmount] = useState('0.00')
useEffect(() => {
if (orderId) {
loadOrder()
}
}, [orderId])
const loadOrder = async () => {
try {
const res = await getShopOrder(Number(orderId))
if (res) {
setOrder(res)
// 默认选中所有商品
const allItemIndices = (res.orderGoods || []).map((_, idx) => idx)
setSelectedItems(allItemIndices)
// 计算可退金额
const amount = calculateRefundAmount(res, allItemIndices)
setRefundAmount(amount)
}
} catch (err) {
console.error('加载订单失败', err)
Taro.showToast({ title: '加载失败', icon: 'none' })
}
}
// 计算可退金额
const calculateRefundAmount = (orderData: ShopOrder, selectedIdx: number[]): string => {
if (!orderData.orderGoods || selectedIdx.length === 0) return '0.00'
let total = 0
orderData.orderGoods.forEach((item, idx) => {
if (selectedIdx.includes(idx)) {
total += Number(item.price || 0) * (item.num || item.quantity || 1)
}
})
// 按比例计算优惠分摊
const totalPrice = Number(orderData.totalPrice || 0)
if (totalPrice > 0) {
const payPrice = Number(orderData.payPrice || 0)
const ratio = payPrice / totalPrice
total = total * ratio
}
return total.toFixed(2)
}
// 切换商品选中
const toggleItem = (idx: number) => {
const newSelected = selectedItems.includes(idx)
? selectedItems.filter(i => i !== idx)
: [...selectedItems, idx]
setSelectedItems(newSelected)
if (order) {
setRefundAmount(calculateRefundAmount(order, newSelected))
}
}
// 全选/取消全选
const toggleAll = () => {
if (!order?.orderGoods) return
if (selectedItems.length === order.orderGoods.length) {
setSelectedItems([])
setRefundAmount('0.00')
} else {
const allIdx = order.orderGoods.map((_, idx) => idx)
setSelectedItems(allIdx)
setRefundAmount(calculateRefundAmount(order, allIdx))
}
}
// 提交申请
const handleSubmit = async () => {
if (selectedItems.length === 0) {
Taro.showToast({ title: '请选择要售后的商品', icon: 'none' })
return
}
if (!selectedReason) {
Taro.showToast({ title: '请选择退款原因', icon: 'none' })
return
}
Taro.showModal({
title: '确认提交',
content: `确认提交${afterSaleType === 'refund' ? '退款' : '退货退款'}申请?`,
success: async (res) => {
if (res.confirm) {
await submitApply()
}
},
})
}
const submitApply = async () => {
if (!selectedReason) {
Taro.showToast({ title: '请选择退款原因', icon: 'none' })
return
}
setLoading(true)
try {
const res = await applyAfterSale({
orderId: orderId || '',
type: afterSaleType,
reason: selectedReason,
description: reasonDesc,
amount: parseFloat(refundAmount),
evidenceImages: images
})
if (res.success) {
Taro.showToast({ title: '申请提交成功', icon: 'success' })
setTimeout(() => {
Taro.redirectTo({ url: '/pages/order/after-sale-list' })
}, 1500)
} else {
Taro.showToast({ title: res.message || '提交失败', icon: 'none' })
}
} catch (err) {
console.error('申请售后失败:', err)
Taro.showToast({ title: '提交失败', icon: 'none' })
} finally {
setLoading(false)
}
}
if (!order) {
return (
<View className='min-h-screen bg-gray-50 flex items-center justify-center'>
<Text className='text-gray-400 text-sm'>...</Text>
</View>
)
}
return (
<View className='min-h-screen bg-gray-50 pb-20 flex flex-col'>
<ScrollView scrollY className='flex-1'>
{/* 售后类型选择 */}
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
<Text className='text-sm font-medium text-gray-800 mb-3 block'></Text>
<View className='flex gap-3'>
{AFTER_SALE_TYPES.map(item => (
<View
key={item.value}
className={`flex-1 p-3 rounded-lg border-2 text-center ${
afterSaleType === item.value
? 'border-green-500 bg-green-50'
: 'border-gray-200 bg-white'
}`}
onClick={() => setAfterSaleType(item.value as 'refund' | 'return')}
>
<Text className={`text-sm font-medium block ${
afterSaleType === item.value ? 'text-green-600' : 'text-gray-600'
}`}>
{item.label}
</Text>
<Text className='text-xs text-gray-400 mt-1 block'>{item.desc}</Text>
</View>
))}
</View>
</View>
{/* 商品选择 */}
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
<View className='flex justify-between items-center mb-3'>
<Text className='text-sm font-medium text-gray-800'></Text>
<Text
className='text-xs text-green-600'
onClick={toggleAll}
>
{selectedItems.length === order.orderGoods?.length ? '取消全选' : '全选'}
</Text>
</View>
{order.orderGoods?.map((item, idx) => (
<View
key={idx}
className='flex items-center gap-3 py-3 border-b border-gray-50'
style={idx === (order.orderGoods?.length ?? 0) - 1 ? { borderBottom: 'none' } : undefined}
onClick={() => toggleItem(idx)}
>
{/* 选中状态 */}
<View className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${
selectedItems.includes(idx)
? 'border-green-500 bg-green-500'
: 'border-gray-300'
}`}>
{selectedItems.includes(idx) && (
<Text className='text-white text-xs'></Text>
)}
</View>
{/* 商品图片 */}
<Image
className='w-16 h-16 rounded-lg'
src={item.image || ''}
mode='aspectFill'
/>
{/* 商品信息 */}
<View className='flex-1'>
<Text className='text-sm text-gray-700 line-clamp-2'>{item.goodsName}</Text>
<View className='flex justify-between items-center mt-1'>
<Price price={item.price || '0'} size='small' />
<Text className='text-xs text-gray-500'>x{item.num || item.quantity || 1}</Text>
</View>
</View>
</View>
))}
</View>
{/* 退款原因 */}
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
<Text className='text-sm font-medium text-gray-800 mb-3 block'>退</Text>
<View className='flex flex-wrap gap-2'>
{REASON_OPTIONS.map(reason => (
<View
key={reason}
className={`px-3 py-2 rounded-full text-sm ${
selectedReason === reason
? 'bg-green-50 text-green-600 border border-green-500'
: 'bg-gray-50 text-gray-600 border border-gray-200'
}`}
onClick={() => setSelectedReason(reason)}
>
<Text>{reason}</Text>
</View>
))}
</View>
{/* 补充说明 */}
<View className='mt-3'>
<Text className='text-xs text-gray-500 mb-1 block'></Text>
<View className='bg-gray-50 rounded-lg p-3'>
<Input
className='w-full text-sm'
placeholder='请详细描述您遇到的问题'
value={reasonDesc}
onInput={e => setReasonDesc(e.detail.value)}
style={{ minHeight: '80px' }}
/>
</View>
</View>
</View>
{/* 上传凭证 */}
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
<Text className='text-sm font-medium text-gray-800 mb-3 block'></Text>
<Uploader
value={images.map((url, idx) => ({ id: String(idx), url }))}
onChange={(files) => {
setImages(files.map(f => f.url || ''))
}}
maxCount={3}
/>
</View>
{/* 退款金额 */}
<View className='bg-white mx-3 mt-3 p-4 rounded-xl mb-4'>
<View className='flex justify-between items-center'>
<Text className='text-sm font-medium text-gray-800'>退</Text>
<Text className='text-lg font-bold text-red-500'>
{'\u00A5'}{refundAmount}
</Text>
</View>
{afterSaleType === 'return' && (
<Text className='text-xs text-gray-400 mt-2 block'>
退退
</Text>
)}
</View>
</ScrollView>
{/* 提交按钮 */}
<View className='bg-white border-t border-gray-100 p-3' style={{ paddingBottom: '20px' }}>
<Button
type='primary'
className='w-full rounded-full'
loading={loading}
style={{ backgroundColor: '#0e932e' }}
onClick={handleSubmit}
>
</Button>
</View>
</View>
)
}
export default AfterSaleApplyPage