aslan-h5/src/views/HostCenterView.vue
2025-08-19 19:55:20 +08:00

871 lines
18 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="host-center gradient-background-circles">
<!-- 顶部导航 -->
<MobileHeader title="Host 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">
<span>{{ (userInfo.userNickname || userInfo.name).charAt(0) }}</span>
</div>
<div class="user-details">
<h3>{{ userInfo.userNickname || userInfo.name }}</h3>
<p>ID: {{ userInfo.account || userInfo.id }}</p>
<!-- 显示系统信息 -->
<p v-if="headerInfo.sysOrigin" class="system-info">
{{ headerInfo.sysOrigin }}
<span v-if="headerInfo.appVersion"> v{{ headerInfo.appVersion }}</span>
</p>
</div>
<button class="notification-btn" @click="goToNotification">
<span></span>
</button>
</div>
<!-- 账户状态标签 -->
<div class="account-tags">
<span class="tag host-tag">👑 Host</span>
</div>
</div>
<!-- My salary 区域 -->
<div class="salary-section">
<div class="salary-header">
<h3>My salary:</h3>
<button class="personal-salary-btn" @click="showDatePicker">
Personal Salary <span class="arrow">></span>
</button>
</div>
<div class="salary-content">
<!--暂时禁用后面补充-->
<!-- <div class="salary-item">
<p class="salary-label">History Salary:</p>
<p class="salary-amount">${{ salaryInfo.hostSalary }}</p>
</div>
<div class="salary-separator"></div>-->
<div class="salary-item">
<p class="salary-label">Current Salary:</p>
<p class="salary-amount">${{ salaryInfo.currentSalary }}</p>
</div>
</div>
</div>
<!-- Today's Task -->
<div class="task-section">
<h3>Today's Task:</h3>
<!-- 任务表格 -->
<div class="task-table">
<div class="table-row table-header">
<div class="table-cell">Anchor type</div>
<div class="table-cell">Minutes</div>
<div class="table-cell">Gift Task</div>
</div>
<div class="table-row table-data">
<div class="table-cell">{{ taskInfo.anchorType }}</div>
<div class="table-cell">
<span class="minutes-completed">{{ taskInfo.minutes }}</span>
</div>
<div class="table-cell">{{ taskInfo.giftTask }}</div>
</div>
</div>
<!-- Platform Policy 链接 -->
<!-- <div class="policy-link" @click="goToPlatformPolicy">
<span>Platform Policy</span>
<span class="arrow">></span>
</div>-->
</div>
<!-- 进度信息 -->
<div class="progress-section">
<div class="progress-header">
<span class="progress-date">{{ progressInfo.date }}</span>
<span class="progress-status">{{ progressInfo.status }}</span>
</div>
<div class="progress-details">
<div class="progress-item">
<span>Target: {{ progressInfo.target }}</span>
<span>Salary: ${{ progressInfo.salary }}</span>
</div>
<div class="progress-item">
<span>Time(Days): {{ progressInfo.timeDays }}</span>
<span>Diamond: {{ progressInfo.diamond }}</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>
<!--
<button class="action-btn withdraw-btn" @click="cashWithdraw">
Cash<br>Withdraw
</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 { getBankBalance, getUserIdentity, getTeamEntry, getTeamMemberWorkStatistics } from '../api/wallet.js'
import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js'
import {getUserId, setUserInfo} from '../utils/userStore.js'
const router = useRouter()
// 当前身份
const currentIdentity = ref('host')
const loading = ref(false)
const appConnected = ref(false)
const headerInfo = ref({})
const userProfile = ref(null)
const identityChecked = 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 and bank balance...')
const teamEntryResult = await fetchTeamEntry()
console.debug('✅ Team entry fetched')
if (teamEntryResult != null) {
await fetchBankBalance()
console.debug('✅ Bank balance fetch initiated')
await fetchWorkStatistics()
console.debug('✅ Work statistics 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...')
}
// 获取银行余额
const fetchBankBalance = async () => {
loading.value = true
try {
const response = await getBankBalance()
if (response.status && typeof response.body === 'number') {
// 格式化余额显示保留2位小数
salaryInfo.currentSalary = response.body.toFixed(2)
} else {
salaryInfo.currentSalary = 0.00
}
} catch (error) {
console.error('Failed to fetch bank balance:', error)
salaryInfo.currentSalary = 0.00
// 可以显示错误提示
alert('Failed to load balance. Please try again.')
} finally {
loading.value = false
}
}
// 获取工作统计数据
const fetchWorkStatistics = async () => {
try {
const response = await getTeamMemberWorkStatistics('DAILY', getUserId())
if (response.status && response.body) {
// 更新任务信息
taskInfo.anchorType = 'Chat' // 固定值
taskInfo.minutes = `${response.body.ownTime}/120` // ownTime/120
taskInfo.giftTask = response.body.acceptGiftValue || 0 // acceptGiftValue
}
} catch (error) {
console.error('获取工作统计失败:', error)
}
}
// 用户信息
const userInfo = reactive({
name: 'User1',
id: '1234567890',
userNickname: 'User1',
account: '1234567890'
})
// 收入信息
const salaryInfo = reactive({
hostSalary: 120,
currentSalary: 0.00
})
// 任务信息
const taskInfo = reactive({
anchorType: 'Chat',
minutes: '0/120',
giftTask: 0
})
// 进度信息
const progressInfo = reactive({
date: '2025.08',
status: 'Pending',
target: 'B',
salary: 50,
timeDays: 20,
diamond: 50000,
hostSalary: '',
agentSalary: '',
total: ''
})
const showDatePicker = () => {
router.push('/platform-policy')
}
const goToNotification = () => {
router.push('/host-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.account = response.body.memberProfile.account
}
// 获取用户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, bd } = response.body
console.debug('用户身份权限:', { freightAgent, anchor, agent, bd })
// 根据优先级判断freightAgent > agent > anchor > bd
if (freightAgent) {
console.debug('跳转到 coin-seller')
router.replace('/coin-seller')
} else if (agent) {
console.debug('跳转到 agency-center')
router.replace('/agency-center')
} else if (bd) {
console.debug('跳转到 bd-center')
router.replace('/bd-center')
} else if (anchor) {
// 保持在当前Host页面
console.debug('保持在 host-center')
currentIdentity.value = 'host'
} else {
// 如果没有任何权限默认保持在Host页面
console.debug('无特殊权限,保持在 host-center')
currentIdentity.value = 'host'
}
}
} catch (error) {
console.error('获取用户身份失败:', error)
// 出错时保持在Host页面
currentIdentity.value = 'host'
}
}
const exchangeGoldCoins = () => {
router.push('/exchange-gold-coins')
}
const transfer = () => {
router.push('/transfer')
}
// 页面加载时先连接APP然后获取数据
onMounted(async () => {
// 先连接APP获取认证信息
await connectToApp()
// 如果不是APP环境直接获取数据
if (!isInApp()) {
await fetchBankBalance()
await fetchWorkStatistics()
}
})
</script>
<style scoped>
.host-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;
}
.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;
}
.edit-btn {
width: 32px;
height: 32px;
border: none;
background: none;
font-size: 16px;
cursor: pointer;
}
.notification-btn {
width: 32px;
height: 32px;
border: none;
background: none;
font-size: 16px;
cursor: pointer;
}
/* 身份切换标签 */
.identity-tabs {
display: flex;
gap: 8px;
margin: 12px 0;
}
.identity-tab {
padding: 6px 12px;
border: 1px solid #e5e7eb;
border-radius: 16px;
background-color: white;
color: #666;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.identity-tab.active {
background-color: #8B5CF6;
color: white;
border-color: #8B5CF6;
}
.identity-tab:not(.active):hover {
border-color: #8B5CF6;
color: #8B5CF6;
}
.identity-tab.disabled {
cursor: not-allowed;
opacity: 0.6;
}
.identity-tab.disabled:hover {
border-color: #e5e7eb;
color: #666;
}
.identity-tab.disabled.active:hover {
border-color: #8B5CF6;
color: white;
}
.account-tags {
display: flex;
gap: 8px;
}
.tag {
padding: 4px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 600;
}
.host-tag {
background-color: #E0E7FF;
color: #5B21B6;
}
.agency-tag {
background-color: #DBEAFE;
color: #1E40AF;
}
.coin-tag {
background-color: #FEF3C7;
color: #D97706;
}
.salary-tag {
background-color: #FEF3C7;
color: #D97706;
}
/* My 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-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.salary-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.personal-salary-btn {
background: none;
border: none;
color: #8B5CF6;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s;
}
.personal-salary-btn:hover {
color: #7C3AED;
}
.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;
}
/* Today's Task 区域 */
.task-section {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.task-section h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
/* 任务表格 */
.task-table {
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
margin-bottom: 12px;
}
.table-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.table-header {
background-color: #f8f9fa;
border-bottom: 1px solid #e5e7eb;
}
.table-data {
background-color: white;
}
.table-cell {
padding: 12px 8px;
text-align: center;
border-right: 1px solid #e5e7eb;
font-size: 14px;
}
.table-cell:last-child {
border-right: none;
}
.table-header .table-cell {
font-weight: 600;
color: #374151;
}
.table-data .table-cell {
color: #111827;
}
.minutes-progress {
color: #6B7280;
}
.minutes-completed {
color: #10B981;
font-weight: 600;
}
/* Platform Policy 链接 */
.policy-link {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
margin-top: 12px;
background-color: #f8f9fa;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid #e9ecef;
}
.policy-link:hover {
background-color: #e9ecef;
border-color: #8B5CF6;
}
.policy-link:active {
transform: scale(0.98);
}
.policy-link span:first-child {
font-size: 14px;
font-weight: 500;
color: #333;
}
/* 进度信息 */
.progress-section {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.progress-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.progress-date {
font-size: 16px;
font-weight: 600;
color: #333;
}
.progress-status {
background-color: #3B82F6;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.progress-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.progress-item {
display: flex;
flex-direction: column;
gap: 8px;
}
.progress-item span {
font-size: 14px;
color: #333;
}
/* 操作按钮 */
.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;
}
.withdraw-btn {
background-color: #10B981;
color: white;
}
/* 通用箭头样式 */
.arrow {
font-size: 16px;
font-weight: bold;
color: #8B5CF6;
}
</style>