aslan-h5/src/views/BDCenterView.vue
2025-08-22 11:26:50 +08:00

611 lines
14 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
<div class="bd-center gradient-background-circles">
<!-- 顶部导航 -->
<MobileHeader title="BD Center" :isHomePage="true" />
<!-- 主要内容 -->
<div class="content">
<!-- 用户信息卡片 -->
<div class="user-card">
<!-- APP连接状态指示 -->
<div v-if="isInApp() && !appConnected" class="app-status connecting">
<div class="status-indicator"></div>
<span>正在连接APP...</span>
</div>
<div class="user-info">
<div class="avatar">
<img
v-if="userInfo.userAvatar"
:src="userInfo.userAvatar"
:alt="userInfo.name"
@error="handleImageError"
/>
<span v-else class="avatar-placeholder">{{ getAvatarPlaceholder() }}</span>
</div>
<div class="user-details">
<h3>{{ userInfo.userNickname || userInfo.name }}</h3>
<p>ID: {{ userInfo.account || userInfo.id }}</p>
</div>
<!-- <button class="notification-btn" @click="goToNotification">-->
<!-- <span></span>-->
<!-- </button>-->
</div>
<!-- 账户状态标签 -->
<div class="account-tags">
<span class="tag bd-tag">👑 BD</span>
</div>
</div>
<!-- Total team salary 区域 -->
<div class="salary-section">
<h3>Total team salary:</h3>
<div class="salary-content">
<div class="salary-item">
<p class="salary-label">Last month:</p>
<p class="salary-amount">${{ salaryInfo.lastMonth }}</p>
</div>
<div class="salary-separator"></div>
<div class="salary-item">
<p class="salary-label">This month:</p>
<p class="salary-amount">${{ salaryInfo.thisMonth }}</p>
</div>
</div>
</div>
<!-- Team Member 卡片 -->
<div class="team-member-section">
<div class="team-member-card" @click="goToTeamMember">
<div class="card-content">
<span class="member-title">
Team Member
<span v-if="loadingMemberCount">(Loading...)</span>
<span v-else>({{ teamMemberCount }})</span>
</span>
<span class="arrow">></span>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button class="action-btn exchange-btn" @click="exchangeGoldCoins">
Exchange
</button>
<button class="action-btn transfer-btn" @click="transfer">
Transfer
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { getUserIdentity, getTeamEntry } from '../api/wallet.js'
import {getAgentMonthLastTarget, getAgentMonthTarget, getTeamMemberCount} from '../api/teamBill.js'
import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js'
import { setUserInfo } from '../utils/userStore.js'
import { get } from '../utils/http.js'
import { getTeamId } from '@/utils/userStore.js'
const router = useRouter()
// 当前身份
const currentIdentity = ref('bd')
const loading = ref(false)
const appConnected = ref(false)
const headerInfo = ref({})
const userProfile = ref(null)
const identityChecked = ref(false)
const teamMemberCount = ref(0)
const loadingMemberCount = ref(false)
// 连接APP并获取认证信息
const connectToApp = async () => {
console.group('🔗 APP Connection Process')
console.debug('🚀 Starting APP connection...')
if (!isInApp()) {
console.warn('⚠️ Not running in APP environment')
console.debug('🌐 Browser environment detected')
console.debug('📱 APP Bridge objects:')
console.debug(' - window.app:', !!window.app)
console.debug(' - window.webkit:', !!window.webkit)
// 非APP环境直接执行后续逻辑
appConnected.value = true
console.debug('✅ Skipping APP connection, proceeding with team entry')
await fetchTeamEntry()
console.groupEnd()
return
}
console.debug('📱 APP environment detected')
console.debug('🔍 Checking APP bridge availability:')
console.debug(' - window.app:', !!window.app)
console.debug(' - window.webkit:', !!window.webkit)
connectApplication(async (access) => {
const result = parseAccessOrigin(access)
if (result.success) {
// 解析头部信息
headerInfo.value = parseHeader(result.data)
// 设置HTTP请求头
console.debug('🔧 Setting HTTP headers...')
await setHttpHeaders(headerInfo.value)
// 标记连接成功
appConnected.value = true
console.debug('🎉 APP connection established successfully')
// 连接成功后获取团队信息
console.debug('📊 Fetching team entry...')
const teamEntryResult = await fetchTeamEntry()
console.debug('✅ Team entry fetched')
if (teamEntryResult != null) {
await fetchTeamMemberCount()
console.debug('✅ Team member count fetch initiated')
}
} else {
console.error('❌ Failed to parse access origin:', result.error)
console.debug('🔄 Redirecting to error page...')
router.push({ path: '/not_app', query: { message: result.error }})
}
console.groupEnd()
})
console.debug('⏳ Waiting for APP response...')
console.groupEnd()
}
// 用户信息
const userInfo = reactive({
userNickname: '',
id: '',
userAvatar: '',
})
// 薪资信息
const salaryInfo = reactive({
lastMonth: '',
thisMonth: ''
})
// 获取团队成员数量
const fetchTeamMemberCount = async () => {
try {
loadingMemberCount.value = true
const teamId = getTeamId()
if (teamId) {
const response = await getTeamMemberCount(teamId)
if (response.status && typeof response.body === 'number') {
teamMemberCount.value = response.body
} else {
teamMemberCount.value = 0
}
}
} catch (error) {
console.error('Failed to fetch team member count:', error)
teamMemberCount.value = 87 // 出错时使用默认值
} finally {
loadingMemberCount.value = false
}
}
const getAgentMonth = async () => {
const res = await getAgentMonthTarget()
if (res.status === true) {
salaryInfo.thisMonth = res.body.thisMonthTotalTarget
}
const res2 = await getAgentMonthLastTarget()
if (res2.status === true) {
salaryInfo.lastMonth = res.body.thisMonthTotalTarget
}
}
const goToNotification = () => {
router.push('/bd-setting')
}
// 获取团队入口信息
const fetchTeamEntry = async () => {
try {
const response = await getTeamEntry()
if (response && response.status && response.body) {
userProfile.value = response.body
// 缓存用户信息
if (response.body.memberProfile) {
setUserInfo(response.body.memberProfile, response.body.teamProfile)
// 更新用户信息显示
userInfo.userNickname = response.body.memberProfile.userNickname
userInfo.id = response.body.memberProfile.account
userInfo.userAvatar = response.body.memberProfile.userAvatar
}
// 获取用户ID后检查身份
const userId = response.body.memberProfile?.id
if (userId) {
await checkUserIdentity(userId)
}
return userId
}
} catch (error) {
console.error('获取团队信息失败:', error)
// HTTP 406错误通常表示NOT_TEAM_MEMBER
if (error.message && error.message.includes('406')) {
await router.push('/apply')
return
}
if (error.message && error.message.includes('401')) {
await router.push({
path: '/not_app',
query: { message: error.message }
})
return
}
}
return null
}
// 获取用户身份并自动跳转
const checkUserIdentity = async (userId) => {
if (!userId) {
console.error('用户ID不存在')
return
}
// 防止重复检查
if (identityChecked.value) {
console.debug('身份已检查,跳过')
return
}
try {
identityChecked.value = true
const response = await getUserIdentity(userId)
if (response && response.status && response.body) {
const { freightAgent, anchor, agent } = response.body
console.debug('用户身份权限:', { freightAgent, anchor, agent })
// 根据优先级判断freightAgent > anchor > agent > bd(默认)
if (freightAgent) {
console.debug('跳转到 coin-seller')
// router.replace('/coin-seller')
} else if (agent) {
console.debug('跳转到 agency-center')
// router.replace('/agency-center')
} else if (anchor) {
console.debug('跳转到 host-center')
// router.replace('/host-center')
} else {
// 如果没有任何权限保持在BD页面
console.debug('保持在 bd-center')
currentIdentity.value = 'bd'
}
}
} catch (error) {
console.error('获取用户身份失败:', error)
// 出错时保持在BD页面
currentIdentity.value = 'bd'
}
}
const goToTeamMember = () => {
router.push('/team-member')
}
const exchangeGoldCoins = () => {
router.push('/exchange-gold-coins')
}
const transfer = () => {
router.push('/transfer')
}
// 处理头像图片加载失败
const handleImageError = (event) => {
// 图片加载失败时清空userAvatar显示占位符
userInfo.userAvatar = ''
}
// 获取头像占位符(用户名首字母)
const getAvatarPlaceholder = () => {
if (userInfo.name && userInfo.name.length > 0) {
return userInfo.name.charAt(0).toUpperCase()
}
return 'U' // 默认显示 'U'
}
// 页面加载时先连接APP然后获取数据
onMounted(async () => {
// 先连接APP获取认证信息
await connectToApp()
await fetchTeamEntry()
await getAgentMonth()
await fetchTeamMemberCount()
})
</script>
<style scoped>
.bd-center {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
/* 用户信息卡片 */
.user-card {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* APP连接状态 */
.app-status {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
margin-bottom: 12px;
font-size: 12px;
font-weight: 500;
}
.app-status.connecting {
background-color: #fef3c7;
color: #d97706;
}
.app-status.connected {
background-color: #d1fae5;
color: #059669;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: currentColor;
}
.app-status.connecting .status-indicator {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 25px;
background-color: #8B5CF6;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
font-weight: 600;
margin-right: 12px;
overflow: hidden;
position: relative;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 25px;
}
.avatar .avatar-placeholder {
color: white;
font-size: 20px;
font-weight: 600;
}
.user-details {
flex: 1;
}
.user-details h3 {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.user-details p {
margin: 0;
font-size: 14px;
color: #666;
}
.system-info {
font-size: 12px !important;
color: #8B5CF6 !important;
margin-top: 2px !important;
}
.notification-btn {
width: 32px;
height: 32px;
border: none;
background: none;
font-size: 16px;
cursor: pointer;
}
.account-tags {
display: flex;
gap: 8px;
}
.tag {
padding: 4px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 600;
}
.bd-tag {
background-color: #E0E7FF;
color: #5B21B6;
}
/* Total team salary 区域 */
.salary-section {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.salary-section h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.salary-content {
display: flex;
align-items: center;
gap: 20px;
}
.salary-item {
flex: 1;
text-align: center;
}
.salary-label {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
}
.salary-amount {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #333;
}
.salary-separator {
width: 1px;
height: 40px;
background-color: #8B5CF6;
}
/* Team Member 独立区域 */
.team-member-section {
margin-bottom: 16px;
}
.team-member-card {
background-color: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
cursor: pointer;
transition: all 0.2s;
border: 1px solid #e5e7eb;
}
.team-member-card:active {
transform: scale(0.98);
}
.team-member-card .card-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
}
.member-title {
font-size: 16px;
font-weight: 600;
color: #1F2937;
}
/* 操作按钮 */
.action-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.action-btn {
padding: 14px 12px;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
text-align: center;
line-height: 1.2;
transition: all 0.2s;
width: 100%;
}
.action-btn:active {
transform: scale(0.98);
}
.exchange-btn {
background-color: #FDE68A;
color: #D97706;
}
.transfer-btn {
background-color: #8B5CF6;
color: white;
}
/* 通用箭头样式 */
.arrow {
font-size: 16px;
font-weight: bold;
color: #8B5CF6;
}
</style>