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