修改:余额显示为持仓加可用余额
This commit is contained in:
parent
fb74c20607
commit
2ceb735d48
@ -57,6 +57,7 @@ async function refreshUserData() {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
await userStore.fetchUserInfo()
|
await userStore.fetchUserInfo()
|
||||||
await userStore.fetchUsdcBalance()
|
await userStore.fetchUsdcBalance()
|
||||||
|
await userStore.fetchPositionsValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -132,7 +133,7 @@ watch(
|
|||||||
padding="4 12"
|
padding="4 12"
|
||||||
@click="$router.push('/wallet')"
|
@click="$router.push('/wallet')"
|
||||||
>
|
>
|
||||||
<span class="balance-text">${{ userStore.balance }}</span>
|
<span class="balance-text">${{ userStore.totalAssetValue }}</span>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
|||||||
@ -197,6 +197,7 @@ export interface MockPosition {
|
|||||||
bet: string
|
bet: string
|
||||||
toWin: string
|
toWin: string
|
||||||
value: string
|
value: string
|
||||||
|
currentValueNum?: number
|
||||||
valueChange?: string
|
valueChange?: string
|
||||||
valueChangePct?: string
|
valueChangePct?: string
|
||||||
valueChangeLoss?: boolean
|
valueChangeLoss?: boolean
|
||||||
@ -262,6 +263,7 @@ export const MOCK_WALLET_POSITIONS: MockPosition[] = [
|
|||||||
bet: '$4.95',
|
bet: '$4.95',
|
||||||
toWin: '$6.87',
|
toWin: '$6.87',
|
||||||
value: '$0.03',
|
value: '$0.03',
|
||||||
|
currentValueNum: 0.03,
|
||||||
valueChange: '-$4.91',
|
valueChange: '-$4.91',
|
||||||
valueChangePct: '99.31%',
|
valueChangePct: '99.31%',
|
||||||
valueChangeLoss: true,
|
valueChangeLoss: true,
|
||||||
@ -285,6 +287,7 @@ export const MOCK_WALLET_POSITIONS: MockPosition[] = [
|
|||||||
bet: '$0.99',
|
bet: '$0.99',
|
||||||
toWin: '$3.54',
|
toWin: '$3.54',
|
||||||
value: '$0.90',
|
value: '$0.90',
|
||||||
|
currentValueNum: 0.90,
|
||||||
valueChange: '-$0.09',
|
valueChange: '-$0.09',
|
||||||
valueChangePct: '8.9%',
|
valueChangePct: '8.9%',
|
||||||
valueChangeLoss: true,
|
valueChangeLoss: true,
|
||||||
@ -303,6 +306,7 @@ export const MOCK_WALLET_POSITIONS: MockPosition[] = [
|
|||||||
bet: '$30',
|
bet: '$30',
|
||||||
toWin: '$36',
|
toWin: '$36',
|
||||||
value: '$32',
|
value: '$32',
|
||||||
|
currentValueNum: 32.00,
|
||||||
valueChange: '+$2',
|
valueChange: '+$2',
|
||||||
valueChangePct: '6.67%',
|
valueChangePct: '6.67%',
|
||||||
valueChangeLoss: false,
|
valueChangeLoss: false,
|
||||||
|
|||||||
@ -142,6 +142,7 @@ export interface PositionDisplayItem {
|
|||||||
bet: string
|
bet: string
|
||||||
toWin: string
|
toWin: string
|
||||||
value: string
|
value: string
|
||||||
|
currentValueNum?: number
|
||||||
valueChange?: string
|
valueChange?: string
|
||||||
valueChangePct?: string
|
valueChangePct?: string
|
||||||
valueChangeLoss?: boolean
|
valueChangeLoss?: boolean
|
||||||
@ -220,18 +221,29 @@ export function mapPositionToDisplayItem(pos: ClobPositionItem): PositionDisplay
|
|||||||
|
|
||||||
// AVG:成本/份额;NOW:market.outcomePrices 中对应 outcome 的当前价
|
// AVG:成本/份额;NOW:market.outcomePrices 中对应 outcome 的当前价
|
||||||
let avgNow = '—'
|
let avgNow = '—'
|
||||||
|
let currentValueNum = costUsd
|
||||||
|
let change = 0
|
||||||
|
let changePct = 0
|
||||||
if (size > 0 && costUsd > 0) {
|
if (size > 0 && costUsd > 0) {
|
||||||
const avgCents = Math.round((costUsd / size) * 100)
|
const avgVal = costUsd / size
|
||||||
|
const avgCents = Math.round(avgVal * 100)
|
||||||
const avgStr = `${avgCents}¢`
|
const avgStr = `${avgCents}¢`
|
||||||
const prices = pos.market?.outcomePrices
|
const prices = pos.market?.outcomePrices
|
||||||
|
let nowVal: number | null = null
|
||||||
if (prices?.length) {
|
if (prices?.length) {
|
||||||
const idx = getOutcomePriceIndex(pos.market?.outcomes, outcome)
|
const idx = getOutcomePriceIndex(pos.market?.outcomes, outcome)
|
||||||
const nowVal = typeof prices[idx] === 'string' ? parseFloat(prices[idx] as string) : Number(prices[idx])
|
nowVal = typeof prices[idx] === 'string' ? parseFloat(prices[idx] as string) : Number(prices[idx])
|
||||||
const nowCents = Number.isFinite(nowVal) ? Math.round(nowVal * 100) : null
|
const nowCents = Number.isFinite(nowVal) ? Math.round(nowVal * 100) : null
|
||||||
avgNow = nowCents != null ? `${avgStr} → ${nowCents}¢` : avgStr
|
avgNow = nowCents != null ? `${avgStr} → ${nowCents}¢` : avgStr
|
||||||
} else {
|
} else {
|
||||||
avgNow = avgStr
|
avgNow = avgStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nowVal != null && Number.isFinite(nowVal)) {
|
||||||
|
currentValueNum = size * nowVal
|
||||||
|
}
|
||||||
|
change = currentValueNum - costUsd
|
||||||
|
changePct = costUsd > 0 ? (change / costUsd) * 100 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketID = String(pos.marketID ?? pos.market?.ID ?? '')
|
const marketID = String(pos.marketID ?? pos.market?.ID ?? '')
|
||||||
@ -252,6 +264,10 @@ export function mapPositionToDisplayItem(pos: ClobPositionItem): PositionDisplay
|
|||||||
bet,
|
bet,
|
||||||
toWin,
|
toWin,
|
||||||
value,
|
value,
|
||||||
|
currentValueNum,
|
||||||
|
valueChange: `${change >= 0 ? '+' : ''}$${Math.abs(change).toFixed(2)}`,
|
||||||
|
valueChangePct: `${changePct.toFixed(2)}%`,
|
||||||
|
valueChangeLoss: change < 0,
|
||||||
sellOutcome: outcome,
|
sellOutcome: outcome,
|
||||||
outcomeWord: outcome,
|
outcomeWord: outcome,
|
||||||
outcomeTag,
|
outcomeTag,
|
||||||
|
|||||||
@ -203,6 +203,7 @@
|
|||||||
"weeksAgo": "{n}w ago"
|
"weeksAgo": "{n}w ago"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
|
"availableAssets": "Available Assets",
|
||||||
"portfolio": "Portfolio",
|
"portfolio": "Portfolio",
|
||||||
"today": "Today",
|
"today": "Today",
|
||||||
"deposit": "Deposit",
|
"deposit": "Deposit",
|
||||||
|
|||||||
@ -203,6 +203,7 @@
|
|||||||
"weeksAgo": "{n}週間前"
|
"weeksAgo": "{n}週間前"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
|
"availableAssets": "利用可能資産",
|
||||||
"portfolio": "ポートフォリオ",
|
"portfolio": "ポートフォリオ",
|
||||||
"today": "今日",
|
"today": "今日",
|
||||||
"deposit": "入金",
|
"deposit": "入金",
|
||||||
|
|||||||
@ -203,6 +203,7 @@
|
|||||||
"weeksAgo": "{n}주 전"
|
"weeksAgo": "{n}주 전"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
|
"availableAssets": "사용 가능 자산",
|
||||||
"portfolio": "포트폴리오",
|
"portfolio": "포트폴리오",
|
||||||
"today": "오늘",
|
"today": "오늘",
|
||||||
"deposit": "입금",
|
"deposit": "입금",
|
||||||
|
|||||||
@ -203,6 +203,7 @@
|
|||||||
"weeksAgo": "{n}周前"
|
"weeksAgo": "{n}周前"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
|
"availableAssets": "可用资产",
|
||||||
"portfolio": "资产组合",
|
"portfolio": "资产组合",
|
||||||
"today": "今日",
|
"today": "今日",
|
||||||
"deposit": "入金",
|
"deposit": "入金",
|
||||||
|
|||||||
@ -203,7 +203,8 @@
|
|||||||
"weeksAgo": "{n}週前"
|
"weeksAgo": "{n}週前"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"portfolio": "資產組合",
|
"availableAssets": "可用資產",
|
||||||
|
"portfolio": "資產組合",
|
||||||
"today": "今日",
|
"today": "今日",
|
||||||
"deposit": "入金",
|
"deposit": "入金",
|
||||||
"withdraw": "提現",
|
"withdraw": "提現",
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import { getUsdcBalance, formatUsdcBalance, getUserInfo } from '@/api/user'
|
|||||||
import { getUserWsUrl, BASE_URL } from '@/api/request'
|
import { getUserWsUrl, BASE_URL } from '@/api/request'
|
||||||
import { jsonInBlacklist } from '@/api/jwt'
|
import { jsonInBlacklist } from '@/api/jwt'
|
||||||
import { UserSdk, type BalanceData, type PositionData } from '../../sdk/userSocket'
|
import { UserSdk, type BalanceData, type PositionData } from '../../sdk/userSocket'
|
||||||
|
import { getPositionList, mapPositionToDisplayItem } from '@/api/position'
|
||||||
|
import { USE_MOCK_WALLET } from '@/config/mock'
|
||||||
|
import { MOCK_WALLET_POSITIONS } from '@/api/mockData'
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
/** 用户 ID(API 可能返回 id 或 ID) */
|
/** 用户 ID(API 可能返回 id 或 ID) */
|
||||||
@ -82,6 +85,14 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
/** 钱包余额显示,如 "0.00",可从接口或 UserSocket 推送更新 */
|
/** 钱包余额显示,如 "0.00",可从接口或 UserSocket 推送更新 */
|
||||||
const balance = ref<string>('0.00')
|
const balance = ref<string>('0.00')
|
||||||
|
|
||||||
|
const positionsValue = ref<number>(0)
|
||||||
|
|
||||||
|
/** 总资产:余额 + 仓位当前价值 */
|
||||||
|
const totalAssetValue = computed(() => {
|
||||||
|
const bal = parseFloat(balance.value) || 0
|
||||||
|
return (bal + positionsValue.value).toFixed(2)
|
||||||
|
})
|
||||||
|
|
||||||
let userSdkRef: UserSdk | null = null
|
let userSdkRef: UserSdk | null = null
|
||||||
const positionUpdateCallbacks: ((data: PositionData & Record<string, unknown>) => void)[] = []
|
const positionUpdateCallbacks: ((data: PositionData & Record<string, unknown>) => void)[] = []
|
||||||
|
|
||||||
@ -109,6 +120,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
})
|
})
|
||||||
sdk.onPositionUpdate((data) => {
|
sdk.onPositionUpdate((data) => {
|
||||||
positionUpdateCallbacks.forEach((cb) => cb(data as PositionData & Record<string, unknown>))
|
positionUpdateCallbacks.forEach((cb) => cb(data as PositionData & Record<string, unknown>))
|
||||||
|
fetchPositionsValue()
|
||||||
})
|
})
|
||||||
sdk.onConnect(() => {})
|
sdk.onConnect(() => {})
|
||||||
sdk.onDisconnect(() => {})
|
sdk.onDisconnect(() => {})
|
||||||
@ -231,6 +243,33 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取仓位并更新仓位总价值 */
|
||||||
|
async function fetchPositionsValue() {
|
||||||
|
if (USE_MOCK_WALLET) {
|
||||||
|
positionsValue.value = MOCK_WALLET_POSITIONS.reduce((acc, p) => acc + (p.currentValueNum || 0), 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const headers = getAuthHeaders()
|
||||||
|
if (!headers) return
|
||||||
|
try {
|
||||||
|
const uid = user.value?.id ?? user.value?.ID
|
||||||
|
const userID = uid != null ? Number(uid) : undefined
|
||||||
|
if (!userID || !Number.isFinite(userID)) return
|
||||||
|
|
||||||
|
const res = await getPositionList(
|
||||||
|
{ page: 1, pageSize: 1000, userID },
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
|
if (res.code === 0 || res.code === 200) {
|
||||||
|
const list = res.data?.list ?? []
|
||||||
|
const posList = list.map(mapPositionToDisplayItem)
|
||||||
|
positionsValue.value = posList.reduce((acc, p) => acc + (p.currentValueNum || 0), 0)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[fetchPositionsValue] 请求失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
user,
|
user,
|
||||||
@ -238,12 +277,15 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
displayName,
|
displayName,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
balance,
|
balance,
|
||||||
|
positionsValue,
|
||||||
|
totalAssetValue,
|
||||||
setUser,
|
setUser,
|
||||||
logout,
|
logout,
|
||||||
clearLocalSession,
|
clearLocalSession,
|
||||||
getAuthHeaders,
|
getAuthHeaders,
|
||||||
fetchUsdcBalance,
|
fetchUsdcBalance,
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
|
fetchPositionsValue,
|
||||||
connectUserSocket,
|
connectUserSocket,
|
||||||
disconnectUserSocket,
|
disconnectUserSocket,
|
||||||
onPositionUpdate,
|
onPositionUpdate,
|
||||||
|
|||||||
@ -46,7 +46,9 @@
|
|||||||
{{ t('profile.walletDetail') }} >
|
{{ t('profile.walletDetail') }} >
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="wallet-balance">${{ totalBalance }}</div>
|
<div class="wallet-balance">
|
||||||
|
${{ totalBalance }}
|
||||||
|
</div>
|
||||||
<div class="wallet-sub">
|
<div class="wallet-sub">
|
||||||
{{ t('profile.walletSub', { available: availableBalance, frozen: frozenBalance }) }}
|
{{ t('profile.walletSub', { available: availableBalance, frozen: frozenBalance }) }}
|
||||||
</div>
|
</div>
|
||||||
@ -244,11 +246,11 @@ const userNameRaw = computed(() => {
|
|||||||
})
|
})
|
||||||
/** 显示用:统一取 nickName(userStore.displayName) */
|
/** 显示用:统一取 nickName(userStore.displayName) */
|
||||||
const displayName = computed(() => userStore.displayName || t('profile.defaultName'))
|
const displayName = computed(() => userStore.displayName || t('profile.defaultName'))
|
||||||
const userIdText = computed(() => {
|
// const userIdText = computed(() => {
|
||||||
const uid = rawUser.value.id ?? rawUser.value.ID
|
// const uid = rawUser.value.id ?? rawUser.value.ID
|
||||||
if (uid == null || uid === '') return '--'
|
// if (uid == null || uid === '') return '--'
|
||||||
return String(uid)
|
// return String(uid)
|
||||||
})
|
// })
|
||||||
const avatarImage = computed(() => userStore.avatarUrl || '')
|
const avatarImage = computed(() => userStore.avatarUrl || '')
|
||||||
const avatarText = computed(() => {
|
const avatarText = computed(() => {
|
||||||
const first = displayName.value.trim().charAt(0)
|
const first = displayName.value.trim().charAt(0)
|
||||||
@ -265,7 +267,7 @@ const hasVip = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
const userTag = computed(() => (hasVip.value ? t('profile.vipTrader') : t('profile.trader')))
|
const userTag = computed(() => (hasVip.value ? t('profile.vipTrader') : t('profile.trader')))
|
||||||
const totalBalance = computed(() => userStore.balance || '0.00')
|
const totalBalance = computed(() => userStore.totalAssetValue || '0.00')
|
||||||
const availableBalance = computed(() => {
|
const availableBalance = computed(() => {
|
||||||
const val = readStringFromUser(['availableBalance', 'available', 'walletAvailable'])
|
const val = readStringFromUser(['availableBalance', 'available', 'walletAvailable'])
|
||||||
return val || totalBalance.value
|
return val || totalBalance.value
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="wallet-container">
|
<v-container class="wallet-container">
|
||||||
<div class="wallet-mobile-frame">
|
<div class="wallet-mobile-frame">
|
||||||
<div class="wallet-mobile-header">{{ t('wallet.walletTitle') }}</div>
|
<div class="wallet-mobile-header">{{ t('wallet.portfolio') }}</div>
|
||||||
<v-card class="wallet-card portfolio-card design-tu-asset" elevation="0" rounded="lg">
|
<v-card class="wallet-card portfolio-card design-tu-asset" elevation="0" rounded="lg">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title">{{ t('wallet.portfolio') }}</span>
|
<span class="card-title">{{ t('wallet.portfolio') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-value">${{ portfolioBalance }}</div>
|
<div class="card-value">
|
||||||
|
<div class="main-balance">${{ portfolioBalance }}</div>
|
||||||
|
<div class="available-balance">({{ t('wallet.availableAssets') }}: ${{ userStore.balance }})</div>
|
||||||
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
<div class="card-actions design-tu-actions">
|
<div class="card-actions design-tu-actions">
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -588,7 +591,8 @@ const { mobile } = useDisplay()
|
|||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { formatAuthError } = useAuthError()
|
const { formatAuthError } = useAuthError()
|
||||||
const localeStore = useLocaleStore()
|
const localeStore = useLocaleStore()
|
||||||
const portfolioBalance = computed(() => userStore.balance)
|
const portfolioBalance = computed(() => userStore.totalAssetValue)
|
||||||
|
const positionsValueText = computed(() => userStore.positionsValue.toFixed(2))
|
||||||
const profitLoss = ref('0.00')
|
const profitLoss = ref('0.00')
|
||||||
const plRange = ref('ALL')
|
const plRange = ref('ALL')
|
||||||
const plTimeRanges = computed(() => [
|
const plTimeRanges = computed(() => [
|
||||||
@ -1640,10 +1644,24 @@ async function submitAuthorize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-value {
|
.card-value {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-balance {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: inherit;
|
||||||
margin-bottom: 0;
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-balance {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.8;
|
||||||
|
color: inherit;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-value-row {
|
.card-value-row {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user