Files
sbnews/uniapp/src/pages/distribution/distribution.vue
T

549 lines
15 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="dist-page">
<view class="dist-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="dist-header__nav">
<u-icon name="arrow-left" color="#fff" size="36" @click="goBack" />
<text class="dist-header__title">分销中心</text>
<view style="width: 36rpx" />
</view>
<view class="dist-stats">
<view class="dist-stats__item">
<text class="dist-stats__num">{{ distInfo.total_commission || '0.00' }}</text>
<text class="dist-stats__label">累计佣金</text>
</view>
<view class="dist-stats__divider" />
<view class="dist-stats__item">
<text class="dist-stats__num">{{ distInfo.available_amount || '0.00' }}</text>
<text class="dist-stats__label">可提现</text>
</view>
<view class="dist-stats__divider" />
<view class="dist-stats__item">
<text class="dist-stats__num">{{ teamTotal }}</text>
<text class="dist-stats__label">团队人数</text>
</view>
</view>
<view class="dist-invite">
<view class="dist-invite__title">
<text>邀请好友赚佣金</text>
</view>
<view class="dist-invite__desc">
<text>邀请好友注册并消费您可获得佣金奖励</text>
</view>
<view class="dist-invite__rates">
<view class="dist-invite__rate-card">
<text class="dist-invite__rate-num">
{{ distInfo.commission_rate_1 ? distInfo.commission_rate_1 + '%' : '-' }}
</text>
<text class="dist-invite__rate-label">一级佣金</text>
<text class="dist-invite__rate-desc">
直接邀请用户消费金额的{{ distInfo.commission_rate_1 ? distInfo.commission_rate_1 + '%' : '-' }}
</text>
</view>
<view class="dist-invite__rate-card">
<text class="dist-invite__rate-num">
{{ distInfo.commission_rate_2 ? distInfo.commission_rate_2 + '%' : '-' }}
</text>
<text class="dist-invite__rate-label">二级佣金</text>
<text class="dist-invite__rate-desc">
间接邀请用户消费金额的{{ distInfo.commission_rate_2 ? distInfo.commission_rate_2 + '%' : '-' }}
</text>
</view>
</view>
<view class="dist-invite__code">
<view class="dist-invite__code-box">
<text class="dist-invite__code-label">我的邀请码</text>
<text class="dist-invite__code-value">{{ inviteCode || '加载中...' }}</text>
</view>
<view class="dist-invite__btns">
<view class="dist-invite__btn dist-invite__btn--copy" @tap="copyCode">
<u-icon name="file-text" size="28" color="#005fff" />
<text>复制邀请码</text>
</view>
<view class="dist-invite__btn dist-invite__btn--share" @tap="handleShare">
<u-icon name="share-square" size="28" color="#fff" />
<text>分享邀请链接</text>
</view>
</view>
</view>
</view>
</view>
<view class="dist-team">
<view class="dist-team__header">
<text class="dist-team__title">我的团队 ({{ teamTotal }})</text>
<view class="dist-team__tabs">
<text class="dist-team__tab" :class="{ 'dist-team__tab--active': teamLevel === 1 }"
@tap="switchTeamLevel(1)">
一级
</text>
<text class="dist-team__tab" :class="{ 'dist-team__tab--active': teamLevel === 2 }"
@tap="switchTeamLevel(2)">
二级
</text>
</view>
</view>
<view v-if="loading" class="dist-team__empty">
<u-loading mode="circle" size="40" />
<text class="dist-team__empty-tip">加载中...</text>
</view>
<view v-else-if="teamList.length === 0" class="dist-team__empty">
<u-empty text="暂无团队成员" mode="data" icon-size="120" />
<text class="dist-team__empty-tip">快去邀请好友加入吧</text>
</view>
<template v-else>
<view v-for="item in teamList" :key="item.id" class="dist-team__item">
<image class="dist-team__avatar" :src="item.avatar || '/static/images/avatar.png'" mode="aspectFill"
lazy-load />
<view class="dist-team__info">
<text class="dist-team__name">{{ item.nickname }}</text>
<text class="dist-team__time">ID:{{ item.sn || item.id }}</text>
</view>
</view>
<view v-if="hasMore" class="dist-team__loadmore" @tap="loadMore">
<text>{{ loadingMore ? '加载中...' : '加载更多' }}</text>
</view>
</template>
<view class="dist-team__footer">
<view class="dist-team__footer-item">
<text class="dist-team__footer-label">总收益 </text>
<text class="dist-team__footer-value">
{{ distInfo.total_income || '0.00' }}
</text>
</view>
<view class="dist-team__footer-item">
<text class="dist-team__footer-label">总消费 </text>
<text class="dist-team__footer-value">
{{ distInfo.total_consume || '0.00' }}
</text>
</view>
</view>
</view>
<share-popup v-if="showSharePopup" v-model:show="showSharePopup" page-type="invite" path="/pages/index/index" title="邀请你加入我的团队" />
</view>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { getShareInfo, getInviteList } from '@/api/distribution'
import type { DistTeamItem } from '@/api/distribution'
import SharePopup from '@/components/share-popup/share-popup.vue'
import { checkLogin } from '@/utils/useLoginCheck'
const statusBarHeight = ref(0)
const inviteCode = ref('')
const h5Domain = ref('')
const teamList = ref<DistTeamItem[]>([])
const teamTotal = ref(0)
const loading = ref(false)
const loadingMore = ref(false)
const page = ref(1)
const hasMore = ref(false)
const showSharePopup = ref(false)
const teamLevel = ref(1)
const distInfo = ref({
total_commission: '',
available_amount: '',
commission_rate_1: '',
commission_rate_2: '',
total_income: '',
total_consume: ''
})
onMounted(async () => {
const sysInfo = uni.getSystemInfoSync()
// #ifdef APP-PLUS || MP
statusBarHeight.value = sysInfo.statusBarHeight || 44
// #endif
// #ifdef H5
statusBarHeight.value = 0
// #endif
const loggedIn = await checkLogin()
if (!loggedIn) return
fetchShareInfo()
fetchTeamList()
})
const goBack = () => {
if (getCurrentPages().length > 1) {
uni.navigateBack()
} else {
uni.switchTab({ url: '/pages/user/user' })
}
}
async function fetchShareInfo() {
try {
const res = await getShareInfo()
inviteCode.value = res.invite_code || ''
h5Domain.value = res.h5_domain || ''
teamTotal.value = res.team_count || 0
} catch (e) {
console.error('获取分享信息失败', e)
}
}
async function fetchTeamList() {
loading.value = true
try {
const res = await getInviteList({ page: 1, size: 20, level: teamLevel.value })
teamList.value = res.list || []
const listTotal = res.total || 0
page.value = 1
hasMore.value = teamList.value.length < listTotal
} catch (e) {
console.error('获取团队列表失败', e)
} finally {
loading.value = false
}
}
async function loadMore() {
if (loadingMore.value || !hasMore.value) return
loadingMore.value = true
try {
const nextPage = page.value + 1
const res = await getInviteList({ page: nextPage, size: 20, level: teamLevel.value })
const list = res.list || []
teamList.value.push(...list)
page.value = nextPage
hasMore.value = teamList.value.length < (res.total || 0)
} catch (e) {
console.error('加载更多失败', e)
} finally {
loadingMore.value = false
}
}
const copyCode = () => {
if (!inviteCode.value) {
uni.showToast({ title: '邀请码加载中', icon: 'none' })
return
}
uni.setClipboardData({
data: inviteCode.value,
success: () => {
uni.showToast({ title: '邀请码已复制', icon: 'success' })
}
})
}
const switchTeamLevel = (level: number) => {
teamLevel.value = level
page.value = 1
fetchTeamList()
}
const handleShare = () => {
if (!inviteCode.value) {
uni.showToast({ title: '信息加载中', icon: 'none' })
return
}
showSharePopup.value = true
}
</script>
<style lang="scss" scoped>
.dist-page {
min-height: 100vh;
background: #f5f5f5;
}
.dist-header {
background: linear-gradient(to bottom, #005fff 70%, #f5f5f5 70%);
padding: 0 0 40rpx;
&__nav {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 32rpx;
}
&__title {
font-size: 34rpx;
font-weight: 600;
color: #fff;
}
}
.dist-stats {
display: flex;
align-items: center;
justify-content: space-around;
margin: 60rpx 40rpx 0;
background: rgba(255, 255, 255, 0.15);
border-radius: 20rpx;
padding: 32rpx 0;
&__item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
&__num {
font-size: 36rpx;
font-weight: 700;
color: #fff;
}
&__label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
margin-top: 8rpx;
}
&__divider {
width: 1rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.2);
}
}
.dist-invite {
margin: 40rpx 40rpx 0;
background: #fff;
border-radius: 20rpx;
padding: 28rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
&__title {
font-size: 32rpx;
font-weight: 700;
color: #005fff;
}
&__desc {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
&__rates {
display: flex;
gap: 20rpx;
margin-top: 24rpx;
}
&__rate-card {
flex: 1;
border: 2rpx solid #e8f0fe;
border-radius: 16rpx;
padding: 20rpx;
background: #f7faff;
}
&__rate-num {
font-size: 36rpx;
font-weight: 700;
color: #005fff;
display: block;
}
&__rate-label {
font-size: 24rpx;
color: #333;
font-weight: 600;
margin-top: 4rpx;
display: block;
}
&__rate-desc {
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
display: block;
line-height: 1.4;
}
&__code {
margin-top: 28rpx;
padding-top: 24rpx;
border-top: 1rpx solid #f0f0f0;
}
&__code-box {
display: flex;
align-items: center;
justify-content: space-between;
background: #f8f8f8;
border-radius: 12rpx;
padding: 20rpx 24rpx;
margin-bottom: 20rpx;
}
&__code-label {
font-size: 24rpx;
color: #999;
}
&__code-value {
font-size: 32rpx;
font-weight: 700;
color: #005fff;
letter-spacing: 4rpx;
}
&__btns {
display: flex;
gap: 16rpx;
}
&__btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 20rpx 0;
border-radius: 44rpx;
font-size: 26rpx;
&--copy {
background: #fff;
border: 2rpx solid #005fff;
color: #005fff;
}
&--share {
background: #005fff;
color: #fff;
}
}
}
.dist-team {
margin: 0 40rpx 20rpx;
background: #fff;
border-radius: 20rpx;
padding: 28rpx;
&__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
&__title {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
&__tabs {
display: flex;
gap: 16rpx;
}
&__tab {
font-size: 24rpx;
color: #666;
padding: 8rpx 28rpx;
border-radius: 24rpx;
border: 2rpx solid #e0e0e0;
background: #fff;
&--active {
background: #005fff;
color: #fff;
border-color: #005fff;
}
}
&__empty {
padding: 40rpx 0;
display: flex;
flex-direction: column;
align-items: center;
}
&__empty-tip {
font-size: 24rpx;
color: #999;
margin-top: 12rpx;
}
&__item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
&__avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
flex-shrink: 0;
}
&__info {
flex: 1;
margin-left: 16rpx;
}
&__name {
font-size: 28rpx;
color: #333;
display: block;
}
&__time {
font-size: 22rpx;
color: #999;
margin-top: 12rpx;
display: block;
}
&__loadmore {
text-align: center;
padding: 24rpx 0;
font-size: 24rpx;
color: #999;
}
&__footer {
display: flex;
border-top: 1rpx solid #f0f0f0;
margin-top: 20rpx;
padding-top: 24rpx;
}
&__footer-item {
flex: 1;
}
&__footer-label {
font-size: 24rpx;
color: #999;
display: block;
}
&__footer-value {
font-size: 32rpx;
font-weight: 700;
color: #333;
margin-top: 8rpx;
display: block;
}
}
</style>