forked from angelo/web-retail-h5
				
			
		
			
	
	
		
			487 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			487 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Vue
		
	
	
	
|  | <template> | |||
|  |   <view class="refund-container"> | |||
|  |     <!-- 导航栏 --> | |||
|  |     <u-navbar | |||
|  |       title="退款列表" | |||
|  |       :border-bottom="false" | |||
|  |       :background="{ backgroundColor: '#005bac' }" | |||
|  |       title-color="#fff" | |||
|  |     /> | |||
|  |     <view class="tips"> | |||
|  |       <u-icon name="info-circle" color="#005bac" size="20"></u-icon> | |||
|  |       <text class="tips-text">如有疑问,请联系客服</text> | |||
|  |     </view> | |||
|  |     <!-- <view class="date-filter"> | |||
|  |       <view class="date-picker-container"> | |||
|  |         <view class="date-input-wrapper" @click="showStartDatePicker = true"> | |||
|  |           <text>{{ startDate || '开始时间' }}</text> | |||
|  |         </view> | |||
|  |         <text class="separator">至</text> | |||
|  |         <view class="date-input-wrapper" @click="showEndDatePicker = true"> | |||
|  |           <text>{{ endDate || '结束时间' }}</text> | |||
|  |         </view> | |||
|  |       </view> | |||
|  |       <button class="search-button" @click="handleSearch"> | |||
|  |         <u-icon name="search" color="#ffffff" size="20" /> | |||
|  |       </button> | |||
|  |     </view> --> | |||
|  |     <!-- 空状态 --> | |||
|  |     <u-empty | |||
|  |       mode="data" | |||
|  |       v-if="refundList.length === 0 && !loading" | |||
|  |       text="暂无退款记录" | |||
|  |     ></u-empty> | |||
|  |     <!-- 退款列表 --> | |||
|  |     <view class="refund-list"> | |||
|  |       <u-list @scrolltolower="loadMore" :show-scrollbar="false"> | |||
|  |         <u-list-item v-for="(item, index) in refundList" :key="index"> | |||
|  |           <view class="refund-item"> | |||
|  |             <view class="refund-header"> | |||
|  |               <view class="order-info"> | |||
|  |                 <text class="order-number" | |||
|  |                   >订单编号:{{ item.businessCode }}</text | |||
|  |                 > | |||
|  |                 <text class="member-code">会员编号:{{ item.memberCode }}</text> | |||
|  |               </view> | |||
|  |               <view | |||
|  |                 class="refund-status" | |||
|  |                 :class="getStatusClass(item.refundStatus)" | |||
|  |               > | |||
|  |                 {{ getStatusText(item.refundStatus) }} | |||
|  |               </view> | |||
|  |             </view> | |||
|  |             <view class="refund-content"> | |||
|  |               <view class="refund-amount"> | |||
|  |                 <text class="amount-label">退款金额</text> | |||
|  |                 <text class="amount-value" | |||
|  |                   >¥{{ stateFormat(item.refundMoney) }}</text | |||
|  |                 > | |||
|  |               </view> | |||
|  |               <view class="refund-time"> | |||
|  |                 <text class="time-label">退款时间</text> | |||
|  |                 <text class="time-value">{{ | |||
|  |                   formatDate(item.finishTime) | |||
|  |                 }}</text> | |||
|  |               </view> | |||
|  |             </view> | |||
|  |           </view> | |||
|  |         </u-list-item> | |||
|  |       </u-list> | |||
|  | 
 | |||
|  |       <!-- 加载更多 --> | |||
|  |       <u-loadmore :status="loadStatus" /> | |||
|  |     </view> | |||
|  | 
 | |||
|  |     <u-datetime-picker | |||
|  |       :show="showStartDatePicker" | |||
|  |       v-model="startDate" | |||
|  |       mode="date" | |||
|  |       @confirm="onStartDateConfirm" | |||
|  |       @cancel="showStartDatePicker = false" | |||
|  |       style="flex: 0" | |||
|  |     ></u-datetime-picker> | |||
|  |     <u-datetime-picker | |||
|  |       :show="showEndDatePicker" | |||
|  |       v-model="endDate" | |||
|  |       mode="date" | |||
|  |       @confirm="onEndDateConfirm" | |||
|  |       @cancel="showEndDatePicker = false" | |||
|  |       style="flex: 0" | |||
|  |     ></u-datetime-picker> | |||
|  |   </view> | |||
|  | </template> | |||
|  | 
 | |||
|  | <script> | |||
|  | import { getRefundList } from '@/config/mine' | |||
|  | import dayjs from '../../uni_modules/uview-ui/libs/util/dayjs.js' | |||
|  | 
 | |||
|  | export default { | |||
|  |   name: 'RefundDetail', | |||
|  |   data() { | |||
|  |     return { | |||
|  |       // 筛选条件
 | |||
|  |       startDate: '', | |||
|  |       endDate: '', | |||
|  |       showStartDatePicker: false, | |||
|  |       showEndDatePicker: false, | |||
|  |       maxDate: new Date().getTime(), | |||
|  | 
 | |||
|  |       // 列表数据
 | |||
|  |       refundList: [], | |||
|  |       loading: false, | |||
|  |       loadStatus: 'loadmore', | |||
|  | 
 | |||
|  |       // 分页参数
 | |||
|  |       currentPage: 1, | |||
|  |       pageSize: 10, | |||
|  |       hasMore: true, | |||
|  | 
 | |||
|  |       // 状态映射
 | |||
|  |       statusMap: { | |||
|  |         0: '退款申请中', | |||
|  |         1: '退款中', | |||
|  |         2: '已退款', | |||
|  |         3: '退款失败', | |||
|  |       }, | |||
|  |     } | |||
|  |   }, | |||
|  |   onLoad() { | |||
|  |     this.loadRefundList() | |||
|  |   }, | |||
|  |   onPullDownRefresh() { | |||
|  |     this.refreshList() | |||
|  |   }, | |||
|  |   methods: { | |||
|  |     // 加载退款列表
 | |||
|  |     async loadRefundList(isLoadMore = false) { | |||
|  |       if (this.loading) return | |||
|  | 
 | |||
|  |       this.loading = true | |||
|  |       this.loadStatus = 'loading' | |||
|  | 
 | |||
|  |       try { | |||
|  |         const params = { | |||
|  |           pageNum: this.currentPage, | |||
|  |           pageSize: this.pageSize, | |||
|  |           startDate: this.startDate, | |||
|  |           endDate: this.endDate, | |||
|  |         } | |||
|  | 
 | |||
|  |         const res = await getRefundList(params) | |||
|  | 
 | |||
|  |         if (res.code === 200) { | |||
|  |           const newList = res.rows || [] | |||
|  | 
 | |||
|  |           if (isLoadMore) { | |||
|  |             this.refundList = [...this.refundList, ...newList] | |||
|  |           } else { | |||
|  |             this.refundList = newList | |||
|  |           } | |||
|  | 
 | |||
|  |           // 判断是否还有更多数据
 | |||
|  |           this.hasMore = newList.length === this.pageSize | |||
|  |           this.loadStatus = this.hasMore ? 'loadmore' : 'nomore' | |||
|  | 
 | |||
|  |           if (this.hasMore) { | |||
|  |             this.currentPage++ | |||
|  |           } | |||
|  |         } else { | |||
|  |           this.$u.toast(res.message || '加载失败') | |||
|  |           this.loadStatus = 'loadmore' | |||
|  |         } | |||
|  |       } catch (error) { | |||
|  |         console.error('加载退款列表失败:', error) | |||
|  |         this.$u.toast('加载失败,请重试') | |||
|  |         this.loadStatus = 'loadmore' | |||
|  |       } finally { | |||
|  |         this.loading = false | |||
|  |         uni.stopPullDownRefresh() | |||
|  |       } | |||
|  |     }, | |||
|  |     stateFormat(val) { | |||
|  |       if (val) { | |||
|  |         return Number(val) | |||
|  |           .toFixed(2) | |||
|  |           .replace(/(\d)(?=(\d{3})+\.)/g, ($0, $1) => { | |||
|  |             return $1 + ',' | |||
|  |           }) | |||
|  |           .replace(/\.$/, '') | |||
|  |       } | |||
|  |     }, | |||
|  |     // 加载更多
 | |||
|  |     loadMore() { | |||
|  |       if (this.hasMore && !this.loading) { | |||
|  |         this.loadRefundList(true) | |||
|  |       } | |||
|  |     }, | |||
|  | 
 | |||
|  |     // 刷新列表
 | |||
|  |     refreshList() { | |||
|  |       this.currentPage = 1 | |||
|  |       this.hasMore = true | |||
|  |       this.loadRefundList() | |||
|  |     }, | |||
|  | 
 | |||
|  |     // 搜索
 | |||
|  |     handleSearch() { | |||
|  |       if (this.startDate && this.endDate && this.startDate > this.endDate) { | |||
|  |         this.$u.toast('开始日期不能大于结束日期') | |||
|  |         return | |||
|  |       } | |||
|  |       this.refreshList() | |||
|  |     }, | |||
|  | 
 | |||
|  |     // 重置
 | |||
|  |     handleReset() { | |||
|  |       this.startDate = '' | |||
|  |       this.endDate = '' | |||
|  |       this.refreshList() | |||
|  |     }, | |||
|  | 
 | |||
|  |     onStartDateConfirm(e) { | |||
|  |       this.startDate = this.formatDate(new Date(e.value)) | |||
|  |       this.startDateValue = e.value | |||
|  |       this.showStartDatePicker = false | |||
|  |     }, | |||
|  |     onEndDateConfirm(e) { | |||
|  |       this.endDate = this.formatDate(new Date(e.value)) | |||
|  |       this.endDateValue = e.value | |||
|  |       this.showEndDatePicker = false | |||
|  |     }, | |||
|  |     // 格式化显示日期
 | |||
|  |     formatDate(dateString) { | |||
|  |       if (!dateString) return '' | |||
|  |       return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss') | |||
|  |     }, | |||
|  |     // 获取状态文本
 | |||
|  |     getStatusText(status) { | |||
|  |       console.log(status) | |||
|  |       return this.statusMap[status] || '未知状态' | |||
|  |     }, | |||
|  |     // 获取状态样式类
 | |||
|  |     getStatusClass(status) { | |||
|  |       const classMap = { | |||
|  |         0: 'status-pending', | |||
|  |         1: 'status-failed', | |||
|  |         2: 'status-success', | |||
|  |         3: 'status-cancelled', | |||
|  |       } | |||
|  |       return classMap[status] || 'status-default' | |||
|  |     }, | |||
|  |   }, | |||
|  | } | |||
|  | </script> | |||
|  | 
 | |||
|  | <style lang="scss" scoped> | |||
|  | .refund-container { | |||
|  |   min-height: 100vh; | |||
|  |   background-color: #f5f5f5; | |||
|  | } | |||
|  | 
 | |||
|  | .tips { | |||
|  |   display: flex; | |||
|  |   align-items: center; | |||
|  |   justify-content: center; | |||
|  |   background-color: #f8f9fa; | |||
|  |   margin: 16rpx 30rpx; | |||
|  |   padding: 12rpx 20rpx; | |||
|  |   border-radius: 4rpx; | |||
|  |   border-left: 3rpx solid #005bac; | |||
|  | 
 | |||
|  |   .tips-text { | |||
|  |     font-size: 22rpx; | |||
|  |     color: #005bac; | |||
|  |     font-weight: 400; | |||
|  |     margin-left: 8rpx; | |||
|  |     line-height: 1.5; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | .filter-section { | |||
|  |   background-color: #fff; | |||
|  |   padding: 20rpx 30rpx; | |||
|  |   margin-bottom: 20rpx; | |||
|  | 
 | |||
|  |   .date-filter { | |||
|  |     display: flex; | |||
|  |     align-items: center; | |||
|  |     margin-bottom: 20rpx; | |||
|  | 
 | |||
|  |     .date-item { | |||
|  |       flex: 1; | |||
|  |       display: flex; | |||
|  |       align-items: center; | |||
|  |       justify-content: space-between; | |||
|  |       padding: 20rpx; | |||
|  |       border: 1px solid #e4e4e4; | |||
|  |       border-radius: 8rpx; | |||
|  |       background-color: #fafafa; | |||
|  | 
 | |||
|  |       .date-label { | |||
|  |         font-size: 28rpx; | |||
|  |         color: #666; | |||
|  |       } | |||
|  | 
 | |||
|  |       .date-value { | |||
|  |         font-size: 28rpx; | |||
|  |         color: #333; | |||
|  |         margin-left: 10rpx; | |||
|  |       } | |||
|  |     } | |||
|  | 
 | |||
|  |     .date-separator { | |||
|  |       padding: 0 20rpx; | |||
|  |       font-size: 28rpx; | |||
|  |       color: #666; | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   .filter-actions { | |||
|  |     display: flex; | |||
|  |     justify-content: center; | |||
|  |     gap: 20rpx; | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | .refund-list { | |||
|  |   padding: 10rpx 30rpx 0; | |||
|  | } | |||
|  | 
 | |||
|  | .refund-item { | |||
|  |   background-color: #fff; | |||
|  |   border-radius: 12rpx; | |||
|  |   padding: 30rpx; | |||
|  |   margin-bottom: 20rpx; | |||
|  |   box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); | |||
|  | 
 | |||
|  |   .refund-header { | |||
|  |     display: flex; | |||
|  |     justify-content: space-between; | |||
|  |     align-items: flex-start; | |||
|  |     margin-bottom: 20rpx; | |||
|  | 
 | |||
|  |     .order-info { | |||
|  |       flex: 1; | |||
|  | 
 | |||
|  |       .order-number { | |||
|  |         display: block; | |||
|  |         font-size: 28rpx; | |||
|  |         color: #333; | |||
|  |         font-weight: 500; | |||
|  |         margin-bottom: 8rpx; | |||
|  |       } | |||
|  | 
 | |||
|  |       .member-code { | |||
|  |         font-size: 24rpx; | |||
|  |         color: #666; | |||
|  |       } | |||
|  |     } | |||
|  | 
 | |||
|  |     .refund-status { | |||
|  |       padding: 8rpx 16rpx; | |||
|  |       border-radius: 20rpx; | |||
|  |       font-size: 24rpx; | |||
|  |       font-weight: 500; | |||
|  | 
 | |||
|  |       &.status-pending { | |||
|  |         background-color: #fff7e6; | |||
|  |         color: #fa8c16; | |||
|  |       } | |||
|  | 
 | |||
|  |       &.status-success { | |||
|  |         background-color: #f6ffed; | |||
|  |         color: #52c41a; | |||
|  |       } | |||
|  | 
 | |||
|  |       &.status-failed { | |||
|  |         background-color: #fff2f0; | |||
|  |         color: #ff4d4f; | |||
|  |       } | |||
|  | 
 | |||
|  |       &.status-cancelled { | |||
|  |         background-color: #f5f5f5; | |||
|  |         color: #8c8c8c; | |||
|  |       } | |||
|  | 
 | |||
|  |       &.status-default { | |||
|  |         background-color: #f5f5f5; | |||
|  |         color: #666; | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   .refund-content { | |||
|  |     display: flex; | |||
|  |     justify-content: space-between; | |||
|  |     align-items: center; | |||
|  | 
 | |||
|  |     .refund-amount, | |||
|  |     .refund-time { | |||
|  |       display: flex; | |||
|  |       flex-direction: column; | |||
|  |       align-items: flex-start; | |||
|  | 
 | |||
|  |       .amount-label, | |||
|  |       .time-label { | |||
|  |         font-size: 24rpx; | |||
|  |         color: #666; | |||
|  |         margin-bottom: 8rpx; | |||
|  |       } | |||
|  | 
 | |||
|  |       .amount-value { | |||
|  |         font-size: 32rpx; | |||
|  |         color: #005bac; | |||
|  |         font-weight: 600; | |||
|  |       } | |||
|  | 
 | |||
|  |       .time-value { | |||
|  |         font-size: 28rpx; | |||
|  |         color: #333; | |||
|  |       } | |||
|  |     } | |||
|  | 
 | |||
|  |     .refund-time { | |||
|  |       align-items: flex-end; | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | 
 | |||
|  | // 响应式适配
 | |||
|  | @media (max-width: 750rpx) { | |||
|  |   .filter-section { | |||
|  |     .date-filter { | |||
|  |       flex-direction: column; | |||
|  |       gap: 20rpx; | |||
|  | 
 | |||
|  |       .date-separator { | |||
|  |         display: none; | |||
|  |       } | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | .date-filter { | |||
|  |   display: flex; | |||
|  |   align-items: center; | |||
|  |   padding: 15px 10px; | |||
|  |   background-color: #ffffff; | |||
|  | 
 | |||
|  |   .date-picker-container { | |||
|  |     flex: 1; | |||
|  |     display: flex; | |||
|  |     align-items: center; | |||
|  |     background-color: #f2f2f2; | |||
|  |     border-radius: 8px; | |||
|  |     padding: 4px; | |||
|  |     .date-input-wrapper { | |||
|  |       flex: 1; | |||
|  |       text-align: center; | |||
|  |       padding: 6px 0; | |||
|  |       font-size: 14px; | |||
|  |       color: #333; | |||
|  |     } | |||
|  |     .separator { | |||
|  |       color: #999; | |||
|  |       margin: 0 5px; | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   .search-button { | |||
|  |     width: 44px; | |||
|  |     height: 36px; | |||
|  |     margin-left: 10px; | |||
|  |     background-color: #007aff; | |||
|  |     color: white; | |||
|  |     display: flex; | |||
|  |     align-items: center; | |||
|  |     justify-content: center; | |||
|  |     border-radius: 4px; | |||
|  |     padding: 0; | |||
|  |     line-height: 1; | |||
|  |     border: none; | |||
|  |     &:after { | |||
|  |       border: none; | |||
|  |     } | |||
|  |   } | |||
|  | } | |||
|  | </style> |