3
0
Fork 0
web-store-retail-h5/pages/bonus/index.vue

457 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="bonus-detail-page">
<view class="header-section">
<!-- 今日实发合计 -->
<view class="summary-bar">
<text class="summary-text"
>今日实发合计:
<text class="summary-amount">{{
formatAmount(todayTotal)
}}</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" @scrolltolower="loadMore">
<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.date }}</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">{{
formatAmount(dailyBonus[fieldKey])
}}</text>
</view>
</view>
<view class="card-footer">
<text class="subtotal-label">小计(¥)</text>
<text class="subtotal-value">{{
formatAmount(dailyBonus.subtotal)
}}</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'
export default {
data() {
return {
todayTotal: 0,
startDate: '',
endDate: '',
showStartDatePicker: false,
showEndDatePicker: false,
startDateValue: Number(new Date()),
endDateValue: Number(new Date()),
bonusList: [],
loading: false,
hasMore: true,
page: {
pageNum: 1,
pageSize: 10,
},
BONUS_FIELD_MAP: {
levelGapIncome: '级差收益',
peerIncome: '平级收益',
regionalIncome: '区域收益',
welfareLevelGapIncome: '福利级差收益',
welfareDividendIncome: '福利分红收益',
repeatConsumptionIncome: '重消收益',
},
}
},
onLoad() {
this.setDefaultDateRange()
this.handleSearch()
},
methods: {
formatAmount(amount) {
if (typeof amount !== 'number') {
return '0.00'
}
return amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
},
setDefaultDateRange() {
const end = new Date()
const start = new Date()
start.setDate(end.getDate() - 7) // 默认查询最近7天
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
}
this.page.pageNum = 1
this.bonusList = []
this.hasMore = true
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
},
loadMore() {
if (this.loading || !this.hasMore) {
return
}
this.page.pageNum++
this.fetchBonusData()
},
// 模拟数据获取
async fetchBonusData() {
if (this.loading) return
this.loading = true
// 模拟API调用延时
await new Promise(resolve => setTimeout(resolve, 800))
// TODO: 当后端API就绪时替换下面的模拟数据逻辑
// 实际调用:
// try {
// const params = { startDate: this.startDate, endDate: this.endDate, pageNum: this.page.pageNum, pageSize: this.page.pageSize };
// const res = await getBonusDetailsByDate(params);
// if (res.data.list.length < this.page.pageSize) {
// this.hasMore = false;
// }
// this.bonusList = [...this.bonusList, ...res.data.list];
// } catch (error) {
// console.error("Failed to fetch bonus data:", error);
// uni.showToast({ title: '数据加载失败', icon: 'none' });
// } finally {
// this.loading = false;
// }
// --- 模拟数据生成 ---
this.todayTotal = Math.random() * 1000
const mockData = []
// 模拟 "没有更多数据"
if (this.page.pageNum > 3) {
this.hasMore = false
this.loading = false
return
}
if (this.startDate && this.endDate) {
// 在模拟中,我们基于页码创建伪随机数据,而不是日期
for (let i = 0; i < this.page.pageSize; i++) {
const dayOffset = (this.page.pageNum - 1) * this.page.pageSize + i
const date = dayjs(this.endDate).subtract(dayOffset, 'day')
if (date.isBefore(dayjs(this.startDate))) {
this.hasMore = false
break
}
const bonusDetails = {
levelGapIncome: Math.random() * 100,
peerIncome: Math.random() * 200,
regionalIncome: 0,
welfareLevelGapIncome: 0,
welfareDividendIncome: 0,
repeatConsumptionIncome: Math.random() * 50,
}
const subtotal = Object.values(bonusDetails).reduce(
(sum, value) => sum + value,
0
)
mockData.push({
date: date.format('YYYY-MM-DD'),
...bonusDetails,
subtotal: subtotal,
})
}
}
if (mockData.length < this.page.pageSize) {
this.hasMore = false
}
this.bonusList = [...this.bonusList, ...mockData]
// --- 模拟数据结束 ---
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>