Initial commit

This commit is contained in:
sjk
2025-11-17 13:32:54 +08:00
commit e788eab6eb
1659 changed files with 171560 additions and 0 deletions

View File

@@ -0,0 +1,392 @@
// pages/refund/refund-apply/index.js
import Toast from 'tdesign-miniprogram/toast/index';
import { config } from '../../../config/index';
Page({
// 检查登录状态并导航到登录页
checkLoginAndNavigate() {
const token = wx.getStorageSync('token');
if (!token) {
wx.showModal({
title: '提示',
content: '请先登录后再进行操作',
showCancel: true,
cancelText: '取消',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
wx.navigateTo({
url: '/pages/login/index'
});
}
}
});
return false;
}
return true;
},
data: {
orderDetail: null,
refundAmount: '',
maxRefundAmount: '0.00',
selectedReason: '',
customReason: '',
description: '',
reasonOptions: [
{ value: 'quality', label: '商品质量问题' },
{ value: 'description', label: '商品与描述不符' },
{ value: 'logistics', label: '物流问题' },
{ value: 'service', label: '服务问题' },
{ value: 'personal', label: '个人原因' },
{ value: 'other', label: '其他原因' }
],
isReapply: false,
originalRefundId: null,
submitting: false,
loading: true,
error: null
},
computed: {
canSubmit() {
const { refundAmount, selectedReason, customReason } = this.data;
const hasValidAmount = refundAmount && parseFloat(refundAmount) > 0;
const hasValidReason = selectedReason && (selectedReason !== 'other' || customReason.trim());
return hasValidAmount && hasValidReason;
}
},
onLoad(options) {
console.log('[退款申请] 页面加载', options);
this.orderNo = options.orderNo;
this.setData({
isReapply: options.reapply === '1',
originalRefundId: options.refundId || null
});
if (!this.orderNo) {
this.setData({
error: '订单号不能为空',
loading: false
});
return;
}
this.loadOrderDetail();
},
onShow() {
// 重新计算canSubmit
this.updateCanSubmit();
},
// 加载订单详情
loadOrderDetail() {
this.setData({
loading: true,
error: null
});
if (!this.checkLoginAndNavigate()) {
this.setData({
loading: false
});
return;
}
const token = wx.getStorageSync('token');
console.log('[退款申请] 请求订单详情', { orderNo: this.orderNo });
wx.request({
url: `${config.apiBase}/orders/${this.orderNo}`,
method: 'GET',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
success: (res) => {
console.log('[退款申请] 订单详情API响应', res);
if (res.statusCode === 200 && res.data.code === 200) {
const orderDetail = this.processOrderDetail(res.data.data);
this.setData({
orderDetail: orderDetail,
maxRefundAmount: orderDetail.totalAmount,
refundAmount: orderDetail.totalAmount, // 默认全额退款
loading: false
});
this.updateCanSubmit();
} else {
console.error('[退款申请] 订单详情API错误', res.data);
this.setData({
error: res.data.message || '获取订单详情失败',
loading: false
});
}
},
fail: (error) => {
console.error('[退款申请] 网络错误', error);
this.setData({
error: '网络错误',
loading: false
});
}
});
},
// 处理订单详情数据
processOrderDetail(data) {
// 订单状态映射
const statusMap = {
1: '待付款',
2: '待发货',
3: '待发货',
4: '已发货',
5: '待收货',
6: '已完成',
7: '已取消',
8: '退货中',
9: '已退款'
};
// 使用正确的字段名根据后端JSON标签
const rawAmount = data.totalAmount || 0;
// 格式化金额(后端返回的是以分为单位的整数,需要转换为元)
const totalAmount = (rawAmount / 100).toFixed(2);
return {
orderId: data.orderId, // 保存订单ID用于提交退款申请
orderNo: data.orderNo,
statusText: statusMap[data.orderStatus] || '未知状态',
totalAmount: totalAmount
};
},
// 更新提交按钮状态
updateCanSubmit() {
const { refundAmount, selectedReason, customReason } = this.data;
const hasValidAmount = refundAmount && parseFloat(refundAmount) > 0;
const hasValidReason = selectedReason && (selectedReason !== 'other' || customReason.trim());
const canSubmit = hasValidAmount && hasValidReason;
this.setData({ canSubmit });
},
// 金额输入
onAmountInput(e) {
let value = e.detail.value;
// 只允许数字和小数点
value = value.replace(/[^\d.]/g, '');
// 只允许一个小数点
const dotIndex = value.indexOf('.');
if (dotIndex !== -1) {
value = value.substring(0, dotIndex + 1) + value.substring(dotIndex + 1).replace(/\./g, '');
// 限制小数点后两位
if (value.length > dotIndex + 3) {
value = value.substring(0, dotIndex + 3);
}
}
// 检查是否超过最大退款金额
const maxAmount = parseFloat(this.data.maxRefundAmount);
const inputAmount = parseFloat(value);
if (inputAmount > maxAmount) {
value = this.data.maxRefundAmount;
Toast({
context: this,
selector: '#t-toast',
message: '退款金额不能超过订单金额',
theme: 'warning',
});
}
this.setData({ refundAmount: value });
this.updateCanSubmit();
},
// 快速填充全额
onQuickFillAmount() {
this.setData({ refundAmount: this.data.maxRefundAmount });
this.updateCanSubmit();
},
// 选择退款原因
onReasonSelect(e) {
const value = e.currentTarget.dataset.value;
this.setData({
selectedReason: value,
customReason: value === 'other' ? this.data.customReason : ''
});
this.updateCanSubmit();
},
// 自定义原因输入
onCustomReasonInput(e) {
this.setData({ customReason: e.detail.value });
this.updateCanSubmit();
},
// 退款说明输入
onDescriptionInput(e) {
this.setData({ description: e.detail.value });
},
// 订单点击
onOrderTap() {
const { orderDetail } = this.data;
if (orderDetail && orderDetail.orderNo) {
wx.navigateTo({
url: `/pages/order/order-detail/index?orderNo=${orderDetail.orderNo}`
});
}
},
// 提交申请
onSubmit() {
if (!this.data.canSubmit || this.data.submitting) {
return;
}
// 验证数据
const { refundAmount, selectedReason, customReason, description } = this.data;
if (!refundAmount || parseFloat(refundAmount) <= 0) {
Toast({
context: this,
selector: '#t-toast',
message: '请输入有效的退款金额',
theme: 'warning',
});
return;
}
if (!selectedReason) {
Toast({
context: this,
selector: '#t-toast',
message: '请选择退款原因',
theme: 'warning',
});
return;
}
if (selectedReason === 'other' && !customReason.trim()) {
Toast({
context: this,
selector: '#t-toast',
message: '请填写具体的退款原因',
theme: 'warning',
});
return;
}
// 确认提交
const confirmText = this.data.isReapply ? '确定要重新提交退款申请吗?' : '确定要提交退款申请吗?';
wx.showModal({
title: '确认提交',
content: confirmText,
success: (res) => {
if (res.confirm) {
this.performSubmit();
}
}
});
},
// 执行提交
performSubmit() {
this.setData({ submitting: true });
const token = wx.getStorageSync('token');
const { refundAmount, selectedReason, customReason, description, isReapply, originalRefundId } = this.data;
// 构建退款原因
let reason = '';
if (selectedReason === 'other') {
reason = customReason.trim();
} else {
const reasonOption = this.data.reasonOptions.find(option => option.value === selectedReason);
reason = reasonOption ? reasonOption.label : '';
}
// 构建请求数据UserID由后端从JWT token中获取不需要前端传递
const requestData = {
order_id: parseInt(this.data.orderDetail.orderId), // 转换为数字类型
refund_amount: parseFloat(refundAmount), // 后端期望的是元为单位的浮点数
refund_reason: reason, // 使用正确的字段名
refund_type: 1 // 1:仅退款 2:退货退款,默认为仅退款
};
console.log('[退款申请] 提交数据', requestData);
// 选择API端点
let url = `${config.apiBase}/refunds`;
let method = 'POST';
if (isReapply && originalRefundId) {
url = `${config.apiBase}/refunds/${originalRefundId}/reapply`;
method = 'PUT';
}
wx.request({
url: url,
method: method,
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data: requestData,
success: (res) => {
console.log('[退款申请] 提交响应', res);
this.setData({ submitting: false });
if (res.statusCode === 200 && res.data.code === 200) {
Toast({
context: this,
selector: '#t-toast',
message: isReapply ? '重新申请成功' : '申请提交成功',
theme: 'success',
});
// 延迟跳转
setTimeout(() => {
wx.navigateBack({
delta: 1
});
}, 1500);
} else {
console.error('[退款申请] 提交失败', res.data);
Toast({
context: this,
selector: '#t-toast',
message: res.data.message || '提交失败',
theme: 'error',
});
}
},
fail: (error) => {
console.error('[退款申请] 网络错误', error);
this.setData({ submitting: false });
Toast({
context: this,
selector: '#t-toast',
message: '网络错误',
theme: 'error',
});
}
});
},
// 重试
onRetry() {
this.loadOrderDetail();
}
});

View File

@@ -0,0 +1,7 @@
{
"navigationBarTitleText": "申请退款",
"enablePullDownRefresh": false,
"usingComponents": {
"t-toast": "tdesign-miniprogram/toast/toast"
}
}

View File

@@ -0,0 +1,129 @@
<!--pages/refund/refund-apply/index.wxml-->
<view class="refund-apply-page">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">{{isReapply ? '重新申请退款' : '申请退款'}}</text>
</view>
<!-- 订单信息 -->
<view class="order-section" wx:if="{{orderDetail}}">
<view class="section-title">订单信息</view>
<view class="order-card" bindtap="onOrderTap">
<view class="order-header">
<text class="order-no">订单号:{{orderDetail.orderNo}}</text>
<text class="order-status">{{orderDetail.statusText}}</text>
</view>
<view class="order-amount">
<text class="amount-label">订单金额:</text>
<text class="amount-value">¥{{orderDetail.totalAmount}}</text>
</view>
</view>
</view>
<!-- 退款金额 -->
<view class="amount-section">
<view class="section-title">退款金额</view>
<view class="amount-input-wrapper">
<text class="currency-symbol">¥</text>
<input
class="amount-input"
type="digit"
placeholder="请输入退款金额"
value="{{refundAmount}}"
bindinput="onAmountInput"
maxlength="10"
/>
</view>
<view class="amount-tips">
<text class="tips-text">最多可退款:¥{{maxRefundAmount}}</text>
<text class="quick-fill" bindtap="onQuickFillAmount">全额退款</text>
</view>
</view>
<!-- 退款原因 -->
<view class="reason-section">
<view class="section-title">退款原因</view>
<view class="reason-options">
<view
class="reason-option {{selectedReason === item.value ? 'selected' : ''}}"
wx:for="{{reasonOptions}}"
wx:key="value"
bindtap="onReasonSelect"
data-value="{{item.value}}"
>
<text class="reason-text">{{item.label}}</text>
<view class="reason-check {{selectedReason === item.value ? 'checked' : ''}}">
<text class="check-icon" wx:if="{{selectedReason === item.value}}">✓</text>
</view>
</view>
</view>
<!-- 自定义原因输入 -->
<view class="custom-reason" wx:if="{{selectedReason === 'other'}}">
<textarea
class="reason-textarea"
placeholder="请详细说明退款原因"
value="{{customReason}}"
bindinput="onCustomReasonInput"
maxlength="200"
show-confirm-bar="{{false}}"
/>
<view class="char-count">{{customReason.length}}/200</view>
</view>
</view>
<!-- 退款说明 -->
<view class="description-section">
<view class="section-title">退款说明(选填)</view>
<textarea
class="description-textarea"
placeholder="请详细描述退款原因,有助于快速处理"
value="{{description}}"
bindinput="onDescriptionInput"
maxlength="500"
show-confirm-bar="{{false}}"
/>
<view class="char-count">{{description.length}}/500</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button
class="submit-btn {{canSubmit ? 'enabled' : 'disabled'}}"
bindtap="onSubmit"
disabled="{{!canSubmit || submitting}}"
>
{{submitting ? '提交中...' : (isReapply ? '重新提交' : '提交申请')}}
</button>
</view>
<!-- 温馨提示 -->
<view class="tips-section">
<view class="tips-title">温馨提示</view>
<view class="tips-content">
<text class="tip-item">• 退款申请提交后我们将在1-3个工作日内处理</text>
<text class="tip-item">• 退款金额将原路返回到您的支付账户</text>
<text class="tip-item">• 如有疑问请联系客服400-123-4567</text>
</view>
</view>
<!-- 加载状态 -->
<view class="loading-wrapper" wx:if="{{loading}}">
<view class="loading-content">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
</view>
<!-- 错误状态 -->
<view class="error-wrapper" wx:if="{{error}}">
<view class="error-content">
<text class="error-icon">⚠️</text>
<text class="error-text">{{error}}</text>
<button class="retry-btn" bindtap="onRetry">重试</button>
</view>
</view>
<!-- Toast组件 -->
<t-toast id="t-toast" />
</view>

View File

@@ -0,0 +1,351 @@
/* pages/refund/refund-apply/index.wxss */
.refund-apply-page {
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 120rpx;
}
/* 页面标题 */
.page-header {
background: #fff;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.page-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
/* 通用区块样式 */
.order-section,
.amount-section,
.reason-section,
.description-section {
background: #fff;
margin-top: 20rpx;
padding: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
}
/* 订单信息 */
.order-card {
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx;
border: 1rpx solid #e9ecef;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.order-no {
font-size: 28rpx;
color: #666;
}
.order-status {
font-size: 24rpx;
color: #007aff;
background: #e3f2fd;
padding: 8rpx 16rpx;
border-radius: 20rpx;
}
.order-amount {
display: flex;
align-items: center;
}
.amount-label {
font-size: 28rpx;
color: #666;
}
.amount-value {
font-size: 32rpx;
font-weight: 600;
color: #ff4757;
margin-left: 8rpx;
}
/* 退款金额 */
.amount-input-wrapper {
display: flex;
align-items: center;
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx;
border: 1rpx solid #e9ecef;
}
.currency-symbol {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-right: 16rpx;
}
.amount-input {
flex: 1;
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.amount-tips {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16rpx;
}
.tips-text {
font-size: 24rpx;
color: #999;
}
.quick-fill {
font-size: 24rpx;
color: #007aff;
padding: 8rpx 16rpx;
background: #e3f2fd;
border-radius: 20rpx;
}
/* 退款原因 */
.reason-options {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.reason-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
background: #f8f9fa;
border-radius: 12rpx;
border: 1rpx solid #e9ecef;
transition: all 0.3s ease;
}
.reason-option.selected {
background: #e3f2fd;
border-color: #007aff;
}
.reason-text {
font-size: 28rpx;
color: #333;
}
.reason-check {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
border: 2rpx solid #ddd;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.reason-check.checked {
background: #007aff;
border-color: #007aff;
}
.check-icon {
font-size: 24rpx;
color: #fff;
font-weight: 600;
}
/* 自定义原因 */
.custom-reason {
margin-top: 20rpx;
}
.reason-textarea {
width: 100%;
min-height: 120rpx;
background: #f8f9fa;
border-radius: 12rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
border: 1rpx solid #e9ecef;
box-sizing: border-box;
}
/* 退款说明 */
.description-textarea {
width: 100%;
min-height: 160rpx;
background: #f8f9fa;
border-radius: 12rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
border: 1rpx solid #e9ecef;
box-sizing: border-box;
}
.char-count {
text-align: right;
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
/* 提交按钮 */
.submit-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
z-index: 100;
}
.submit-btn {
width: 100%;
height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
transition: all 0.3s ease;
}
.submit-btn.enabled {
background: #007aff;
color: #fff;
}
.submit-btn.disabled {
background: #f5f5f5;
color: #ccc;
}
/* 温馨提示 */
.tips-section {
background: #fff;
margin-top: 20rpx;
padding: 30rpx;
}
.tips-title {
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
.tips-content {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.tip-item {
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
/* 加载状态 */
.loading-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #007aff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 28rpx;
color: #666;
}
/* 错误状态 */
.error-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.error-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
padding: 40rpx;
}
.error-icon {
font-size: 80rpx;
}
.error-text {
font-size: 28rpx;
color: #666;
text-align: center;
}
.retry-btn {
background: #007aff;
color: #fff;
border: none;
border-radius: 44rpx;
padding: 16rpx 32rpx;
font-size: 28rpx;
}

View File

@@ -0,0 +1,330 @@
// pages/refund/refund-detail/index.js
import Toast from 'tdesign-miniprogram/toast/index';
import { config } from '../../../config/index';
Page({
data: {
refundDetail: null,
loading: true,
error: null
},
onLoad(options) {
console.log('[退款详情] 页面加载', options);
this.refundId = options.id;
if (!this.refundId) {
this.setData({
error: '退款记录ID不能为空',
loading: false
});
return;
}
this.loadRefundDetail();
},
onShow() {
// 页面显示时刷新数据
if (this.refundId) {
this.loadRefundDetail();
}
},
onPullDownRefresh() {
this.loadRefundDetail();
},
// 加载退款详情
loadRefundDetail() {
this.setData({
loading: true,
error: null
});
let token = wx.getStorageSync('token');
if (!token) {
// 自动设置测试token
const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjozNywiZXhwIjoxNzYxNjE5NjAyLCJpYXQiOjE3NjE1MzMyMDJ9.xyZLQbwhYyUDiF9_UOCX39nVwYOHvvd6d4TqwFnT_yg';
wx.setStorageSync('token', testToken);
token = testToken;
console.log('[退款详情] 自动设置测试token');
}
console.log('[退款详情] 请求退款详情', { refundId: this.refundId });
wx.request({
url: `${config.apiBase}/refunds/${this.refundId}`,
method: 'GET',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
success: (res) => {
console.log('[退款详情] API响应', res);
if (res.statusCode === 200 && res.data.code === 200) {
const refundDetail = this.processRefundDetail(res.data.data);
this.setData({
refundDetail: refundDetail,
loading: false
});
wx.stopPullDownRefresh();
} else {
console.error('[退款详情] API错误', res.data);
this.setData({
error: res.data.message || '获取退款详情失败',
loading: false
});
}
},
fail: (error) => {
console.error('[退款详情] 网络错误', error);
this.setData({
error: '网络错误',
loading: false
});
}
});
},
// 处理退款详情数据
processRefundDetail(data) {
// 状态映射 - 后端返回的是数字状态
const statusMap = {
1: {
text: '待审核',
desc: '您的退款申请已提交,请耐心等待审核',
icon: '⏳'
},
2: {
text: '审核通过',
desc: '退款申请已通过,正在处理退款',
icon: '✓'
},
3: {
text: '审核拒绝',
desc: '退款申请被拒绝,如有疑问请联系客服',
icon: '✗'
},
4: {
text: '退款中',
desc: '退款正在处理中,请耐心等待',
icon: '⏳'
},
5: {
text: '退款成功',
desc: '退款已完成,请查看您的账户余额',
icon: '✓'
},
6: {
text: '退款失败',
desc: '退款处理失败,如有疑问请联系客服',
icon: '✗'
}
};
const statusInfo = statusMap[data.status] || {
text: '未知状态',
desc: '',
icon: '?'
};
// 格式化金额 - 添加安全检查,将分转换为元
const refundAmount = ((data.refund_amount || 0) / 100).toFixed(2);
// 订单金额从关联的订单对象中获取,将分转换为元
const orderAmount = ((data.order && data.order.total_amount ? data.order.total_amount : 0) / 100).toFixed(2);
// 格式化时间
const createdAt = this.formatTime(data.created_at);
const processedAt = data.processed_at ? this.formatTime(data.processed_at) : null;
const completedAt = data.completed_at ? this.formatTime(data.completed_at) : null;
// 退款方式
const refundMethodMap = {
'wechat': '微信支付',
'alipay': '支付宝',
'bank': '银行卡',
'balance': '账户余额'
};
// 订单状态
const orderStatusMap = {
1: '待付款',
2: '待发货',
3: '待发货',
4: '已发货',
5: '待收货',
6: '已完成',
7: '已取消',
8: '退货中',
9: '已退款'
};
// 处理退款日志
const logs = (data.logs || []).map(log => ({
id: log.id,
action: log.action,
remark: log.remark,
createdAt: this.formatTime(log.created_at)
})).reverse(); // 倒序显示,最新的在前面
// 生成操作按钮
const actions = this.generateActions(data.status);
return {
id: data.id,
orderNo: data.order_no,
refundAmount: refundAmount,
orderAmount: orderAmount,
reason: data.reason,
adminRemark: data.admin_remark,
status: data.status,
statusText: statusInfo.text,
statusDesc: statusInfo.desc,
statusIcon: statusInfo.icon,
createdAt: createdAt,
processedAt: processedAt,
completedAt: completedAt,
refundMethod: refundMethodMap[data.refund_method] || '未知方式',
orderStatusText: orderStatusMap[data.order_status] || '未知状态',
logs: logs,
actions: actions
};
},
// 生成操作按钮
generateActions(status) {
const actions = [];
switch (status) {
case 'pending':
actions.push({ type: 'cancel', text: '取消申请' });
break;
case 'rejected':
actions.push({ type: 'reapply', text: '重新申请' });
break;
}
return actions;
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return '';
const date = new Date(timeStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
},
// 订单点击
onOrderTap() {
const { refundDetail } = this.data;
if (refundDetail && refundDetail.orderNo) {
wx.navigateTo({
url: `/pages/order/order-detail/index?orderNo=${refundDetail.orderNo}`
});
}
},
// 操作按钮点击
onActionTap(e) {
const action = e.currentTarget.dataset.action;
console.log('[退款详情] 操作按钮点击', action);
switch (action) {
case 'cancel':
this.cancelRefund();
break;
case 'reapply':
this.reapplyRefund();
break;
}
},
// 取消退款申请
cancelRefund() {
wx.showModal({
title: '取消申请',
content: '确定要取消这个退款申请吗?',
success: (res) => {
if (res.confirm) {
this.performCancelRefund();
}
}
});
},
// 执行取消退款
performCancelRefund() {
const token = wx.getStorageSync('token');
wx.showLoading({ title: '处理中...' });
wx.request({
url: `${config.apiBase}/refunds/${this.refundId}/cancel`,
method: 'PUT',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
success: (res) => {
wx.hideLoading();
if (res.statusCode === 200 && res.data.code === 200) {
Toast({
context: this,
selector: '#t-toast',
message: '取消成功',
theme: 'success',
});
// 刷新详情
setTimeout(() => {
this.loadRefundDetail();
}, 1000);
} else {
Toast({
context: this,
selector: '#t-toast',
message: res.data.message || '取消失败',
theme: 'error',
});
}
},
fail: (error) => {
wx.hideLoading();
console.error('[退款详情] 取消退款失败', error);
Toast({
context: this,
selector: '#t-toast',
message: '网络错误',
theme: 'error',
});
}
});
},
// 重新申请退款
reapplyRefund() {
const { refundDetail } = this.data;
if (refundDetail) {
wx.navigateTo({
url: `/pages/refund/refund-apply/index?reapply=1&orderNo=${refundDetail.orderNo}&refundId=${this.refundId}`
});
}
},
// 重试
onRetry() {
this.loadRefundDetail();
}
});

View File

@@ -0,0 +1,8 @@
{
"navigationBarTitleText": "退款详情",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50,
"usingComponents": {
"t-toast": "tdesign-miniprogram/toast/toast"
}
}

View File

@@ -0,0 +1,115 @@
<!--pages/refund/refund-detail/index.wxml-->
<view class="refund-detail" wx:if="{{refundDetail}}">
<!-- 退款状态 -->
<view class="status-section">
<view class="status-icon {{refundDetail.status}}">
<text class="icon-text">{{refundDetail.statusIcon}}</text>
</view>
<view class="status-info">
<text class="status-text">{{refundDetail.statusText}}</text>
<text class="status-desc">{{refundDetail.statusDesc}}</text>
</view>
</view>
<!-- 退款信息 -->
<view class="info-section">
<view class="section-title">退款信息</view>
<view class="info-item">
<text class="info-label">退款金额</text>
<text class="info-value amount">¥{{refundDetail.refundAmount}}</text>
</view>
<view class="info-item">
<text class="info-label">申请时间</text>
<text class="info-value">{{refundDetail.createdAt}}</text>
</view>
<view class="info-item" wx:if="{{refundDetail.processedAt}}">
<text class="info-label">处理时间</text>
<text class="info-value">{{refundDetail.processedAt}}</text>
</view>
<view class="info-item" wx:if="{{refundDetail.completedAt}}">
<text class="info-label">完成时间</text>
<text class="info-value">{{refundDetail.completedAt}}</text>
</view>
<view class="info-item">
<text class="info-label">退款方式</text>
<text class="info-value">{{refundDetail.refundMethod}}</text>
</view>
</view>
<!-- 订单信息 -->
<view class="info-section">
<view class="section-title">相关订单</view>
<view class="order-card" bindtap="onOrderTap">
<view class="order-header">
<text class="order-no">订单号:{{refundDetail.orderNo}}</text>
<text class="order-status">{{refundDetail.orderStatusText}}</text>
</view>
<view class="order-amount">
<text class="amount-label">订单金额:</text>
<text class="amount-value">¥{{refundDetail.orderAmount}}</text>
</view>
</view>
</view>
<!-- 退款原因 -->
<view class="info-section" wx:if="{{refundDetail.reason}}">
<view class="section-title">退款原因</view>
<view class="reason-content">
<text>{{refundDetail.reason}}</text>
</view>
</view>
<!-- 处理备注 -->
<view class="info-section" wx:if="{{refundDetail.adminRemark}}">
<view class="section-title">处理备注</view>
<view class="remark-content">
<text>{{refundDetail.adminRemark}}</text>
</view>
</view>
<!-- 退款进度 -->
<view class="info-section" wx:if="{{refundDetail.logs && refundDetail.logs.length > 0}}">
<view class="section-title">退款进度</view>
<view class="progress-list">
<view
class="progress-item {{index === 0 ? 'current' : ''}}"
wx:for="{{refundDetail.logs}}"
wx:key="id"
>
<view class="progress-dot"></view>
<view class="progress-content">
<view class="progress-title">{{item.action}}</view>
<view class="progress-time">{{item.createdAt}}</view>
<view class="progress-remark" wx:if="{{item.remark}}">{{item.remark}}</view>
</view>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-section" wx:if="{{refundDetail.actions && refundDetail.actions.length > 0}}">
<view
class="action-btn {{action.type}}"
wx:for="{{refundDetail.actions}}"
wx:key="type"
data-action="{{action.type}}"
bindtap="onActionTap"
>
{{action.text}}
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading-state" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 错误状态 -->
<view class="error-state" wx:if="{{error}}">
<text>{{error}}</text>
<button class="retry-btn" bindtap="onRetry">重试</button>
</view>
<!-- Toast 组件 -->
<t-toast id="t-toast" />

View File

@@ -0,0 +1,299 @@
/* pages/refund/refund-detail/index.wxss */
.refund-detail {
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 120rpx;
}
/* 状态部分 */
.status-section {
background-color: #fff;
padding: 48rpx 32rpx;
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.status-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 32rpx;
}
.status-icon.pending {
background-color: #fff3cd;
}
.status-icon.approved {
background-color: #d1ecf1;
}
.status-icon.rejected {
background-color: #f8d7da;
}
.status-icon.completed {
background-color: #d4edda;
}
.icon-text {
font-size: 32rpx;
font-weight: 600;
}
.status-icon.pending .icon-text {
color: #856404;
}
.status-icon.approved .icon-text {
color: #0c5460;
}
.status-icon.rejected .icon-text {
color: #721c24;
}
.status-icon.completed .icon-text {
color: #155724;
}
.status-info {
flex: 1;
}
.status-text {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.status-desc {
font-size: 26rpx;
color: #666;
}
/* 信息部分 */
.info-section {
background-color: #fff;
margin-bottom: 24rpx;
padding: 32rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #eee;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-size: 28rpx;
color: #666;
}
.info-value {
font-size: 28rpx;
color: #333;
}
.info-value.amount {
color: #ff6b35;
font-weight: 600;
font-size: 32rpx;
}
/* 订单卡片 */
.order-card {
border: 1rpx solid #eee;
border-radius: 12rpx;
padding: 24rpx;
background-color: #fafafa;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.order-no {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.order-status {
font-size: 24rpx;
color: #666;
padding: 4rpx 12rpx;
background-color: #e9ecef;
border-radius: 6rpx;
}
.order-amount {
display: flex;
align-items: center;
}
.amount-label {
font-size: 26rpx;
color: #666;
margin-right: 16rpx;
}
.amount-value {
font-size: 28rpx;
color: #ff6b35;
font-weight: 600;
}
/* 原因和备注内容 */
.reason-content,
.remark-content {
padding: 24rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
font-size: 28rpx;
color: #333;
line-height: 1.6;
}
/* 进度列表 */
.progress-list {
position: relative;
}
.progress-item {
display: flex;
padding: 24rpx 0;
position: relative;
}
.progress-item:not(:last-child)::after {
content: '';
position: absolute;
left: 15rpx;
top: 60rpx;
bottom: -24rpx;
width: 2rpx;
background-color: #dee2e6;
}
.progress-item.current .progress-dot {
background-color: #ff6b35;
border-color: #ff6b35;
}
.progress-item.current::after {
background-color: #ff6b35;
}
.progress-dot {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
background-color: #dee2e6;
border: 4rpx solid #dee2e6;
margin-right: 24rpx;
flex-shrink: 0;
margin-top: 8rpx;
}
.progress-content {
flex: 1;
}
.progress-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.progress-time {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.progress-remark {
font-size: 26rpx;
color: #666;
line-height: 1.5;
}
/* 操作按钮 */
.action-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 24rpx 32rpx;
border-top: 1rpx solid #eee;
display: flex;
gap: 24rpx;
}
.action-btn {
flex: 1;
padding: 24rpx 0;
text-align: center;
border-radius: 12rpx;
font-size: 28rpx;
font-weight: 500;
}
.action-btn.primary {
background-color: #ff6b35;
color: #fff;
}
.action-btn.secondary {
background-color: #f8f9fa;
color: #666;
border: 1rpx solid #dee2e6;
}
/* 加载和错误状态 */
.loading-state,
.error-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 32rpx;
font-size: 28rpx;
color: #999;
}
.retry-btn {
margin-top: 32rpx;
padding: 16rpx 32rpx;
background-color: #ff6b35;
color: #fff;
border: none;
border-radius: 8rpx;
font-size: 26rpx;
}

View File

@@ -0,0 +1,328 @@
// pages/refund/refund-list/index.js
import Toast from 'tdesign-miniprogram/toast/index';
import { config } from '../../../config/index';
Page({
data: {
refundList: [],
currentStatus: '', // 当前筛选状态
loading: false,
page: 1,
pageSize: 10,
hasMore: true
},
onLoad(options) {
console.log('[退款记录] 页面加载', options);
this.loadRefundList();
},
onShow() {
// 页面显示时刷新数据
this.refreshData();
},
onPullDownRefresh() {
this.refreshData();
},
onReachBottom() {
if (this.data.hasMore && !this.data.loading) {
this.loadMoreData();
}
},
// 刷新数据
refreshData() {
this.setData({
page: 1,
hasMore: true,
refundList: []
});
this.loadRefundList();
},
// 加载更多数据
loadMoreData() {
this.setData({
page: this.data.page + 1
});
this.loadRefundList(false);
},
// 加载退款记录列表
loadRefundList(showLoading = true) {
if (showLoading) {
this.setData({ loading: true });
}
let token = wx.getStorageSync('token');
if (!token) {
// 自动设置测试token
const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjozNywiZXhwIjoxNzYxNjE5NjAyLCJpYXQiOjE3NjE1MzMyMDJ9.xyZLQbwhYyUDiF9_UOCX39nVwYOHvvd6d4TqwFnT_yg';
wx.setStorageSync('token', testToken);
token = testToken;
console.log('[退款记录] 自动设置测试token');
}
const params = {
page: this.data.page,
page_size: this.data.pageSize
};
if (this.data.currentStatus) {
params.status = this.data.currentStatus;
}
const queryString = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&');
console.log('[退款记录] 请求参数', params);
wx.request({
url: `${config.apiBase}/refunds/user?${queryString}`,
method: 'GET',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
success: (res) => {
console.log('[退款记录] API响应', res);
if (res.statusCode === 200 && res.data.code === 200) {
const newRefunds = res.data.data.list || [];
console.log('API返回的原始数据:', newRefunds);
const processedRefunds = newRefunds.map(item => this.processRefundItem(item));
console.log('处理后的数据:', processedRefunds);
this.setData({
refundList: this.data.page === 1 ? processedRefunds : [...this.data.refundList, ...processedRefunds],
hasMore: newRefunds.length === this.data.pageSize,
loading: false
});
if (showLoading) {
wx.stopPullDownRefresh();
}
} else {
console.error('[退款记录] API错误', res.data);
Toast({
context: this,
selector: '#t-toast',
message: res.data.message || '获取退款记录失败',
theme: 'error',
});
this.setData({ loading: false });
}
},
fail: (error) => {
console.error('[退款记录] 网络错误', error);
Toast({
context: this,
selector: '#t-toast',
message: '网络错误',
theme: 'error',
});
this.setData({ loading: false });
}
});
},
// 处理退款记录数据
processRefundItem(item) {
// 状态文本映射 - 后端返回的是数字状态
const statusMap = {
1: '待审核',
2: '审核通过',
3: '审核拒绝',
4: '退款中',
5: '退款成功',
6: '退款失败'
};
// 状态字符串映射用于CSS类名
const statusClassMap = {
1: 'pending',
2: 'approved',
3: 'rejected',
4: 'processing',
5: 'completed',
6: 'failed'
};
// 格式化金额 - 添加安全检查,将分转换为元
const refundAmount = ((item.refund_amount || 0) / 100).toFixed(2);
// 格式化时间
const createdAt = this.formatTime(item.created_at);
// 生成操作按钮
const actions = this.generateActions(statusClassMap[item.status]);
return {
id: item.id,
orderNo: item.order ? item.order.order_no : '',
refundAmount: refundAmount,
reason: item.refund_reason,
status: statusClassMap[item.status] || 'pending',
statusText: statusMap[item.status] || '未知状态',
createdAt: createdAt,
actions: actions
};
},
// 生成操作按钮
generateActions(status) {
const actions = [];
switch (status) {
case 'pending':
actions.push({ type: 'cancel', text: '取消申请' });
break;
case 'approved':
actions.push({ type: 'view', text: '查看详情' });
break;
case 'rejected':
actions.push({ type: 'view', text: '查看详情' });
actions.push({ type: 'reapply', text: '重新申请' });
break;
case 'completed':
actions.push({ type: 'view', text: '查看详情' });
break;
}
return actions;
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return '';
const date = new Date(timeStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
},
// 状态筛选
onStatusFilter(e) {
const status = e.currentTarget.dataset.status;
console.log('[退款记录] 状态筛选', status);
this.setData({
currentStatus: status,
page: 1,
hasMore: true,
refundList: []
});
this.loadRefundList();
},
// 退款记录项点击
onRefundItemTap(e) {
const refund = e.currentTarget.dataset.refund;
console.log('[退款记录] 点击退款记录', refund);
// 跳转到退款详情页面
wx.navigateTo({
url: `/pages/refund/refund-detail/index?id=${refund.id}`
});
},
// 操作按钮点击
onActionTap(e) {
e.stopPropagation(); // 阻止事件冒泡
const { refundId, action } = e.currentTarget.dataset;
console.log('[退款记录] 操作按钮点击', { refundId, action });
switch (action) {
case 'cancel':
this.cancelRefund(refundId);
break;
case 'view':
wx.navigateTo({
url: `/pages/refund/refund-detail/index?id=${refundId}`
});
break;
case 'reapply':
this.reapplyRefund(refundId);
break;
}
},
// 取消退款申请
cancelRefund(refundId) {
wx.showModal({
title: '取消申请',
content: '确定要取消这个退款申请吗?',
success: (res) => {
if (res.confirm) {
this.performCancelRefund(refundId);
}
}
});
},
// 执行取消退款
performCancelRefund(refundId) {
const token = wx.getStorageSync('token');
wx.showLoading({ title: '处理中...' });
wx.request({
url: `${config.apiBase}/refunds/${refundId}/cancel`,
method: 'PUT',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
success: (res) => {
wx.hideLoading();
if (res.statusCode === 200 && res.data.code === 200) {
Toast({
context: this,
selector: '#t-toast',
message: '取消成功',
theme: 'success',
});
// 刷新列表
this.refreshData();
} else {
Toast({
context: this,
selector: '#t-toast',
message: res.data.message || '取消失败',
theme: 'error',
});
}
},
fail: (error) => {
wx.hideLoading();
console.error('[退款记录] 取消退款失败', error);
Toast({
context: this,
selector: '#t-toast',
message: '网络错误',
theme: 'error',
});
}
});
},
// 重新申请退款
reapplyRefund(refundId) {
// 获取原退款记录信息,跳转到退款申请页面
wx.navigateTo({
url: `/pages/refund/refund-apply/index?reapply=1&refundId=${refundId}`
});
}
});

View File

@@ -0,0 +1,8 @@
{
"navigationBarTitleText": "退款记录",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50,
"usingComponents": {
"t-toast": "tdesign-miniprogram/toast/toast"
}
}

View File

@@ -0,0 +1,124 @@
<!--pages/refund/refund-list/index.wxml-->
<view class="refund-list">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">退款记录</text>
</view>
<!-- 状态筛选 -->
<view class="status-filter">
<view
class="filter-item {{currentStatus === '' ? 'active' : ''}}"
data-status=""
bindtap="onStatusFilter"
>
全部
</view>
<view
class="filter-item {{currentStatus === 'pending' ? 'active' : ''}}"
data-status="pending"
bindtap="onStatusFilter"
>
待审核
</view>
<view
class="filter-item {{currentStatus === 'approved' ? 'active' : ''}}"
data-status="approved"
bindtap="onStatusFilter"
>
审核通过
</view>
<view
class="filter-item {{currentStatus === 'rejected' ? 'active' : ''}}"
data-status="rejected"
bindtap="onStatusFilter"
>
审核拒绝
</view>
<view
class="filter-item {{currentStatus === 'processing' ? 'active' : ''}}"
data-status="processing"
bindtap="onStatusFilter"
>
退款中
</view>
<view
class="filter-item {{currentStatus === 'completed' ? 'active' : ''}}"
data-status="completed"
bindtap="onStatusFilter"
>
退款成功
</view>
<view
class="filter-item {{currentStatus === 'failed' ? 'active' : ''}}"
data-status="failed"
bindtap="onStatusFilter"
>
退款失败
</view>
</view>
<!-- 退款记录列表 -->
<view class="refund-items" wx:if="{{refundList.length > 0}}">
<view
class="refund-item"
wx:for="{{refundList}}"
wx:key="id"
bindtap="onRefundItemTap"
data-refund="{{item}}"
>
<!-- 退款状态和时间 -->
<view class="refund-header">
<view class="refund-status {{item.status}}">{{item.statusText}}</view>
<view class="refund-time">{{item.createdAt}}</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<text class="order-label">订单号:</text>
<text class="order-no">{{item.orderNo}}</text>
</view>
<!-- 退款金额 -->
<view class="refund-amount">
<text class="amount-label">退款金额:</text>
<text class="amount-value">¥{{item.refundAmount}}</text>
</view>
<!-- 退款原因 -->
<view class="refund-reason" wx:if="{{item.reason}}">
<text class="reason-label">退款原因:</text>
<text class="reason-text">{{item.reason}}</text>
</view>
<!-- 操作按钮 -->
<view class="refund-actions" wx:if="{{item.actions && item.actions.length > 0}}">
<view
class="action-btn {{action.type}}"
wx:for="{{item.actions}}"
wx:for-item="action"
wx:key="type"
data-refund-id="{{item.id}}"
data-action="{{action.type}}"
bindtap="onActionTap"
>
{{action.text}}
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{refundList.length === 0 && !loading}}">
<image class="empty-icon" src="/assets/images/empty-refund.png" mode="aspectFit"></image>
<text class="empty-text">暂无退款记录</text>
</view>
<!-- 加载状态 -->
<view class="loading-state" wx:if="{{loading}}">
<text>加载中...</text>
</view>
</view>
<!-- Toast 组件 -->
<t-toast id="t-toast" />

View File

@@ -0,0 +1,208 @@
/* pages/refund/refund-list/index.wxss */
.refund-list {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 页面标题 */
.page-header {
background-color: #fff;
padding: 32rpx;
border-bottom: 1rpx solid #eee;
}
.page-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
/* 状态筛选 */
.status-filter {
display: flex;
background-color: #fff;
padding: 24rpx 32rpx;
border-bottom: 1rpx solid #eee;
}
.filter-item {
flex: 1;
text-align: center;
padding: 16rpx 0;
font-size: 28rpx;
color: #666;
border-radius: 8rpx;
transition: all 0.3s ease;
}
.filter-item.active {
background-color: #ff6b35;
color: #fff;
}
/* 退款记录列表 */
.refund-items {
padding: 24rpx 32rpx;
}
.refund-item {
background-color: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
/* 退款头部 */
.refund-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.refund-status {
padding: 8rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
font-weight: 500;
}
.refund-status.pending {
background-color: #fff3cd;
color: #856404;
}
.refund-status.approved {
background-color: #d1ecf1;
color: #0c5460;
}
.refund-status.rejected {
background-color: #f8d7da;
color: #721c24;
}
.refund-status.completed {
background-color: #d4edda;
color: #155724;
}
.refund-time {
font-size: 24rpx;
color: #999;
}
/* 订单信息 */
.order-info {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.order-label {
font-size: 28rpx;
color: #666;
margin-right: 16rpx;
}
.order-no {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
/* 退款金额 */
.refund-amount {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.amount-label {
font-size: 28rpx;
color: #666;
margin-right: 16rpx;
}
.amount-value {
font-size: 32rpx;
color: #ff6b35;
font-weight: 600;
}
/* 退款原因 */
.refund-reason {
display: flex;
margin-bottom: 24rpx;
}
.reason-label {
font-size: 28rpx;
color: #666;
margin-right: 16rpx;
flex-shrink: 0;
}
.reason-text {
font-size: 28rpx;
color: #333;
line-height: 1.5;
}
/* 操作按钮 */
.refund-actions {
display: flex;
justify-content: flex-end;
gap: 16rpx;
}
.action-btn {
padding: 12rpx 24rpx;
border-radius: 8rpx;
font-size: 26rpx;
text-align: center;
min-width: 120rpx;
}
.action-btn.primary {
background-color: #ff6b35;
color: #fff;
}
.action-btn.secondary {
background-color: #f8f9fa;
color: #666;
border: 1rpx solid #dee2e6;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 32rpx;
}
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 32rpx;
opacity: 0.6;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 加载状态 */
.loading-state {
display: flex;
justify-content: center;
align-items: center;
padding: 80rpx 32rpx;
font-size: 28rpx;
color: #999;
}