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

712 lines
17 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, index) 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
v-if="!loading && normalList.length === 0 && !specialData"
class="empty-state"
>
<view class="empty-icon">📊</view>
<text class="empty-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: '',
selYearList: [
{
value: 0,
label: new Date().getFullYear() - 1,
},
{
value: 1,
label: new Date().getFullYear(),
},
],
mounthList: [
{
value: '01',
label: 1 + '月',
},
{
value: '02',
label: 2 + '月',
},
{
value: '03',
label: 3 + '月',
},
{
value: '04',
label: 4 + '月',
},
{
value: '05',
label: 5 + '月',
},
{
value: '06',
label: 6 + '月',
},
{
value: '07',
label: 7 + '月',
},
{
value: '08',
label: 8 + '月',
},
{
value: '09',
label: 9 + '月',
},
{
value: '10',
label: 10 + '月',
},
{
value: '11',
label: 11 + '月',
},
{
value: '12',
label: 12 + '月',
},
],
// 列表数据相关
specialData: null, // 特殊数据(第一页第一条)
normalList: [], // 普通列表数据
total: 0, // 总条数
currentPage: 1, // 当前页码
pageSize: 10, // 每页条数
loading: false, // 加载状态
hasMore: true, // 是否还有更多数据
}
},
created() {
this.initDate()
this.getMonthAchieve()
},
methods: {
changeYear(e) {
this.whatYear = this.selYearList[e.detail.value].label
this.refresh()
},
bindPickerChange(e) {
this.whatMounth = e.detail.value
this.refresh()
},
// 获取当前年/月
initDate() {
const month = new Date().getMonth() + 1
this.whatMounth = month.toString().padStart(2, '0')
this.whatYear = new Date().getFullYear()
},
// 获取月度业绩数据
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: 20rpx;
}
.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: 20rpx;
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 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
gap: 20rpx;
}
.empty-icon {
font-size: 80rpx;
opacity: 0.5;
}
.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>