预发布版本

This commit is contained in:
tianfeng 2025-08-15 13:12:36 +08:00
parent 11c24e9d0c
commit 8a546e1b18
9 changed files with 512 additions and 100 deletions

View File

@ -22,7 +22,7 @@ export const getTeamMembers = async (teamId) => {
*/ */
export const deleteMember = async (memberId) => { export const deleteMember = async (memberId) => {
try { try {
const response = await post('/team/member/remove', { const response = await post('/team-member/client/removeMemberTeam', {
memberId: memberId memberId: memberId
}) })
return response return response

View File

@ -55,10 +55,64 @@ export function userBankSearchUserProfile(type, account) {
/** /**
* 获取用户身份信息 * 获取用户身份信息
* @param {string} userId - 用户ID
* @returns {Promise} 返回用户身份权限信息 * @returns {Promise} 返回用户身份权限信息
*/ */
export function getUserIdentity() { export function getUserIdentity(userId) {
return get('/app/h5/1911661502909562881/identity') return get(`/app/h5/${userId}/identity`)
}
/**
* 获取团队入口信息
* @returns {Promise} 返回团队和用户信息
*/
export function getTeamEntry() {
return get('/team/entry')
}
/**
* 校验金币销售商权限
* @returns {Promise} 返回是否具有销售商权限
*/
export function checkFreightDealer() {
return get('/wallet/freight/seller/check/dealer')
}
/**
* 获取金币销售商余额
* @returns {Promise} 返回销售商金币余额
*/
export function getFreightBalance() {
return get('/wallet/freight/balance')
}
/**
* 搜索用户
* @param {string} account - 用户账号
* @returns {Promise} 返回用户信息
*/
export function searchFreightUser(account) {
return get(`/wallet/freight/search-user?account=${account}`)
}
/**
* 金币充值
* @param {Object} data - 充值数据
* @param {number} data.acceptUserId - 接收用户ID
* @param {number} data.quantity - 充值数量
* @returns {Promise} 返回剩余可用余额
*/
export function freightRecharge(data) {
return post('/wallet/freight/ship', data)
}
/**
* 获取金币交易流水
* @param {string} userId - 用户ID
* @returns {Promise} 返回交易流水记录
*/
export function getFreightWaterFlow(userId) {
return get(`/wallet/freight/balance/running/water/client/flowByUserId?userId=${userId}`)
} }
/** /**

View File

@ -35,7 +35,7 @@ export function connectApplication(renderFun) {
window.renderData = renderFun window.renderData = renderFun
// 兼容历史版本 // 兼容历史版本
window.getIosAccessOriginParam = renderFun window.getIosAccessOriginParam = renderFun
try { try {
if (ios()) { if (ios()) {
// iOS可能需要特殊处理这里先使用通用方法 // iOS可能需要特殊处理这里先使用通用方法
@ -55,10 +55,10 @@ export function connectApplication(renderFun) {
// 开发环境可以提供模拟数据 // 开发环境可以提供模拟数据
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
const mockAccess = JSON.stringify({ const mockAccess = JSON.stringify({
'Authorization': 'Bearer mock-token-for-development', 'Authorization': 'Bearer F77126B08E13987C3AD88B6015C5B777.djIlM0ExOTU0MDU5NzkzOTYzMzkzMDI1JTNBTElLRUklM0ExNzU3NzYzODU5NzE1JTNBMTc1NTE3MTg1OTcxNQ==',
'Req-Lang': 'en', 'Req-Lang': 'en',
'Req-App-Intel': 'build=1.0;version=2.1;channel=official;Req-Imei=mock-imei', 'Req-App-Intel': 'build=1.0;version=2.1;channel=official;Req-Imei=mock-imei',
'Req-Sys-Origin': 'origin=web;child=browser' 'Req-Sys-Origin': 'origin=LIKEI;originChild=LIKEI'
}) })
setTimeout(() => renderFun(mockAccess), 100) setTimeout(() => renderFun(mockAccess), 100)
} }
@ -102,13 +102,13 @@ export function parseHeader(head) {
/** /**
* 解析键值对字符串 (: "key1=value1;key2=value2") * 解析键值对字符串 (: "key1=value1;key2=value2")
* @param {string} values * @param {string} values
* @returns {Object} * @returns {Object}
*/ */
function getKeyValObj(values) { function getKeyValObj(values) {
const keyValBody = {} const keyValBody = {}
if (!values) return keyValBody if (!values) return keyValBody
const valueArray = values.split(';') const valueArray = values.split(';')
for (let index = 0; index < valueArray.length; index++) { for (let index = 0; index < valueArray.length; index++) {
const keyVal = valueArray[index] const keyVal = valueArray[index]
@ -150,7 +150,7 @@ export function setHttpHeaders(headerInfo) {
}).catch(error => { }).catch(error => {
console.error('Failed to update HTTP headers:', error) console.error('Failed to update HTTP headers:', error)
}) })
console.log('🔑 Authorization token set:', headerInfo.authorization) console.log('🔑 Authorization token set:', headerInfo.authorization)
console.log('🌍 Language set:', headerInfo.reqLang) console.log('🌍 Language set:', headerInfo.reqLang)
console.log('📱 App info:', { console.log('📱 App info:', {

View File

@ -21,7 +21,6 @@
<!-- 账户状态标签 --> <!-- 账户状态标签 -->
<div class="account-tags"> <div class="account-tags">
<span class="tag agency-tag">🏢 Agency</span> <span class="tag agency-tag">🏢 Agency</span>
<span class="tag salary-tag">💰 Salary</span>
</div> </div>
</div> </div>
@ -636,9 +635,7 @@ onMounted(() => {
.team-bill-card .card-content { .team-bill-card .card-content {
padding: 16px; padding: 16px;
display: flex; position: relative;
flex-direction: column;
gap: 12px;
} }
/* 通用卡片内容样式 */ /* 通用卡片内容样式 */
@ -650,20 +647,37 @@ onMounted(() => {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #1F2937; color: #1F2937;
margin-bottom: 8px;
} }
.card-link { .card-link {
position: absolute;
top: 16px;
right: 16px;
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
color: #6B7280; gap: 4px;
color: #8B5CF6;
font-size: 14px; font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: color 0.2s;
}
.card-link:hover {
color: #7C3AED;
}
.card-link .arrow {
font-size: 16px;
font-weight: bold;
} }
.card-details { .card-details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
margin-top: 12px;
} }
.detail-row { .detail-row {

View File

@ -40,7 +40,14 @@
> >
<div class="user-info"> <div class="user-info">
<div class="user-avatar"> <div class="user-avatar">
<span>{{ user.name.charAt(0) }}</span> <img
v-if="user.userAvatar"
:src="user.userAvatar"
:alt="user.name"
class="avatar-image"
@error="(e) => e.target.style.display = 'none'"
/>
<span v-if="!user.userAvatar" class="avatar-text">{{ user.name.charAt(0) }}</span>
</div> </div>
<div class="user-details"> <div class="user-details">
<h4>{{ user.name }}</h4> <h4>{{ user.name }}</h4>
@ -81,6 +88,7 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue' import MobileHeader from '../components/MobileHeader.vue'
import { setSelectedUser } from '../utils/coinSellerStore.js' import { setSelectedUser } from '../utils/coinSellerStore.js'
import { searchFreightUser } from '../api/wallet.js'
const router = useRouter() const router = useRouter()
const searchQuery = ref('') const searchQuery = ref('')
@ -104,7 +112,7 @@ const mockUsers = [
] ]
// //
const performSearch = () => { const performSearch = async () => {
if (!searchQuery.value.trim()) { if (!searchQuery.value.trim()) {
searchResults.value = [] searchResults.value = []
return return
@ -112,15 +120,28 @@ const performSearch = () => {
isSearching.value = true isSearching.value = true
// try {
setTimeout(() => { const response = await searchFreightUser(searchQuery.value.trim())
const query = searchQuery.value.toLowerCase()
searchResults.value = mockUsers.filter(user => if (response && response.status && response.body && response.body.userProfile) {
user.name.toLowerCase().includes(query) || const profile = response.body.userProfile
user.id.includes(query) searchResults.value = [{
) id: profile.account || profile.id,
userId: profile.id, // ID
name: profile.userNickname || 'User',
userNickname: profile.userNickname,
account: profile.account,
userAvatar: profile.userAvatar
}]
} else {
searchResults.value = []
}
} catch (error) {
console.error('搜索用户失败:', error)
searchResults.value = []
} finally {
isSearching.value = false isSearching.value = false
}, 500) }
} }
// //
@ -148,7 +169,11 @@ const selectUser = (user) => {
// //
setSelectedUser({ setSelectedUser({
id: user.id, id: user.id,
name: user.name userId: user.userId, // ID
name: user.name,
userNickname: user.userNickname,
account: user.account,
userAvatar: user.userAvatar
}) })
console.log('Selected user:', user) console.log('Selected user:', user)
@ -267,6 +292,21 @@ const selectUser = (user) => {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
margin-right: 12px; margin-right: 12px;
overflow: hidden;
position: relative;
}
.avatar-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 25px;
}
.avatar-text {
color: white;
font-size: 18px;
font-weight: 600;
} }
.user-details h4 { .user-details h4 {

View File

@ -2,25 +2,39 @@
<div class="coin-seller gradient-background-circles"> <div class="coin-seller gradient-background-circles">
<MobileHeader title="Coin Seller" /> <MobileHeader title="Coin Seller" />
<div class="content"> <!-- 权限校验加载状态 -->
<div v-if="checkingAccess" class="loading-container">
<div class="loading-spinner"></div>
<p>正在校验权限...</p>
</div>
<!-- 主要内容 -->
<div v-else-if="dealerAccess" class="content">
<!-- 用户信息卡片 --> <!-- 用户信息卡片 -->
<div class="user-card"> <div class="user-card">
<div class="user-info"> <div class="user-info">
<div class="avatar"> <div class="avatar">
<span>{{ userInfo.name.charAt(0) }}</span> <img
v-if="userInfo.userAvatar"
:src="userInfo.userAvatar"
:alt="userInfo.userNickname || userInfo.name"
class="avatar-image"
@error="handleAvatarError"
/>
<span v-else class="avatar-text">{{ (userInfo.userNickname || userInfo.name).charAt(0) }}</span>
</div> </div>
<div class="user-details"> <div class="user-details">
<h3>{{ userInfo.name }}</h3> <h3>{{ userInfo.userNickname || userInfo.name }}</h3>
<p>ID: {{ userInfo.id }}</p> <p>ID: {{ userInfo.account || userInfo.id }}</p>
</div> </div>
<button class="settings-btn" @click="goToSettings"> <button class="settings-btn" @click="goToSettings">
<span></span> <span>📈</span>
</button> </button>
</div> </div>
<!-- 账户状态标签 --> <!-- 账户状态标签 -->
<div class="account-tags"> <div class="account-tags">
<span class="tag seller-tag">🪙 Seller</span> <span class="tag seller-tag">🪙 Salary</span>
</div> </div>
</div> </div>
@ -29,7 +43,8 @@
<h3>My coins:</h3> <h3>My coins:</h3>
<div class="coins-display"> <div class="coins-display">
<span class="coin-icon">🪙</span> <span class="coin-icon">🪙</span>
<span class="coins-amount">{{ coinsAmount }}</span> <span v-if="loadingBalance" class="coins-loading">Loading...</span>
<span v-else class="coins-amount">{{ coinsAmount }}</span>
</div> </div>
</div> </div>
@ -67,10 +82,10 @@
</div> </div>
<button <button
class="recharge-btn" class="recharge-btn"
:disabled="!canRecharge" :disabled="!canRecharge || isRecharging"
@click="rechargeNow" @click="rechargeNow"
> >
Recharge now {{ isRecharging ? 'Processing...' : 'Recharge now' }}
</button> </button>
</div> </div>
</div> </div>
@ -82,17 +97,26 @@ import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue' import MobileHeader from '../components/MobileHeader.vue'
import { getSelectedUser, clearSelectedUser } from '../utils/coinSellerStore.js' import { getSelectedUser, clearSelectedUser } from '../utils/coinSellerStore.js'
import { checkFreightDealer, getTeamEntry, getFreightBalance, searchFreightUser, freightRecharge } from '../api/wallet.js'
const router = useRouter() const router = useRouter()
// //
const userInfo = ref({ const userInfo = ref({
name: 'User1', name: 'User1',
id: '1234567890' id: '1234567890',
userNickname: 'User1',
account: '1234567890',
userAvatar: null
}) })
//
const dealerAccess = ref(false)
const checkingAccess = ref(true)
// //
const coinsAmount = ref('123456478') const coinsAmount = ref('0')
const loadingBalance = ref(false)
// //
const selectedUser = ref({ const selectedUser = ref({
@ -100,6 +124,73 @@ const selectedUser = ref({
name: '' name: ''
}) })
//
const checkDealerAccess = async () => {
try {
checkingAccess.value = true
const response = await checkFreightDealer()
if (response && response.status !== undefined) {
dealerAccess.value = response.body === true
if (!dealerAccess.value) {
//
alert('您没有金币销售商权限')
router.go(-1)
return
}
}
} catch (error) {
console.error('权限校验失败:', error)
dealerAccess.value = false
router.go(-1)
} finally {
checkingAccess.value = false
}
}
//
const fetchUserInfo = async () => {
try {
const response = await getTeamEntry()
if (response && response.status && response.body && response.body.memberProfile) {
const profile = response.body.memberProfile
userInfo.value = {
name: profile.userNickname || 'User',
id: profile.account || profile.id,
userNickname: profile.userNickname,
account: profile.account,
userAvatar: profile.userAvatar
}
}
} catch (error) {
console.error('获取用户信息失败:', error)
}
}
//
const fetchFreightBalance = async () => {
try {
loadingBalance.value = true
const response = await getFreightBalance()
if (response && response.status && response.body !== undefined) {
coinsAmount.value = response.body.toString()
}
} catch (error) {
console.error('获取金币余额失败:', error)
} finally {
loadingBalance.value = false
}
}
//
const handleAvatarError = () => {
console.log('头像加载失败,使用默认显示')
userInfo.value.userAvatar = null
}
// //
const initializeUser = () => { const initializeUser = () => {
const savedUser = getSelectedUser() const savedUser = getSelectedUser()
@ -110,10 +201,11 @@ const initializeUser = () => {
// //
const rechargeAmount = ref('') const rechargeAmount = ref('')
const isRecharging = ref(false)
// //
const canRecharge = computed(() => { const canRecharge = computed(() => {
return selectedUser.value.id && rechargeAmount.value && parseFloat(rechargeAmount.value) > 0 return selectedUser.value.id && rechargeAmount.value && parseFloat(rechargeAmount.value) > 0 && !isRecharging.value
}) })
// //
@ -127,19 +219,64 @@ const searchUser = () => {
} }
// //
const rechargeNow = () => { const rechargeNow = async () => {
if (!canRecharge.value) return if (!canRecharge.value || isRecharging.value) return
alert(`Recharge ${rechargeAmount.value} coins to ${selectedUser.value.name}`) const amount = parseFloat(rechargeAmount.value)
if (amount <= 0) {
alert('请输入正确的充值金额')
return
}
// // ID
rechargeAmount.value = '' const acceptUserId = selectedUser.value.userId || selectedUser.value.id
// if (!acceptUserId) {
alert('无法获取用户ID请重新选择用户')
return
}
try {
isRecharging.value = true
const rechargeData = {
acceptUserId: parseInt(acceptUserId),
quantity: amount
}
const response = await freightRecharge(rechargeData)
if (response && response.status) {
//
if (response.body !== undefined) {
coinsAmount.value = response.body.toString()
}
alert(`成功向 ${selectedUser.value.name} 充值 ${amount} 金币`)
//
rechargeAmount.value = ''
} else {
alert('充值失败,请重试')
}
} catch (error) {
console.error('充值失败:', error)
alert('充值失败,请检查网络后重试')
} finally {
isRecharging.value = false
}
} }
// //
onMounted(() => { onMounted(async () => {
initializeUser() //
await checkDealerAccess()
//
if (dealerAccess.value) {
await fetchUserInfo()
await fetchFreightBalance()
initializeUser()
}
}) })
</script> </script>
@ -148,6 +285,30 @@ onMounted(() => {
font-family: -apple-system, BlinkMacSystemFont, sans-serif; font-family: -apple-system, BlinkMacSystemFont, sans-serif;
} }
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 20px;
color: #666;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #F59E0B;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.content { .content {
padding: 16px; padding: 16px;
position: relative; position: relative;
@ -181,6 +342,21 @@ onMounted(() => {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin-right: 12px; margin-right: 12px;
overflow: hidden;
position: relative;
}
.avatar-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 25px;
}
.avatar-text {
color: white;
font-size: 20px;
font-weight: 600;
} }
.user-details { .user-details {
@ -258,6 +434,12 @@ onMounted(() => {
color: #F59E0B; color: #F59E0B;
} }
.coins-loading {
font-size: 16px;
color: #9ca3af;
font-style: italic;
}
/* 用户搜索部分 */ /* 用户搜索部分 */
.user-section { .user-section {
background-color: white; background-color: white;

View File

@ -19,11 +19,11 @@
<div class="user-info"> <div class="user-info">
<div class="avatar"> <div class="avatar">
<span>{{ userInfo.name.charAt(0) }}</span> <span>{{ (userInfo.userNickname || userInfo.name).charAt(0) }}</span>
</div> </div>
<div class="user-details"> <div class="user-details">
<h3>{{ userInfo.name }}</h3> <h3>{{ userInfo.userNickname || userInfo.name }}</h3>
<p>ID: {{ userInfo.id }}</p> <p>ID: {{ userInfo.account || userInfo.id }}</p>
<!-- 显示系统信息 --> <!-- 显示系统信息 -->
<p v-if="headerInfo.sysOrigin" class="system-info"> <p v-if="headerInfo.sysOrigin" class="system-info">
{{ headerInfo.sysOrigin }} {{ headerInfo.sysOrigin }}
@ -132,7 +132,7 @@
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue' import MobileHeader from '../components/MobileHeader.vue'
import { getBankBalance, getUserIdentity } from '../api/wallet.js' import { getBankBalance, getUserIdentity, getTeamEntry } from '../api/wallet.js'
import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js' import { connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders, isInApp } from '../utils/appBridge.js'
const router = useRouter() const router = useRouter()
@ -142,6 +142,8 @@ const currentIdentity = ref('host')
const loading = ref(false) const loading = ref(false)
const appConnected = ref(false) const appConnected = ref(false)
const headerInfo = ref({}) const headerInfo = ref({})
const userProfile = ref(null)
const identityChecked = ref(false)
// //
const identities = [ const identities = [
@ -151,16 +153,16 @@ const identities = [
] ]
// APP // APP
const connectToApp = () => { const connectToApp = async () => {
if (!isInApp()) { if (!isInApp()) {
console.warn('Not running in APP environment') console.warn('Not running in APP environment')
// APP // APP
appConnected.value = true appConnected.value = true
checkUserIdentity() await fetchTeamEntry()
return return
} }
connectApplication(access => { connectApplication(async (access) => {
console.log('Received access from APP:', access) console.log('Received access from APP:', access)
const result = parseAccessOrigin(access) const result = parseAccessOrigin(access)
@ -176,8 +178,8 @@ const connectToApp = () => {
console.log('APP connection established:', headerInfo.value) console.log('APP connection established:', headerInfo.value)
// //
checkUserIdentity() await fetchTeamEntry()
fetchBankBalance() fetchBankBalance()
} else { } else {
console.error('Failed to parse access origin:', result.error) console.error('Failed to parse access origin:', result.error)
@ -213,7 +215,9 @@ const fetchBankBalance = async () => {
// //
const userInfo = reactive({ const userInfo = reactive({
name: 'User1', name: 'User1',
id: '1234567890' id: '1234567890',
userNickname: 'User1',
account: '1234567890'
}) })
// //
@ -250,24 +254,70 @@ const goToNotification = () => {
router.push('/host-setting') router.push('/host-setting')
} }
// //
const checkUserIdentity = async () => { const fetchTeamEntry = async () => {
try { try {
const response = await getUserIdentity() const response = await getTeamEntry()
if (response && response.status && response.body) {
userProfile.value = response.body
//
if (response.body.memberProfile) {
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)
}
return null
}
//
const checkUserIdentity = async (userId) => {
if (!userId) {
console.error('用户ID不存在')
return
}
//
if (identityChecked.value) {
console.log('身份已检查,跳过')
return
}
try {
identityChecked.value = true
const response = await getUserIdentity(userId)
if (response && response.status && response.body) { if (response && response.status && response.body) {
const { freightAgent, anchor, agent } = response.body const { freightAgent, anchor, agent } = response.body
console.log('用户身份权限:', { freightAgent, anchor, agent })
// freightAgent > anchor > agent // freightAgent > anchor > agent
if (freightAgent) { if (freightAgent) {
router.push('/coin-seller') console.log('跳转到 coin-seller')
router.replace('/coin-seller')
} else if (anchor) { } else if (anchor) {
router.push('/agency-center') console.log('跳转到 agency-center')
router.replace('/agency-center')
} else if (agent) { } else if (agent) {
// Host // Host
console.log('保持在 host-center')
currentIdentity.value = 'host' currentIdentity.value = 'host'
} else { } else {
// Host // Host
console.log('无特殊权限,保持在 host-center')
currentIdentity.value = 'host' currentIdentity.value = 'host'
} }
} }
@ -288,9 +338,9 @@ const transfer = () => {
} }
// APP // APP
onMounted(() => { onMounted(async () => {
// APP // APP
connectToApp() await connectToApp()
// APP // APP
if (!isInApp()) { if (!isInApp()) {

View File

@ -25,7 +25,8 @@
</div> </div>
</div> </div>
<div class="record-amount"> <div class="record-amount">
<span class="coins-amount">{{ record.amount }} coins</span> <span :class="['coins-amount', record.type === 0 ? 'positive' : 'negative']">{{ record.amount }} coins</span>
<span class="transaction-type">{{ getTransactionType(record.type, record.origin) }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -43,10 +44,12 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue' import MobileHeader from '../components/MobileHeader.vue'
import { getFreightWaterFlow, getTeamEntry } from '../api/wallet.js'
const router = useRouter() const router = useRouter()
const loading = ref(false) const loading = ref(false)
const records = ref([]) const records = ref([])
const currentUserId = ref(null)
// //
const mockRecords = [ const mockRecords = [
@ -73,24 +76,93 @@ const mockRecords = [
} }
] ]
// ID
const fetchCurrentUserId = async () => {
try {
const response = await getTeamEntry()
if (response && response.status && response.body && response.body.memberProfile) {
currentUserId.value = response.body.memberProfile.id
return response.body.memberProfile.id
}
} catch (error) {
console.error('获取用户ID失败:', error)
}
return null
}
//
const formatTime = (timestamp) => {
const date = new Date(timestamp)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}.${month}.${day} ${hours}:${minutes}:${seconds}`
}
//
const getTransactionType = (type, origin) => {
if (type === 0) {
return '进货' //
} else if (type === 1) {
return '出货' //
}
return origin || '未知'
}
//
const getAmountDisplay = (type, quantity) => {
const prefix = type === 0 ? '+' : '-'
return `${prefix}${quantity}`
}
// //
const fetchRecords = async () => { const fetchRecords = async () => {
loading.value = true loading.value = true
try { try {
// API // ID
setTimeout(() => { const userId = currentUserId.value || await fetchCurrentUserId()
records.value = mockRecords
loading.value = false if (!userId) {
}, 800) console.error('无法获取用户ID')
records.value = []
return
}
const response = await getFreightWaterFlow(userId)
if (response && response.status && response.body) {
//
records.value = response.body.map(item => ({
id: item.id,
userId: item.acceptUserId,
userName: `User${item.acceptUserId.toString().slice(-6)}...`, // 6
amount: getAmountDisplay(item.type, item.quantity),
time: formatTime(item.createTime),
type: item.type,
quantity: item.quantity,
balance: item.balance,
origin: item.origin,
remark: item.remark,
rechargeType: item.rechargeType
}))
} else {
records.value = []
}
} catch (error) { } catch (error) {
console.error('Failed to fetch records:', error) console.error('获取记录失败:', error)
records.value = []
} finally {
loading.value = false loading.value = false
} }
} }
onMounted(() => { onMounted(async () => {
fetchRecords() await fetchRecords()
}) })
</script> </script>
@ -203,7 +275,20 @@ onMounted(() => {
.coins-amount { .coins-amount {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: #EF4444; }
.coins-amount.positive {
color: #10B981; /* 绿色表示进货/入账 */
}
.coins-amount.negative {
color: #EF4444; /* 红色表示出货/出账 */
}
.transaction-type {
font-size: 12px;
color: #9ca3af;
margin-top: 2px;
} }
/* 空状态 */ /* 空状态 */

View File

@ -4,19 +4,6 @@
<div class="content"> <div class="content">
<!-- 成员资料信息 --> <!-- 成员资料信息 -->
<div v-if="memberProfile" class="member-profile-card">
<div class="member-info">
<div class="avatar-wrapper">
<img :src="memberProfile.userAvatar" class="member-avatar" alt="Avatar" />
<span class="country-flag">{{ memberProfile.countryCode }}</span>
</div>
<div class="member-details">
<div class="member-name">{{ memberProfile.userNickname }}</div>
<div class="member-id">ID: {{ memberProfile.account }}</div>
<div class="member-status">{{ memberProfile.accountStatusName }}</div>
</div>
</div>
</div>
<!-- 工作报告标题 --> <!-- 工作报告标题 -->
<div class="work-report-title"> <div class="work-report-title">
@ -32,8 +19,8 @@
<!-- 账单列表 --> <!-- 账单列表 -->
<div v-else-if="billList.length > 0" class="bill-list"> <div v-else-if="billList.length > 0" class="bill-list">
<div <div
v-for="bill in billList" v-for="bill in billList"
:key="bill.id" :key="bill.id"
class="bill-item" class="bill-item"
@click="showBillDetail(bill)" @click="showBillDetail(bill)"
@ -44,13 +31,13 @@
{{ getStatusText(bill.status) }} {{ getStatusText(bill.status) }}
</span> </span>
</div> </div>
<div class="bill-details"> <div class="bill-details">
<div class="bill-row"> <div class="bill-row">
<span class="bill-label">Host Salary:</span> <span class="bill-label">Host Salary:</span>
<span class="bill-value">${{ formatSalaryValue(bill.target?.ownSalary) }}</span> <span class="bill-value">${{ formatSalaryValue(bill.target?.ownSalary) }}</span>
</div> </div>
<div class="bill-row"> <div class="bill-row">
<span class="bill-label">Agent Salary:</span> <span class="bill-label">Agent Salary:</span>
<span class="bill-value">${{ formatSalaryValue(bill.target?.memberSalary) }}</span> <span class="bill-value">${{ formatSalaryValue(bill.target?.memberSalary) }}</span>
@ -61,7 +48,7 @@
<span class="bill-value total-salary-value">${{ formatSalaryValue(bill.target?.totalSalry) }}</span> <span class="bill-value total-salary-value">${{ formatSalaryValue(bill.target?.totalSalry) }}</span>
</div> </div>
</div> </div>
<div class="more-btn"> <div class="more-btn">
<span>More</span> <span>More</span>
<span class="arrow">></span> <span class="arrow">></span>
@ -83,14 +70,14 @@
<h3>Work Report Help</h3> <h3>Work Report Help</h3>
<button class="close-btn" @click="closeHelpModal">×</button> <button class="close-btn" @click="closeHelpModal">×</button>
</div> </div>
<div class="help-modal-body"> <div class="help-modal-body">
<div class="help-section"> <div class="help-section">
<p class="help-description"> <p class="help-description">
Below is the work report of the team members. Click "More" to view detailed daily work records. Below is the work report of the team members. Click "More" to view detailed daily work records.
</p> </p>
</div> </div>
<div class="help-section"> <div class="help-section">
<h4>Status Description:</h4> <h4>Status Description:</h4>
<div class="status-list"> <div class="status-list">
@ -118,8 +105,8 @@
<!-- 账单详情模态框 (使用 TeamBillMore 组件) --> <!-- 账单详情模态框 (使用 TeamBillMore 组件) -->
<div v-if="showDetailModal" class="modal-overlay"> <div v-if="showDetailModal" class="modal-overlay">
<div class="detail-modal-wrapper"> <div class="detail-modal-wrapper">
<TeamBillMore <TeamBillMore
:billData="selectedBill" :billData="selectedBill"
@close="closeDetailModal" @close="closeDetailModal"
@propsReceived="handlePropsReceived" @propsReceived="handlePropsReceived"
/> />
@ -150,7 +137,7 @@ const fetchTeamMemberWork = async (userId) => {
try { try {
loading.value = true loading.value = true
const response = await getTeamMemberWork(userId) const response = await getTeamMemberWork(userId)
if (response.status && response.body) { if (response.status && response.body) {
memberProfile.value = response.body.memberProfile memberProfile.value = response.body.memberProfile
billList.value = response.body.targets || [] billList.value = response.body.targets || []
@ -173,7 +160,7 @@ const formatSalaryValue = (value) => {
const getStatusText = (status) => { const getStatusText = (status) => {
const statusMap = { const statusMap = {
'UNPAID': 'In Progress', 'UNPAID': 'In Progress',
'HANG_UP': 'Pending', 'HANG_UP': 'Pending',
'PAID': 'Completed' 'PAID': 'Completed'
} }
return statusMap[status] || status return statusMap[status] || status
@ -632,4 +619,4 @@ onMounted(() => {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
} }
} }
</style> </style>