3
0
Fork 0
web-store-retail-h5/components/distribution/performanceDistribution.vue

756 lines
18 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>
<view class="date-selector-container">
<view class="date-selector-full">
<view class="date-picker-full">
<picker
:range="selYearList"
:value="index"
range-key="label"
@change="changeYear"
>
<view class="date-picker-item-full">
<text class="date-value-full">{{ whatYear }}</text>
<u-icon name="arrow-down" color="#005BAC" size="20"></u-icon>
</view>
</picker>
</view>
<view class="date-picker-full">
<picker
:range="mounthList"
:value="index"
range-key="label"
@change="bindPickerChange"
>
<view class="date-picker-item-full">
<text class="date-value-full">{{ whatMounth }}</text>
<u-icon name="arrow-down" color="#005BAC" size="20"></u-icon>
</view>
</picker>
</view>
</view>
</view>
<!-- 业绩分布列表 -->
<view class="performance-list-container">
<!-- 特殊数据展示第一页第一条 -->
<view v-if="specialData" class="special-item">
<view class="special-content">
<view class="special-main-info">
<view class="special-user-info">
<view class="special-name">{{ specialData.memberName }}</view>
<view class="special-level">{{ specialData.memberLevel }}</view>
</view>
</view>
<view class="special-performance-compact">
<view class="special-performance-row">
<view class="special-performance-item-compact">
<text class="special-performance-label-compact">销售业绩</text>
<text class="special-performance-value-compact">{{
specialData.currentMonthPv
}}</text>
</view>
<view class="special-performance-item-compact">
<text class="special-performance-label-compact">销售盒数</text>
<text class="special-performance-value-compact"
>{{ specialData.currentMonthBoxNum }}盒</text
>
</view>
</view>
<view class="special-performance-row">
<view class="special-performance-item-compact">
<text class="special-performance-label-compact">复购业绩</text>
<text class="special-performance-value-compact">{{
specialData.repurchasePv
}}</text>
</view>
<view class="special-performance-item-compact">
<text class="special-performance-label-compact">复购盒数</text>
<text class="special-performance-value-compact"
>{{ specialData.repurchaseBox }}盒</text
>
</view>
</view>
</view>
</view>
</view>
<!-- 普通列表数据 -->
<view v-if="normalList.length > 0" class="normal-list">
<view class="list-content">
<view v-for="item in normalList" :key="item.id" class="list-item">
<!-- <view class="item-index" :class="getRankClass(index + 1)">{{
index + 1
}}</view> -->
<view class="item-content">
<view class="item-header">
<view class="item-name">{{ item.memberName }}</view>
<view class="item-level">{{ item.memberLevel }}</view>
</view>
<view class="item-performance">
<view class="performance-row">
<view class="performance-group">
<text class="performance-label">销售业绩</text>
<text class="performance-value">{{
item.currentMonthPv
}}</text>
</view>
<view class="performance-group">
<text class="performance-label">销售盒数</text>
<text class="performance-value"
>{{ item.currentMonthBoxNum }}盒</text
>
</view>
</view>
<view class="performance-row">
<view class="performance-group">
<text class="performance-label">复购业绩</text>
<text class="performance-value">{{
item.repurchasePv
}}</text>
</view>
<view class="performance-group">
<text class="performance-label">复购盒数</text>
<text class="performance-value"
>{{ item.repurchaseBox }}盒</text
>
</view>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon
mode="spinner"
color="#005bac"
size="28"
></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
</view>
</view>
<!-- 空状态 - 基于total为0判断 -->
<view v-if="!loading && total === 0" class="empty-state-container">
<view class="empty-state">
<view class="empty-icon">
<u-icon name="file-text" color="#d0d7de" size="60"></u-icon>
</view>
<text class="empty-title">暂无业绩数据</text>
<text class="empty-desc"
>{{ whatYear }}年{{ whatMounth }}月暂时没有业绩分布数据</text
>
<view class="empty-action" @tap="refresh">
<u-icon name="reload" color="#005bac" size="16"></u-icon>
<text class="refresh-text">点击刷新</text>
</view>
</view>
</view>
<!-- 无更多数据 -->
<view v-if="!hasMore && normalList.length > 0" class="no-more">
<text class="no-more-text">— 没有更多数据了 —</text>
</view>
</view>
</view>
</template>
<script>
import { getMonthAchieve } from '@/config/distribute.js'
export default {
data() {
return {
index: 0,
show: false,
achieveData: {},
whatMounth: '',
yjType: 1,
showType: false,
selMounthList: [],
yjTypeList: [],
yearShow: false,
whatYear: '',
currentYear: new Date().getFullYear(), // 当前真实年份
currentMonth: new Date().getMonth() + 1, // 当前真实月份
selYearList: [
{
value: 0,
label: new Date().getFullYear() - 1,
},
{
value: 1,
label: new Date().getFullYear(),
},
],
mounthList: [], // 改为动态生成
// 列表数据相关
specialData: null, // 特殊数据(第一页第一条)
normalList: [], // 普通列表数据
total: 0, // 总条数
currentPage: 1, // 当前页码
pageSize: 10, // 每页条数
loading: false, // 加载状态
hasMore: true, // 是否还有更多数据
}
},
created() {
this.initDate()
this.getMonthAchieve()
},
methods: {
changeYear(e) {
const selectedYear = this.selYearList[e.detail.value].label
this.whatYear = selectedYear
// 生成新的月份列表
this.generateMonthList(selectedYear)
// 如果从上一年切换到当年,且当前选中月份大于真实当前月,则调整为当前月
if (selectedYear === this.currentYear) {
const currentMonthNum = parseInt(this.whatMounth)
if (currentMonthNum > this.currentMonth) {
this.whatMounth = this.currentMonth.toString().padStart(2, '0')
}
}
this.refresh()
},
bindPickerChange(e) {
// 使用月份列表中的value值
this.whatMounth = this.mounthList[e.detail.value].value
this.refresh()
},
// 获取当前年/月
initDate() {
this.whatMounth = this.currentMonth.toString().padStart(2, '0')
this.whatYear = this.currentYear
// 初始化月份列表
this.generateMonthList(this.currentYear)
},
// 动态生成月份列表
generateMonthList(year) {
const maxMonth = year === this.currentYear ? this.currentMonth : 12
this.mounthList = []
for (let i = 1; i <= maxMonth; i++) {
this.mounthList.push({
value: i.toString().padStart(2, '0'),
label: i + '月',
})
}
},
// 获取月度业绩数据
async getMonthAchieve(isLoadMore = false) {
if (this.loading) return
this.loading = true
try {
const params = {
year: this.whatYear,
month: this.whatMounth,
pageNum: this.currentPage,
pageSize: this.pageSize,
}
const res = await getMonthAchieve(params)
if (res.code === 200) {
const { total, rows } = res
this.total = total
if (isLoadMore) {
// 加载更多:追加到现有列表
this.normalList = [...this.normalList, ...rows]
} else {
// 首次加载或刷新:处理特殊数据和普通数据
if (this.currentPage === 1 && rows.length > 0) {
// 第一页第一条数据作为特殊数据
this.specialData = rows[0]
this.normalList = rows.slice(1) // 剩余数据作为普通列表
} else {
this.specialData = null
this.normalList = rows
}
}
// 判断是否还有更多数据
this.hasMore = this.currentPage * this.pageSize < total
} else {
uni.showToast({
title: res.msg || '获取数据失败',
icon: 'none',
})
}
} catch (error) {
console.error('获取月度业绩数据失败:', error)
uni.showToast({
title: '网络错误,请重试',
icon: 'none',
})
} finally {
this.loading = false
}
},
// 刷新数据(供父组件调用)
refresh() {
this.currentPage = 1
this.hasMore = true
this.specialData = null
this.normalList = []
this.getMonthAchieve()
},
// 加载下一页(供父组件调用)
nextPage() {
if (!this.hasMore || this.loading) return
this.currentPage++
this.getMonthAchieve(true)
},
// 获取排名样式类
getRankClass(rank) {
if (rank === 1) return 'rank-first'
if (rank === 2) return 'rank-second'
if (rank === 3) return 'rank-third'
return ''
},
},
}
</script>
<style lang="scss" scoped>
::v-deep .uni-picker {
width: 100%;
}
// 全宽日期选择器样式
.date-selector-container {
padding: 10rpx 20rpx 10rpx;
}
.date-selector-full {
display: flex;
width: 100%;
gap: 16rpx;
}
.date-picker-full {
flex: 1;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
overflow: hidden;
}
.date-picker-item-full {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
transition: background-color 0.2s ease;
}
.date-picker-item-full:active {
background-color: #f8f9fa;
}
.date-value-full {
font-size: 28rpx;
font-weight: 600;
color: #005bac;
}
.title {
padding: 10rpx 0;
margin-top: 10rpx;
}
.width-auto {
background-color: #fff;
padding: 0 22rpx;
width: 690rpx;
margin: 0 auto;
padding-bottom: 20rpx;
}
.listrefor {
color: #ffffff;
border-radius: 10rpx;
padding: 32rpx 21rpx;
margin-top: 25rpx;
display: flex;
justify-content: space-between;
.flex_item:nth-child(1) {
width: 40%;
}
.flex_item:nth-child(2) {
width: 35%;
}
.flex_item:nth-child(3) {
width: 25%;
}
.text1 {
font-size: 28rpx;
}
.text2 {
font-size: 30rpx;
font-weight: bold;
margin-top: 30rpx;
}
}
.bg1 {
background: linear-gradient(180deg, #fc7c7c 0%, #f65757 100%);
}
.bg2 {
background: linear-gradient(180deg, #ff9354 0%, #ff7f36 100%);
}
.bg3 {
background: linear-gradient(180deg, #677af9 0%, #697bf2 100%);
}
/* 业绩分布列表样式 */
.performance-list-container {
padding: 10rpx 20rpx 0;
padding-bottom: 60rpx;
background-color: #f5f7fa;
min-height: 100vh;
}
/* 特殊数据样式 */
.special-item {
background: linear-gradient(135deg, #005bac 0%, #0074d9 100%);
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
position: relative;
overflow: hidden;
box-shadow: 0 6rpx 24rpx rgba(0, 91, 172, 0.2);
}
.special-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="3" fill="rgba(255,255,255,0.1)"/><circle cx="80" cy="30" r="2" fill="rgba(255,255,255,0.08)"/><circle cx="60" cy="80" r="2.5" fill="rgba(255,255,255,0.06)"/><circle cx="40" cy="60" r="1.5" fill="rgba(255,255,255,0.05)"/></svg>')
no-repeat;
background-size: cover;
pointer-events: none;
}
.special-user-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 12rpx;
border: 1rpx solid rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10rpx);
}
.special-content {
position: relative;
z-index: 1;
}
.special-main-info {
margin-bottom: 20rpx;
}
.special-name {
color: #ffffff;
font-size: 28rpx;
font-weight: 600;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
flex: 1;
}
.special-level {
color: #ffffff;
font-size: 20rpx;
font-weight: 500;
padding: 6rpx 12rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 16rpx;
backdrop-filter: blur(10rpx);
border: 1rpx solid rgba(255, 255, 255, 0.3);
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
}
.special-performance-compact {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.special-performance-row {
display: flex;
justify-content: space-between;
gap: 16rpx;
}
.special-performance-item-compact {
flex: 1;
background: rgba(255, 255, 255, 0.15);
border-radius: 12rpx;
padding: 12rpx 16rpx;
text-align: center;
backdrop-filter: blur(10rpx);
border: 1rpx solid rgba(255, 255, 255, 0.1);
}
.special-performance-label-compact {
color: rgba(255, 255, 255, 0.8);
font-size: 20rpx;
margin-bottom: 4rpx;
display: block;
}
.special-performance-value-compact {
color: #ffffff;
font-size: 22rpx;
font-weight: 600;
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
display: block;
}
/* 普通列表样式 */
.normal-list {
background: #ffffff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.list-content {
padding: 0 0 16rpx 0;
}
.list-item {
display: flex;
align-items: flex-start;
padding: 16rpx 24rpx;
border-bottom: 1rpx solid #f5f5f5;
position: relative;
transition: background-color 0.2s ease;
}
.list-item:last-child {
border-bottom: none;
}
.list-item:active {
background-color: #f8f9fa;
}
.item-index {
width: 48rpx;
height: 48rpx;
background: linear-gradient(135deg, #005bac 0%, #0074d9 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
font-weight: 600;
margin-right: 16rpx;
margin-top: 2rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 91, 172, 0.2);
}
.item-content {
flex: 1;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.item-name {
color: #333333;
font-size: 24rpx;
font-weight: 600;
}
.item-level {
color: #005bac;
font-size: 18rpx;
padding: 2rpx 8rpx;
background: rgba(0, 91, 172, 0.1);
border-radius: 8rpx;
border: 1rpx solid rgba(0, 91, 172, 0.2);
}
.item-performance {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.performance-row {
display: flex;
justify-content: space-between;
gap: 16rpx;
}
.performance-group {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.performance-label {
color: #666666;
font-size: 18rpx;
margin-bottom: 2rpx;
}
.performance-value {
color: #333333;
font-size: 20rpx;
font-weight: 600;
}
/* 状态样式 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx;
gap: 16rpx;
}
.loading-text {
color: #666666;
font-size: 24rpx;
}
.no-more {
text-align: center;
padding: 20rpx 0;
}
.no-more-text {
color: #999999;
font-size: 20rpx;
}
/* 空状态容器样式 */
.empty-state-container {
background: #ffffff;
border-radius: 16rpx;
margin: 0;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 40rpx 80rpx;
gap: 20rpx;
}
.empty-icon {
margin-bottom: 16rpx;
opacity: 0.6;
animation: float 3s ease-in-out infinite;
}
.empty-title {
color: #333333;
font-size: 28rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.empty-desc {
color: #666666;
font-size: 24rpx;
text-align: center;
line-height: 1.5;
margin-bottom: 16rpx;
}
.empty-action {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 12rpx 24rpx;
background: rgba(0, 91, 172, 0.1);
border-radius: 20rpx;
border: 1rpx solid rgba(0, 91, 172, 0.2);
transition: all 0.3s ease;
}
.empty-action:active {
background: rgba(0, 91, 172, 0.15);
transform: scale(0.98);
}
.refresh-text {
color: #005bac;
font-size: 22rpx;
font-weight: 500;
}
/* 浮动动画 */
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-8rpx);
}
}
/* 旧的空状态样式保留(以防其他地方使用) */
.empty-text {
color: #999999;
font-size: 24rpx;
}
/* 排名特殊颜色 */
.item-index.rank-first {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.3);
}
.item-index.rank-second {
background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
box-shadow: 0 2rpx 8rpx rgba(78, 205, 196, 0.3);
}
.item-index.rank-third {
background: linear-gradient(135deg, #45b7d1 0%, #96c93d 100%);
box-shadow: 0 2rpx 8rpx rgba(69, 183, 209, 0.3);
}
</style>