peixue-dev/peidu/uniapp/distributor-package/pages/distributor/team.vue

616 lines
14 KiB
Vue
Raw Permalink 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="team-page">
<!-- 团队概览 -->
<view class="overview-card gradient-bg">
<view class="card-title">我的团队</view>
<view class="stats-grid">
<view class="stat-item">
<text class="stat-value">{{ teamStats.memberCount }}</text>
<text class="stat-label">团队成员</text>
</view>
<view class="stat-item">
<text class="stat-value">¥{{ teamStats.totalSales }}</text>
<text class="stat-label">团队业绩</text>
</view>
<view class="stat-item">
<text class="stat-value">¥{{ teamStats.totalCommission }}</text>
<text class="stat-label">团队佣金</text>
</view>
</view>
</view>
<!-- 邀请新成员 -->
<view class="invite-section">
<view class="invite-card">
<view class="invite-content">
<text class="invite-title">邀请好友加入团队</text>
<text class="invite-desc">好友成功注册并推广您可获得二级佣金</text>
</view>
<button class="invite-btn" @click="inviteMember">
<text class="btn-icon"></text>
<text>立即邀请</text>
</button>
</view>
</view>
<!-- 团队成员列表 -->
<view class="member-section">
<view class="section-header">
<text class="section-title">团队成员</text>
<picker mode="selector" :range="sortOptions" @change="onSortChange">
<view class="sort-picker">
<text>{{ selectedSort }}</text>
<text class="arrow"></text>
</view>
</picker>
</view>
<view class="member-list">
<view class="member-item" v-for="member in memberList" :key="member.id" @click="viewMemberDetail(member)">
<image class="member-avatar" :src="member.avatar" mode="aspectFill"></image>
<view class="member-info">
<view class="member-name-row">
<text class="member-name">{{ member.name }}</text>
<text class="member-level" :class="'level-' + member.level">{{ getLevelText(member.level) }}</text>
</view>
<text class="member-time">加入时间:{{ member.joinTime }}</text>
<view class="member-stats">
<text class="stat-text">推广客户:{{ member.customerCount }}人</text>
<text class="stat-text">成交订单:{{ member.orderCount }}单</text>
</view>
</view>
<view class="member-performance">
<text class="performance-label">业绩</text>
<text class="performance-value">¥{{ member.totalSales }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="memberList.length === 0">
<text class="empty-icon"></text>
<text class="empty-text">暂无团队成员</text>
<text class="empty-hint">快去邀请好友加入吧</text>
</view>
</view>
<!-- 团队排行榜 -->
<view class="rank-section">
<view class="section-header">
<text class="section-title">本月排行榜</text>
</view>
<view class="rank-list">
<view class="rank-item" v-for="(item, index) in rankList" :key="item.id">
<view class="rank-number" :class="'rank-' + (index + 1)">
{{ index + 1 }}
</view>
<image class="rank-avatar" :src="item.avatar" mode="aspectFill"></image>
<view class="rank-info">
<text class="rank-name">{{ item.name }}</text>
<text class="rank-sales">业绩:¥{{ item.sales }}</text>
</view>
<view class="rank-badge" v-if="index < 3">
<text>{{ ['', '', ''][index] }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
teamStats: {
memberCount: 0,
totalSales: 0,
totalCommission: 0
},
selectedSort: '按加入时间',
sortOptions: ['按加入时间', '按业绩排序', '按客户数排序'],
memberList: [],
rankList: []
}
},
onLoad() {
this.loadTeamStats()
this.loadMemberList()
this.loadRankList()
},
methods: {
async loadTeamStats() {
try {
// TODO: 调用API获取团队统计
this.teamStats = {
memberCount: 8,
totalSales: 15680.00,
totalCommission: 1568.00
}
} catch (error) {
console.error('加载团队统计失败:', error)
}
},
async loadMemberList() {
try {
// 调用API获取团队成员列表
const token = uni.getStorageSync('token')
const res = await uni.request({
url: 'http://localhost:8089/api/distributor/team/members',
method: 'GET',
header: {
'Authorization': 'Bearer ' + token
},
data: {
page: 1,
size: 100
}
})
if (res.data && res.data.code === 200) {
const records = res.data.data.records || []
// 处理团队成员数据,保留所有详细信息
this.memberList = records.map(member => {
// 处理孩子信息
let children = []
if (member.studentInfo) {
children = [{
studentName: member.studentInfo.studentName,
gender: member.studentInfo.gender,
age: member.studentInfo.age,
grade: member.studentInfo.grade,
school: member.studentInfo.school,
hobbies: member.studentInfo.hobbies,
specialties: member.studentInfo.specialties
}]
}
return {
id: member.id,
name: member.name || member.nickname,
realName: member.realName, // ✅ 保存真实姓名
phone: member.phone, // ✅ 保存电话
avatar: member.avatar || '/static/images/default-avatar.png',
level: member.level || 1,
joinTime: member.joinTime ? member.joinTime.substring(0, 10) : '',
customerCount: member.customerCount || 0,
orderCount: member.orderCount || 0,
totalSales: member.totalSales || 0,
residenceAddress: member.residenceAddress, // ✅ 保存所在区域
customerRequirement: member.customerRequirement, // ✅ 保存客户需求
children: children // ✅ 保存孩子信息
}
})
console.log('团队成员列表加载成功:', this.memberList)
} else {
console.error('加载团队成员失败:', res.data)
uni.showToast({
title: '加载失败',
icon: 'none'
})
}
} catch (error) {
console.error('加载团队成员失败:', error)
uni.showToast({
title: '网络错误',
icon: 'none'
})
}
},
async loadRankList() {
try {
// TODO: 调用API获取排行榜
this.rankList = [
{
id: 1,
name: '张小明',
avatar: '/static/images/avatar1.jpg',
sales: 5680.00
},
{
id: 2,
name: '李红',
avatar: '/static/images/avatar2.jpg',
sales: 3200.00
},
{
id: 3,
name: '王芳',
avatar: '/static/images/avatar3.jpg',
sales: 2800.00
}
]
} catch (error) {
console.error('加载排行榜失败:', error)
}
},
onSortChange(e) {
this.selectedSort = this.sortOptions[e.detail.value]
this.loadMemberList()
},
getLevelText(level) {
const levelMap = {
1: '一级',
2: '二级'
}
return levelMap[level] || '未知'
},
inviteMember() {
uni.navigateTo({
url: '/distributor-package/pages/distributor/promotion-code'
})
},
viewMemberDetail(member) {
// 构建详细信息内容(竖向排列,增加空行)
let content = ''
// 1. 电话
content += `电话:${member.phone || '未提供'}\n\n`
// 2. 姓名
content += `姓名:${member.realName || member.name || '未知'}\n\n`
// 3. 孩子基本情况
content += `孩子基本情况:`
if (member.children && member.children.length > 0) {
content += `\n`
member.children.forEach((child, index) => {
const childName = child.studentName || '未知'
const gender = child.gender === 1 ? '男' : child.gender === 2 ? '女' : '未知'
const age = child.age ? `${child.age}` : ''
const grade = child.grade || ''
content += `${childName} (${gender}) ${age} ${grade}\n`
})
content += `\n`
} else {
content += `暂无\n\n`
}
// 4. 所在区域
content += `所在区域:${member.residenceAddress || '未提供'}\n\n`
// 5. 客户需求
content += `客户需求:${member.customerRequirement || '暂无'}`
uni.showModal({
title: '团队成员详情',
content: content,
showCancel: false,
confirmText: '关闭'
})
}
}
}
</script>
<style lang="scss" scoped>
.team-page {
min-height: 100vh;
background: #f0f9f7;
padding: 20rpx;
}
.gradient-bg {
background: linear-gradient(135deg, #7dd3c0 0%, #5fb8a8 100%);
}
.overview-card {
border-radius: 20rpx;
padding: 40rpx;
margin-bottom: 20rpx;
box-shadow: 0 8rpx 24rpx rgba(125, 211, 192, 0.3);
}
.card-title {
font-size: 36rpx;
font-weight: bold;
color: #ffffff;
margin-bottom: 30rpx;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 10rpx;
}
.stat-value {
font-size: 40rpx;
font-weight: bold;
color: #ffffff;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.9);
}
.invite-section {
margin-bottom: 20rpx;
}
.invite-card {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
display: flex;
align-items: center;
gap: 20rpx;
}
.invite-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.invite-title {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.invite-desc {
font-size: 24rpx;
color: #999999;
}
.invite-btn {
display: flex;
align-items: center;
gap: 10rpx;
padding: 20rpx 30rpx;
background: linear-gradient(135deg, #7dd3c0 0%, #5fb8a8 100%);
color: #ffffff;
border-radius: 50rpx;
font-size: 28rpx;
border: none;
white-space: nowrap;
}
.btn-icon {
font-size: 32rpx;
}
.member-section, .rank-section {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.sort-picker {
display: flex;
align-items: center;
gap: 10rpx;
padding: 12rpx 20rpx;
background: #f0f9f7;
border-radius: 8rpx;
font-size: 26rpx;
color: #666666;
}
.arrow {
font-size: 20rpx;
}
.member-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.member-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 30rpx;
background: #f0f9f7;
border-radius: 16rpx;
}
.member-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
flex-shrink: 0;
}
.member-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.member-name-row {
display: flex;
align-items: center;
gap: 15rpx;
}
.member-name {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.member-level {
padding: 6rpx 12rpx;
font-size: 22rpx;
border-radius: 8rpx;
&.level-1 {
background: #fff3e0;
color: #ff9800;
}
&.level-2 {
background: #e3f2fd;
color: #2196f3;
}
}
.member-time {
font-size: 24rpx;
color: #999999;
}
.member-stats {
display: flex;
gap: 30rpx;
}
.stat-text {
font-size: 24rpx;
color: #666666;
}
.member-performance {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10rpx;
}
.performance-label {
font-size: 24rpx;
color: #999999;
}
.performance-value {
font-size: 32rpx;
font-weight: bold;
color: #ff6b6b;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 100rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999999;
margin-bottom: 10rpx;
}
.empty-hint {
font-size: 24rpx;
color: #cccccc;
}
.rank-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.rank-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 20rpx;
background: #f0f9f7;
border-radius: 16rpx;
}
.rank-number {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
background: #e0e0e0;
color: #666666;
border-radius: 50%;
font-size: 28rpx;
font-weight: bold;
flex-shrink: 0;
&.rank-1 {
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
color: #ffffff;
}
&.rank-2 {
background: linear-gradient(135deg, #c0c0c0 0%, #e8e8e8 100%);
color: #ffffff;
}
&.rank-3 {
background: linear-gradient(135deg, #cd7f32 0%, #e8a87c 100%);
color: #ffffff;
}
}
.rank-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
flex-shrink: 0;
}
.rank-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.rank-name {
font-size: 28rpx;
font-weight: bold;
color: #333333;
}
.rank-sales {
font-size: 24rpx;
color: #666666;
}
.rank-badge {
font-size: 48rpx;
}
</style>