更新版

This commit is contained in:
tianfeng 2025-08-15 10:00:39 +08:00
parent a04b27b0c3
commit aa9c365cdf
7 changed files with 170 additions and 222 deletions

View File

@ -1,4 +1,4 @@
import { get, post } from '../utils/http.js'
import {get, post} from '../utils/http.js'
/**
* 获取团队成员列表
@ -147,14 +147,20 @@ export const processInviteMessage = async (data) => {
*/
export const getTeamMemberWork = async (userId) => {
try {
const response = await get(`/team/member-work?userId=${userId}`)
return response
return await get(`/team/member-work?userId=${userId}`)
} catch (error) {
console.error('Failed to fetch team member work:', error)
throw error
}
}
/**
* 用户申请记录查询
*/
export function getApplyRecord(userId, status) {
return get(`/team/user/apply/record?teamId=${userId}&status=${status}`)
}
/**
* 格式化显示状态
* @param {string} status - 状态值
@ -183,7 +189,7 @@ export const formatBillStatus = (status) => {
color: '#059669'
}
}
return statusMap[status] || {
text: status,
class: 'unknown',
@ -214,15 +220,15 @@ export const formatOnlineTime = (minutes) => {
*/
export const formatGiftValue = (value) => {
if (!value || value === '0') return '$0'
// 如果已经是格式化的字符串,直接返回
if (typeof value === 'string' && value.includes('K')) {
return `$${value}`
}
const numValue = parseFloat(value)
if (isNaN(numValue)) return '$0'
if (numValue >= 1000000) {
return `$${(numValue / 1000000).toFixed(1)}M`
} else if (numValue >= 1000) {
@ -240,7 +246,7 @@ export const formatDiamondValue = (value) => {
if (!value || value === '0') return '0'
const numValue = parseInt(value)
if (isNaN(numValue)) return '0'
if (numValue >= 1000000) {
return `${(numValue / 1000000).toFixed(1)}M`
} else if (numValue >= 1000) {
@ -258,7 +264,7 @@ export const formatDateDisplay = (dateNumber) => {
if (!dateNumber) return ''
const dateStr = dateNumber.toString()
if (dateStr.length !== 8) return dateStr
const year = dateStr.substring(0, 4)
const month = dateStr.substring(4, 6)
const day = dateStr.substring(6, 8)
@ -272,12 +278,12 @@ export const formatDateDisplay = (dateNumber) => {
*/
export const formatAccount = (memberProfile) => {
if (!memberProfile) return ''
// 如果有特殊ID优先显示特殊ID
if (memberProfile.ownSpecialId && memberProfile.ownSpecialId.account) {
return memberProfile.ownSpecialId.account
}
// 否则显示普通账户
return memberProfile.account || ''
}

View File

@ -53,6 +53,14 @@ export function userBankSearchUserProfile(type, account) {
return get(`/wallet/bank/search/user-profile?type=${type}&account=${account}`)
}
/**
* 获取用户身份信息
* @returns {Promise} 返回用户身份权限信息
*/
export function getUserIdentity() {
return get('/app/h5/1911661502909562881/identity')
}
/**
* 格式化时间戳为可读格式
* @param {number} timestamp - 时间戳毫秒

View File

@ -1,115 +1,79 @@
<template>
<div class="team-bill-more">
<div class="bill-header">
<h2>{{ billData?.billTitle }}</h2>
<span :class="['bill-status', getStatusClass(billData?.status)]">
{{ getStatusText(billData?.status) }}
</span>
</div>
<!-- 账单汇总信息 -->
<div class="bill-summary-card">
<h3>Summary</h3>
<div class="summary-grid">
<div class="summary-item">
<span class="summary-label">Gift Value:</span>
<span class="summary-value">{{ formatGiftValue(billData?.target?.acceptGiftValue) }}</span>
</div>
<div class="summary-item">
<span class="summary-label">Online Time:</span>
<span class="summary-value">{{ formatOnlineTime(billData?.target?.onlineTime) }}</span>
</div>
<div class="summary-item">
<span class="summary-label">Diamond:</span>
<span class="summary-value diamond-value">{{ formatDiamondValue(billData?.target?.acceptDiamondValue) }}</span>
</div>
<div class="summary-item">
<span class="summary-label">Effective Days:</span>
<span class="summary-value">{{ billData?.target?.effectiveDay || 0 }}</span>
<!-- 抽屉背景蒙层 -->
<div class="drawer-overlay" @click="$emit('close')"></div>
<!-- 抽屉主体 -->
<div class="drawer-content">
<!-- 拖拽指示器 -->
<div class="drag-indicator"></div>
<!-- 头部区域 -->
<div class="bill-header">
<h2>{{ billData?.billTitle }}</h2>
<div class="header-right">
<span :class="['bill-status', getStatusClass(billData?.status)]">
{{ getStatusText(billData?.status) }}
</span>
<button class="close-btn" @click="$emit('close')">
<span>×</span>
</button>
</div>
</div>
<!-- 结算结果如果已结算 -->
<div v-if="billData?.status === 'SETTLED' && billData?.target?.settlementResult" class="settlement-info">
<div class="settlement-row">
<span class="settlement-label">Target Level:</span>
<span class="settlement-value">{{ billData.target.settlementResult.level || '' }}</span>
</div>
<div class="settlement-row">
<span class="settlement-label">Member Salary:</span>
<span class="settlement-value salary-value">${{ billData.target.settlementResult.memberSalary || 0 }}</span>
</div>
<!-- 道具奖励 -->
<div v-if="canReceivePropsReward(billData)" class="props-reward-section">
<div class="props-reward-header">
<span class="props-label">
{{ billData.target.settlementResult.propsReceived ? 'Props Received:' : 'Props Reward:' }}
</span>
<button
v-if="!billData.target.settlementResult.propsReceived"
class="receive-props-btn"
@click="receivePropsReward"
:disabled="receivingReward"
>
{{ receivingReward ? 'Receiving...' : 'Receive Props' }}
</button>
<!-- 薪资汇总信息 -->
<div class="bill-summary-card">
<h3>Salary Summary</h3>
<div class="summary-grid">
<div class="summary-item">
<span class="summary-label">Host Salary:</span>
<span class="summary-value">${{ formatSalaryValue(billData?.target?.ownSalary) }}</span>
</div>
<div class="props-list">
<div
v-for="(prop, index) in billData.target.settlementResult.propsRewards"
:key="index"
class="prop-item"
>
<div class="prop-info">
<span class="prop-name">{{ prop.name }}</span>
<span class="prop-type">{{ prop.type }}</span>
<div class="summary-item">
<span class="summary-label">Agent Salary:</span>
<span class="summary-value">${{ formatSalaryValue(billData?.target?.memberSalary) }}</span>
</div>
<div class="summary-item total-salary">
<span class="summary-label">Total Salary:</span>
<span class="summary-value total-salary-value">${{ formatSalaryValue(billData?.target?.totalSalry) }}</span>
</div>
</div>
</div>
<!-- 每日薪资详情 -->
<div class="daily-details-card">
<h3>Daily Salary Details</h3>
<div v-if="billData?.target?.dailyTargets?.length > 0" class="daily-list">
<div
v-for="(daily, index) in billData.target.dailyTargets"
:key="index"
class="daily-item"
>
<div class="daily-header">
<span class="daily-date">{{ formatDate(daily.dateNumber) }}</span>
<span class="daily-time">{{ formatCreateTime(daily.createTime) }}</span>
</div>
<div class="daily-stats">
<div class="daily-stat">
<span class="stat-label">Host:</span>
<span class="stat-value">${{ formatSalaryValue(daily.ownSalary) }}</span>
</div>
<div class="daily-stat">
<span class="stat-label">Agent:</span>
<span class="stat-value">${{ formatSalaryValue(daily.memberSalary) }}</span>
</div>
<div class="daily-stat">
<span class="stat-label">Total:</span>
<span class="stat-value total-daily-value">${{ formatSalaryValue(daily.totalSalry) }}</span>
</div>
<div class="prop-value">${{ prop.amount || 0 }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 每日详情 -->
<div class="daily-details-card">
<h3>Daily Details</h3>
<div v-if="billData?.target?.dailyTargets?.length > 0" class="daily-list">
<div
v-for="(daily, index) in billData.target.dailyTargets"
:key="index"
class="daily-item"
>
<div class="daily-header">
<span class="daily-date">{{ formatDate(daily.dateNumber) }}</span>
<span class="daily-time">{{ formatCreateTime(daily.createTime) }}</span>
</div>
<div class="daily-stats">
<div class="daily-stat">
<span class="stat-label">Gift:</span>
<span class="stat-value">{{ formatGiftValue(daily.acceptGiftValue) }}</span>
</div>
<div class="daily-stat">
<span class="stat-label">Time:</span>
<span class="stat-value">{{ formatOnlineTime(daily.onlineTime) }}</span>
</div>
<div class="daily-stat">
<span class="stat-label">Diamond:</span>
<span class="stat-value diamond-value">{{ formatDiamondValue(daily.acceptDiamondValue) }}</span>
</div>
</div>
<div v-else class="no-daily-data">
<div class="no-data-icon">💰</div>
<p>No daily salary data available</p>
</div>
</div>
<div v-else class="no-daily-data">
<div class="no-data-icon">📅</div>
<p>No daily data available</p>
</div>
</div>
<!-- 关闭按钮 -->
<div class="close-button-container">
<button class="close-btn" @click="$emit('close')">Close</button>
</div>
</div>
</template>
@ -117,12 +81,7 @@
<script setup>
import { ref, computed } from 'vue'
import {
formatGiftValue,
formatOnlineTime,
formatDiamondValue,
formatDateDisplay,
canReceivePropsReward,
receivePolicyPropsReward
formatDateDisplay
} from '../../api/teamBill.js'
const props = defineProps({
@ -132,9 +91,14 @@ const props = defineProps({
}
})
const emit = defineEmits(['close', 'propsReceived'])
const emit = defineEmits(['close'])
const receivingReward = ref(false)
//
const formatSalaryValue = (value) => {
if (!value || value === '0' || value === 0) return '0.00'
const numValue = parseFloat(value)
return numValue.toFixed(2)
}
//
const getStatusText = (status) => {
@ -173,30 +137,6 @@ const formatCreateTime = (timestamp) => {
minute: '2-digit'
})
}
//
const receivePropsReward = async () => {
if (!props.billData?.target?.id || receivingReward.value) {
return
}
try {
receivingReward.value = true
await receivePolicyPropsReward(props.billData.target.id)
//
if (props.billData.target.settlementResult) {
props.billData.target.settlementResult.propsReceived = true
}
emit('propsReceived')
} catch (error) {
console.error('Failed to receive props reward:', error)
alert('Failed to receive props reward. Please try again.')
} finally {
receivingReward.value = false
}
}
</script>
<style scoped>

View File

@ -18,18 +18,6 @@
</button>
</div>
<!-- 身份切换标签 -->
<div class="identity-tabs">
<button
v-for="identity in identities"
:key="identity.value"
@click="handleIdentityChange(identity.value)"
:class="['identity-tab', { active: currentIdentity === identity.value }]"
>
{{ identity.label }}
</button>
</div>
<!-- 账户状态标签 -->
<div class="account-tags">
<span class="tag agency-tag">🏢 Agency</span>
@ -140,7 +128,7 @@
<div class="team-member-card" @click="goToTeamMember">
<div class="card-content">
<span class="member-title">
Team Member
Team Member
<span v-if="loadingMemberCount">(Loading...)</span>
<span v-else>({{ teamMemberCount }})</span>
</span>
@ -272,9 +260,9 @@ const fetchTeamMemberCount = async () => {
loadingMemberCount.value = true
// 使teamId使ID
const teamId = '1927993836921233409'
const response = await get(`/team/members/count?id=${teamId}`)
if (response.status && typeof response.body === 'number') {
teamMemberCount.value = response.body
} else {

View File

@ -20,18 +20,6 @@
</button>
</div>
<!-- 身份切换标签 -->
<div class="identity-tabs">
<button
v-for="identity in identities"
:key="identity.value"
@click="handleIdentityChange(identity.value)"
:class="['identity-tab', { active: currentIdentity === identity.value }]"
>
{{ identity.label }}
</button>
</div>
<!-- 账户状态标签 -->
<div class="account-tags">
<span class="tag host-tag">👑 Host</span>
@ -129,7 +117,7 @@
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { getBankBalance } from '../api/wallet.js'
import { getBankBalance, getUserIdentity } from '../api/wallet.js'
const router = useRouter()
@ -137,7 +125,7 @@ const router = useRouter()
const currentIdentity = ref('host')
const loading = ref(false)
//
//
const identities = [
{ label: 'Host', value: 'host' },
{ label: 'Agency', value: 'agency' },
@ -207,23 +195,35 @@ const goToNotification = () => {
router.push('/host-setting')
}
//
const handleIdentityChange = (identity) => {
switch (identity) {
case 'agency':
router.push('/agency-center')
break
case 'coin-seller':
router.push('/coin-seller')
break
case 'host':
default:
//
currentIdentity.value = identity
break
//
const checkUserIdentity = async () => {
try {
const response = await getUserIdentity()
if (response && response.status && response.body) {
const { freightAgent, anchor, agent } = response.body
// freightAgent > anchor > agent
if (freightAgent) {
router.push('/coin-seller')
} else if (anchor) {
router.push('/agency-center')
} else if (agent) {
// Host
currentIdentity.value = 'host'
} else {
// Host
currentIdentity.value = 'host'
}
}
} catch (error) {
console.error('获取用户身份失败:', error)
// Host
currentIdentity.value = 'host'
}
}
const exchangeGoldCoins = () => {
router.push('/exchange-gold-coins')
}
@ -232,9 +232,10 @@ const transfer = () => {
router.push('/transfer')
}
//
//
onMounted(() => {
fetchBankBalance()
checkUserIdentity()
})
</script>
@ -345,6 +346,21 @@ onMounted(() => {
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;

View File

@ -9,8 +9,8 @@
</div>
<div v-else class="message-list">
<div
v-for="message in messages"
<div
v-for="message in messages"
:key="message.id"
class="message-item"
>
@ -47,6 +47,7 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { getApplyRecord } from '../api/teamBill.js'
const router = useRouter()
const loading = ref(false)
@ -77,15 +78,13 @@ const mockMessages = [
//
const fetchMessages = async () => {
loading.value = true
try {
// API
setTimeout(() => {
messages.value = mockMessages
loading.value = false
}, 800)
const response = await getApplyRecord('1954059793963393025', 'WAIT') //
// { data: [...] }
messages.value = response.data || response //
} catch (error) {
console.error('Failed to fetch messages:', error)
} finally {
loading.value = false
}
}

View File

@ -47,23 +47,18 @@
<div class="bill-details">
<div class="bill-row">
<span class="bill-label">Gift Value:</span>
<span class="bill-value">{{ formatGiftValue(bill.target?.acceptGiftValue) }}</span>
<span class="bill-label">Host Salary:</span>
<span class="bill-value">${{ formatSalaryValue(bill.target?.ownSalary) }}</span>
</div>
<div class="bill-row">
<span class="bill-label">Online Time:</span>
<span class="bill-value">{{ formatOnlineTime(bill.target?.onlineTime) }}</span>
<span class="bill-label">Agent Salary:</span>
<span class="bill-value">${{ formatSalaryValue(bill.target?.memberSalary) }}</span>
</div>
<div class="bill-row">
<span class="bill-label">Diamond:</span>
<span class="bill-value diamond-value">{{ formatDiamondValue(bill.target?.acceptDiamondValue) }}</span>
</div>
<div class="bill-row">
<span class="bill-label">Effective Days:</span>
<span class="bill-value">{{ bill.target?.effectiveDay || 0 }}</span>
<span class="bill-label">Total Salary:</span>
<span class="bill-value total-salary-value">${{ formatSalaryValue(bill.target?.totalSalry) }}</span>
</div>
</div>
@ -138,7 +133,7 @@ import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import TeamBillMore from '../components/team/TeamBillMore.vue'
import { getTeamMemberWork, formatGiftValue, formatOnlineTime, formatDateDisplay } from '../api/teamBill.js'
import { getTeamMemberWork, formatDateDisplay } from '../api/teamBill.js'
const router = useRouter()
const loading = ref(false)
@ -167,6 +162,13 @@ const fetchTeamMemberWork = async (userId) => {
}
}
//
const formatSalaryValue = (value) => {
if (!value || value === '0' || value === 0) return '0.00'
const numValue = parseFloat(value)
return numValue.toFixed(2)
}
//
const getStatusText = (status) => {
const statusMap = {
@ -187,18 +189,6 @@ const getStatusClass = (status) => {
return statusClassMap[status] || 'unknown'
}
//
const formatDiamondValue = (value) => {
if (!value || value === '0') return '0'
const numValue = parseInt(value)
if (numValue >= 1000000) {
return `${(numValue / 1000000).toFixed(1)}M`
} else if (numValue >= 1000) {
return `${(numValue / 1000).toFixed(1)}K`
}
return numValue.toString()
}
//
const formatDate = (dateNumber) => {
return formatDateDisplay(dateNumber)
@ -444,8 +434,9 @@ onMounted(() => {
color: #333;
}
.diamond-value {
color: #1E40AF !important;
.total-salary-value {
color: #059669 !important;
font-weight: 700;
}
.more-btn {