forked from angelo/web-retail-h5
				
			
		
			
				
	
	
		
			382 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | |
|   <view class="bonus-detail-page">
 | |
|     <view class="header-section">
 | |
|       <view class="summary-bar">
 | |
|         <text class="summary-text"
 | |
|           >今日实发合计:
 | |
|           <text class="summary-amount">{{ realIncomeTotal }}</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>
 | |
|     </view>
 | |
| 
 | |
|     <!-- 奖金列表 -->
 | |
|     <scroll-view scroll-y class="bonus-list-scroll">
 | |
|       <view v-if="bonusList.length === 0 && !loading" class="empty-state"
 | |
|         >暂无数据</view
 | |
|       >
 | |
|       <template v-else>
 | |
|         <view
 | |
|           v-for="(dailyBonus, index) in bonusList"
 | |
|           :key="index"
 | |
|           class="daily-bonus-card"
 | |
|         >
 | |
|           <view class="card-header">
 | |
|             <view class="header-left">
 | |
|               <u-icon
 | |
|                 name="calendar"
 | |
|                 size="18"
 | |
|                 color="#333"
 | |
|                 class="header-icon"
 | |
|               ></u-icon>
 | |
|               <text class="header-title">奖金明细</text>
 | |
|             </view>
 | |
|             <text class="header-date">{{ dailyBonus.settleDate }}</text>
 | |
|           </view>
 | |
|           <view class="card-content">
 | |
|             <view
 | |
|               v-for="(fieldName, fieldKey) in BONUS_FIELD_MAP"
 | |
|               :key="fieldKey"
 | |
|               class="bonus-item"
 | |
|             >
 | |
|               <text class="item-label">{{ fieldName }}(¥)</text>
 | |
|               <text class="item-value">{{ dailyBonus[fieldKey] }}</text>
 | |
|             </view>
 | |
|           </view>
 | |
|           <view class="card-footer">
 | |
|             <text class="subtotal-label">小计(¥)</text>
 | |
|             <text class="subtotal-value">{{
 | |
|               dailyBonus.retailRealSubtotal
 | |
|             }}</text>
 | |
|           </view>
 | |
|         </view>
 | |
|       </template>
 | |
|       <view class="scroll-footer" v-if="bonusList.length > 0">
 | |
|         <text v-if="loading">加载中...</text>
 | |
|         <text v-else-if="!hasMore">没有更多数据了</text>
 | |
|       </view>
 | |
|     </scroll-view>
 | |
| 
 | |
|     <u-datetime-picker
 | |
|       :show="showStartDatePicker"
 | |
|       v-model="startDateValue"
 | |
|       mode="date"
 | |
|       @confirm="onStartDateConfirm"
 | |
|       @cancel="showStartDatePicker = false"
 | |
|       style="flex: 0"
 | |
|     ></u-datetime-picker>
 | |
|     <u-datetime-picker
 | |
|       :show="showEndDatePicker"
 | |
|       v-model="endDateValue"
 | |
|       mode="date"
 | |
|       @confirm="onEndDateConfirm"
 | |
|       @cancel="showEndDatePicker = false"
 | |
|       style="flex: 0"
 | |
|     ></u-datetime-picker>
 | |
|   </view>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import dayjs from 'dayjs'
 | |
| import { queryBonusTotal, queryBonusList } from '@/config/bonus'
 | |
| export default {
 | |
|   data() {
 | |
|     return {
 | |
|       realIncomeTotal: 0,
 | |
|       startDate: '',
 | |
|       endDate: '',
 | |
|       showStartDatePicker: false,
 | |
|       showEndDatePicker: false,
 | |
|       startDateValue: Number(new Date()),
 | |
|       endDateValue: Number(new Date()),
 | |
|       bonusList: [],
 | |
|       loading: false,
 | |
|       hasMore: true,
 | |
|       BONUS_FIELD_MAP: {
 | |
|         retailRangeIncome: '直推级差收益',
 | |
|         repurRangeIncome: '复购级差收益',
 | |
|         retailAreaIncome: '复购配送收益',
 | |
|       },
 | |
|     }
 | |
|   },
 | |
|   onLoad() {
 | |
|     this.setDefaultDateRange()
 | |
|     this.handleSearch()
 | |
|     this.getBonusTotal()
 | |
|   },
 | |
|   methods: {
 | |
|     setDefaultDateRange() {
 | |
|       const end = dayjs().subtract(1, 'day')
 | |
|       const start = dayjs().subtract(61, 'day')
 | |
|       this.startDate = start.format('YYYY-MM-DD')
 | |
|       this.endDate = end.format('YYYY-MM-DD')
 | |
| 
 | |
|       this.startDateValue = Number(start.toDate())
 | |
|       this.endDateValue = Number(end.toDate())
 | |
|     },
 | |
|     formatDate(date) {
 | |
|       return dayjs(date).format('YYYY-MM-DD')
 | |
|     },
 | |
|     goBack() {
 | |
|       uni.navigateBack()
 | |
|     },
 | |
|     handleSearch() {
 | |
|       // 校验日期
 | |
|       if (
 | |
|         this.startDate &&
 | |
|         this.endDate &&
 | |
|         new Date(this.startDate) > new Date(this.endDate)
 | |
|       ) {
 | |
|         uni.showToast({
 | |
|           title: '开始时间不能晚于结束时间',
 | |
|           icon: 'none',
 | |
|         })
 | |
|         return
 | |
|       }
 | |
|       // 校验区间不能超过31天
 | |
|       const start = dayjs(this.startDate)
 | |
|       const end = dayjs(this.endDate)
 | |
| 
 | |
|       this.bonusList = []
 | |
|       this.fetchBonusData()
 | |
|     },
 | |
|     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
 | |
|     },
 | |
|     getBonusTotal() {
 | |
|       queryBonusTotal().then(res => {
 | |
|         this.realIncomeTotal = res.rows[0]?.realIncomeTotal || '0.00'
 | |
|       })
 | |
|     },
 | |
|     async fetchBonusData() {
 | |
|       if (this.loading) return
 | |
|       this.loading = true
 | |
| 
 | |
|       try {
 | |
|         const params = {
 | |
|           startDate: this.startDate,
 | |
|           endDate: this.endDate,
 | |
|         }
 | |
|         const res = await queryBonusList(params)
 | |
|         console.log(res)
 | |
|         this.bonusList = res.rows || []
 | |
|       } catch (error) {
 | |
|         console.error('Failed to fetch bonus data:', error)
 | |
|         uni.showToast({ title: '数据加载失败', icon: 'none' })
 | |
|       } finally {
 | |
|         this.loading = false
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss">
 | |
| page {
 | |
|   background-color: #f7f7f8;
 | |
|   font-family:
 | |
|     -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
 | |
|     Arial, sans-serif;
 | |
|   height: 100%;
 | |
| }
 | |
| 
 | |
| .bonus-detail-page {
 | |
|   display: flex;
 | |
|   flex-direction: column;
 | |
|   height: 100%;
 | |
| }
 | |
| 
 | |
| .header-section {
 | |
|   flex-shrink: 0;
 | |
|   background-color: #ffffff;
 | |
| }
 | |
| 
 | |
| .summary-bar {
 | |
|   display: flex;
 | |
|   align-items: center;
 | |
|   padding: 10px 15px;
 | |
|   background-color: #e6f7ff;
 | |
|   border: 1px solid #91d5ff;
 | |
|   border-radius: 4px;
 | |
|   margin: 10px 15px;
 | |
|   .summary-text {
 | |
|     margin-left: 8px;
 | |
|     font-size: 14px;
 | |
|     color: #333;
 | |
|   }
 | |
|   .summary-amount {
 | |
|     font-weight: bold;
 | |
|     font-size: 16px;
 | |
|     margin-left: 5px;
 | |
|   }
 | |
| }
 | |
| 
 | |
| .date-filter {
 | |
|   display: flex;
 | |
|   align-items: center;
 | |
|   padding: 0 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;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| .bonus-list-scroll {
 | |
|   flex: 1;
 | |
|   height: 0; // for flexbox to correctly size the scroll view
 | |
| }
 | |
| 
 | |
| .empty-state {
 | |
|   text-align: center;
 | |
|   color: #999;
 | |
|   padding-top: 50px;
 | |
| }
 | |
| 
 | |
| .daily-bonus-card {
 | |
|   background-color: #ffffff;
 | |
|   border-radius: 12px;
 | |
|   margin: 12px 15px;
 | |
|   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
 | |
|   overflow: hidden;
 | |
|   border: 1px solid #f0f0f0;
 | |
|   transition: all 0.2s ease-in-out;
 | |
|   &:active {
 | |
|     transform: scale(0.98);
 | |
|     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 | |
|   }
 | |
| }
 | |
| 
 | |
| .card-header {
 | |
|   display: flex;
 | |
|   justify-content: space-between;
 | |
|   align-items: center;
 | |
|   padding: 15px;
 | |
|   border-bottom: 1px solid #f0f0f0;
 | |
| 
 | |
|   .header-left {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     .header-icon {
 | |
|       margin-right: 8px;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .header-title {
 | |
|     font-weight: 600;
 | |
|     font-size: 16px;
 | |
|     color: #333;
 | |
|   }
 | |
|   .header-date {
 | |
|     color: #666;
 | |
|     font-size: 14px;
 | |
|   }
 | |
| }
 | |
| 
 | |
| .card-content {
 | |
|   padding: 0;
 | |
|   .bonus-item {
 | |
|     display: flex;
 | |
|     justify-content: space-between;
 | |
|     padding: 12px 15px;
 | |
|     font-size: 14px;
 | |
|     border-bottom: 1px dashed #e5e5e5;
 | |
| 
 | |
|     &:last-child {
 | |
|       border-bottom: none;
 | |
|     }
 | |
| 
 | |
|     .item-label {
 | |
|       color: #555;
 | |
|     }
 | |
|     .item-value {
 | |
|       color: #111;
 | |
|       font-weight: 600;
 | |
|       font-family:
 | |
|         'DIN-Alternate', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| .card-footer {
 | |
|   display: flex;
 | |
|   justify-content: space-between;
 | |
|   align-items: center;
 | |
|   padding: 15px;
 | |
|   border-top: 1px solid #f0f0f0;
 | |
|   .subtotal-label {
 | |
|     font-weight: 600;
 | |
|     font-size: 15px;
 | |
|     color: #333;
 | |
|   }
 | |
|   .subtotal-value {
 | |
|     font-weight: bold;
 | |
|     color: #fa3534;
 | |
|     font-size: 18px;
 | |
|   }
 | |
| }
 | |
| 
 | |
| .scroll-footer {
 | |
|   text-align: center;
 | |
|   padding: 10px 0;
 | |
|   color: #999;
 | |
|   font-size: 14px;
 | |
| }
 | |
| </style>
 |