401 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			401 lines
		
	
	
		
			9.5 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"
 | 
						|
            >
 | 
						|
              <view class="bonus-item" v-if="dailyBonus[fieldKey]">
 | 
						|
                <text class="item-label">{{ fieldName }}(¥)</text>
 | 
						|
                <text class="item-value">{{ dailyBonus[fieldKey] }}</text>
 | 
						|
              </view>
 | 
						|
            </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>
 | 
						|
// 注意: 后端API需要支持按日期范围查询奖金明细
 | 
						|
// import { getBonusDetailsByDate, getTodayBonusTotal } from '@/config/bonus.js';
 | 
						|
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: '直推收益',
 | 
						|
        retailSameLevelIncome: '平级收益',
 | 
						|
        retailAreaIncome: '区域收益',
 | 
						|
        retailBenefitRangeIncome: '福利级差收益',
 | 
						|
        repurRangeIncome: '复购级差收益',
 | 
						|
        retailMonthRepurchaseIncome: '月复购级差收益',
 | 
						|
        coachIncome: '培育津贴',
 | 
						|
        retailBenefitIncomeTotal: '福利分红收益',
 | 
						|
        backPoints: '重消收益',
 | 
						|
      },
 | 
						|
    }
 | 
						|
  },
 | 
						|
  onLoad() {
 | 
						|
    this.setDefaultDateRange()
 | 
						|
    this.handleSearch()
 | 
						|
    this.getBonusTotal()
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    setDefaultDateRange() {
 | 
						|
      const end = new Date()
 | 
						|
      const start = new Date()
 | 
						|
      end.setDate(end.getDate() - 1)
 | 
						|
      start.setDate(start.getDate() - 15) // 默认查询最近15天
 | 
						|
 | 
						|
      this.startDate = this.formatDate(start)
 | 
						|
      this.endDate = this.formatDate(end)
 | 
						|
      this.startDateValue = Number(start)
 | 
						|
      this.endDateValue = Number(end)
 | 
						|
    },
 | 
						|
    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)
 | 
						|
      console.log(end.diff(start, 'day'), '...a?')
 | 
						|
      if (end.diff(start, 'day') > 30) {
 | 
						|
        uni.showToast({
 | 
						|
          title: '最多只能查询31天内的数据',
 | 
						|
          icon: 'none',
 | 
						|
        })
 | 
						|
        return
 | 
						|
      }
 | 
						|
      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>
 |