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

616 lines
14 KiB
Vue
Raw Permalink Normal View History

<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>