Compare commits
No commits in common. "c976333b7244d47d53970920e0e23f92ba9d87b0" and "7bc5831edd7d760bd40d354275f2db014843bb17" have entirely different histories.
c976333b72
...
7bc5831edd
File diff suppressed because it is too large
Load Diff
@ -64,49 +64,9 @@ export interface PmEventMarketItem {
|
|||||||
clobTokenIds?: string[]
|
clobTokenIds?: string[]
|
||||||
endDate?: string
|
endDate?: string
|
||||||
volume?: number
|
volume?: number
|
||||||
/** 为 true 时表示市场已关闭/已结算,不再交易 */
|
|
||||||
closed?: boolean
|
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 市场是否已关闭(不再接受下单) */
|
|
||||||
export function isPmMarketClosed(m: PmEventMarketItem | null | undefined): boolean {
|
|
||||||
return m?.closed === true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已结算市场的结果角标:取 outcomePrices 中概率最高项对应 outcomes 文案(兼容 0–1 或 1–100)。
|
|
||||||
*/
|
|
||||||
export function getPmMarketSettledWinner(m: PmEventMarketItem): {
|
|
||||||
label: string
|
|
||||||
winnerIndex: number
|
|
||||||
} | null {
|
|
||||||
if (!isPmMarketClosed(m)) return null
|
|
||||||
const prices = m.outcomePrices
|
|
||||||
const outcomes = m.outcomes
|
|
||||||
if (!prices?.length) {
|
|
||||||
return { label: outcomes?.[0] ?? '—', winnerIndex: 0 }
|
|
||||||
}
|
|
||||||
let bestIdx = 0
|
|
||||||
let bestProb = -1
|
|
||||||
for (let i = 0; i < prices.length; i++) {
|
|
||||||
const p = parseFloat(String(prices[i]))
|
|
||||||
if (!Number.isFinite(p)) continue
|
|
||||||
const prob = p > 1 ? Math.min(1, p / 100) : p
|
|
||||||
if (prob > bestProb) {
|
|
||||||
bestProb = prob
|
|
||||||
bestIdx = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bestProb < 0) {
|
|
||||||
return { label: outcomes?.[0] ?? '—', winnerIndex: 0 }
|
|
||||||
}
|
|
||||||
const label =
|
|
||||||
outcomes?.[bestIdx] ??
|
|
||||||
(bestIdx === 0 ? 'Yes' : bestIdx === 1 ? 'No' : `#${bestIdx + 1}`)
|
|
||||||
return { label, winnerIndex: bestIdx }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 从市场项取 marketId,兼容 ID / id */
|
/** 从市场项取 marketId,兼容 ID / id */
|
||||||
export function getMarketId(m: PmEventMarketItem | null | undefined): string | undefined {
|
export function getMarketId(m: PmEventMarketItem | null | undefined): string | undefined {
|
||||||
if (!m) return undefined
|
if (!m) return undefined
|
||||||
@ -277,24 +237,36 @@ export function readTradeRouteEventSlug(obj: unknown): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TradeDetailRouteInput {
|
export interface TradeDetailRouteInput {
|
||||||
/** 事件数字 ID;无 slug 时用作路径段 */
|
/** 事件数字 ID(与首页 MarketCard :id 一致),优先用于路径参数 */
|
||||||
eventId?: string | number | null
|
eventId?: string | number | null
|
||||||
/** 事件 slug,优先作为路径参数(详情页仅用其请求 findPmEvent) */
|
/** 事件 slug;无 eventId 时用作路径;路径为数字 ID 时可作为 query.slug 传给 findPmEvent */
|
||||||
eventSlug?: string | null
|
eventSlug?: string | null
|
||||||
|
/** 市场行 ID,对应详情页 query.marketId */
|
||||||
|
marketId?: string | null
|
||||||
|
/** 详情页标题回显 query.title */
|
||||||
|
title?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转详情:`/trade-detail/:id` 仅传路径参数,无 query;优先 slug,否则数字 ID。
|
* 构造与 MarketCard、EventMarkets → trade-detail 一致的 router.push 参数。
|
||||||
|
* 路径:`/trade-detail/:id`,id 为事件数字 ID 或事件 slug。
|
||||||
*/
|
*/
|
||||||
export function buildTradeDetailPushOptions(
|
export function buildTradeDetailPushOptions(
|
||||||
input: TradeDetailRouteInput,
|
input: TradeDetailRouteInput,
|
||||||
): { name: 'trade-detail'; params: { id: string } } | null {
|
): { name: 'trade-detail'; params: { id: string }; query: Record<string, string> } | null {
|
||||||
const slug = input.eventSlug?.trim() || ''
|
|
||||||
const eid =
|
const eid =
|
||||||
input.eventId != null && String(input.eventId).trim() !== '' ? String(input.eventId).trim() : ''
|
input.eventId != null && String(input.eventId).trim() !== '' ? String(input.eventId).trim() : ''
|
||||||
const pathId = slug || eid
|
const slugOnly = input.eventSlug?.trim() || ''
|
||||||
if (!pathId) return null
|
if (!eid && !slugOnly) return null
|
||||||
return { name: 'trade-detail', params: { id: pathId } }
|
const pathId = eid || slugOnly
|
||||||
|
const numId = parseInt(eid, 10)
|
||||||
|
const isNumericEventPath =
|
||||||
|
eid !== '' && Number.isFinite(numId) && String(numId) === eid && numId >= 1
|
||||||
|
const query: Record<string, string> = {}
|
||||||
|
if (input.title?.trim()) query.title = input.title.trim()
|
||||||
|
if (input.marketId?.trim()) query.marketId = input.marketId.trim()
|
||||||
|
if (isNumericEventPath && slugOnly) query.slug = slugOnly
|
||||||
|
return { name: 'trade-detail', params: { id: pathId }, query }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 多选项卡片中单个选项(用于左右滑动切换) */
|
/** 多选项卡片中单个选项(用于左右滑动切换) */
|
||||||
|
|||||||
@ -68,12 +68,10 @@ export function usdcToAmount(displayAmount: number): number {
|
|||||||
return Math.round(displayAmount * USDC_SCALE)
|
return Math.round(displayAmount * USDC_SCALE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 提现状态:审核中、提现成功(含 processed)、审核不通过、提现失败 */
|
/** 提现状态:审核中、提现成功、审核不通过、提现失败 */
|
||||||
export const WITHDRAW_STATUS = {
|
export const WITHDRAW_STATUS = {
|
||||||
PENDING: 'pending',
|
PENDING: 'pending',
|
||||||
SUCCESS: 'success',
|
SUCCESS: 'success',
|
||||||
/** 与 success 同义,后端已处理完成 */
|
|
||||||
PROCESSED: 'processed',
|
|
||||||
REJECTED: 'rejected',
|
REJECTED: 'rejected',
|
||||||
FAILED: 'failed',
|
FAILED: 'failed',
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@ -232,13 +232,26 @@ const semiProgressColor = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const navigateToDetail = () => {
|
const navigateToDetail = () => {
|
||||||
const segment = (props.slug?.trim() || props.id || '').trim()
|
|
||||||
if (!segment) return
|
|
||||||
if (props.displayType === 'multi' && (props.outcomes?.length ?? 0) > 1) {
|
if (props.displayType === 'multi' && (props.outcomes?.length ?? 0) > 1) {
|
||||||
router.push({ name: 'event-markets', params: { id: segment } })
|
router.push({
|
||||||
|
path: `/event/${props.id}/markets`,
|
||||||
|
query: { ...(props.slug && { slug: props.slug }) },
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
router.push({ name: 'trade-detail', params: { id: segment } })
|
router.push({
|
||||||
|
path: `/trade-detail/${props.id}`,
|
||||||
|
query: {
|
||||||
|
title: props.marketTitle,
|
||||||
|
imageUrl: props.imageUrl || undefined,
|
||||||
|
category: props.category || undefined,
|
||||||
|
marketInfo: props.marketInfo || undefined,
|
||||||
|
expiresAt: props.expiresAt || undefined,
|
||||||
|
chance: String(props.chanceValue),
|
||||||
|
...(props.marketId && { marketId: props.marketId }),
|
||||||
|
...(props.slug && { slug: props.slug }),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function openTradeSingle(side: 'yes' | 'no') {
|
function openTradeSingle(side: 'yes' | 'no') {
|
||||||
|
|||||||
@ -77,10 +77,6 @@
|
|||||||
"pleaseSelectMarket": "Please select a market (with clobTokenIds)",
|
"pleaseSelectMarket": "Please select a market (with clobTokenIds)",
|
||||||
"userError": "User info error",
|
"userError": "User info error",
|
||||||
"orderFailed": "Order failed",
|
"orderFailed": "Order failed",
|
||||||
"marketClosedTitle": "Market is closed",
|
|
||||||
"marketClosedDesc": "This market has settled. Trading and new orders are not available.",
|
|
||||||
"marketClosedOutcome": "Outcome: {outcome}",
|
|
||||||
"marketClosedBanner": "This market is closed and has settled.",
|
|
||||||
"expiration": {
|
"expiration": {
|
||||||
"5m": "5m",
|
"5m": "5m",
|
||||||
"15m": "15m",
|
"15m": "15m",
|
||||||
@ -110,8 +106,7 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"priceZero": "0¢",
|
"priceZero": "0¢",
|
||||||
"moreActions": "More actions",
|
"moreActions": "More actions"
|
||||||
"settledSubMarketsTitle": "Sub-markets (settled)"
|
|
||||||
},
|
},
|
||||||
"searchPage": {
|
"searchPage": {
|
||||||
"title": "Search",
|
"title": "Search",
|
||||||
|
|||||||
@ -77,10 +77,6 @@
|
|||||||
"pleaseSelectMarket": "市場を選択してください(clobTokenIds が必要)",
|
"pleaseSelectMarket": "市場を選択してください(clobTokenIds が必要)",
|
||||||
"userError": "ユーザー情報エラー",
|
"userError": "ユーザー情報エラー",
|
||||||
"orderFailed": "注文に失敗しました",
|
"orderFailed": "注文に失敗しました",
|
||||||
"marketClosedTitle": "マーケットは閉鎖されています",
|
|
||||||
"marketClosedDesc": "このマーケットは決済済みです。取引や新規注文はできません。",
|
|
||||||
"marketClosedOutcome": "結果:{outcome}",
|
|
||||||
"marketClosedBanner": "このマーケットは閉鎖され決済されています。",
|
|
||||||
"expiration": {
|
"expiration": {
|
||||||
"5m": "5分",
|
"5m": "5分",
|
||||||
"15m": "15分",
|
"15m": "15分",
|
||||||
@ -110,8 +106,7 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"priceZero": "0¢",
|
"priceZero": "0¢",
|
||||||
"moreActions": "その他の操作",
|
"moreActions": "その他の操作"
|
||||||
"settledSubMarketsTitle": "サブマーケット(決済済み)"
|
|
||||||
},
|
},
|
||||||
"searchPage": {
|
"searchPage": {
|
||||||
"title": "検索",
|
"title": "検索",
|
||||||
|
|||||||
@ -77,10 +77,6 @@
|
|||||||
"pleaseSelectMarket": "시장을 선택하세요 (clobTokenIds 필요)",
|
"pleaseSelectMarket": "시장을 선택하세요 (clobTokenIds 필요)",
|
||||||
"userError": "사용자 정보 오류",
|
"userError": "사용자 정보 오류",
|
||||||
"orderFailed": "주문 실패",
|
"orderFailed": "주문 실패",
|
||||||
"marketClosedTitle": "마켓이 종료되었습니다",
|
|
||||||
"marketClosedDesc": "이 마켓은 정산되었으며 거래와 신규 주문을 받지 않습니다.",
|
|
||||||
"marketClosedOutcome": "결과: {outcome}",
|
|
||||||
"marketClosedBanner": "이 마켓은 종료되어 정산되었습니다.",
|
|
||||||
"expiration": {
|
"expiration": {
|
||||||
"5m": "5분",
|
"5m": "5분",
|
||||||
"15m": "15분",
|
"15m": "15분",
|
||||||
@ -110,8 +106,7 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"priceZero": "0¢",
|
"priceZero": "0¢",
|
||||||
"moreActions": "더보기",
|
"moreActions": "더보기"
|
||||||
"settledSubMarketsTitle": "하위 마켓(정산 완료)"
|
|
||||||
},
|
},
|
||||||
"searchPage": {
|
"searchPage": {
|
||||||
"title": "검색",
|
"title": "검색",
|
||||||
|
|||||||
@ -77,10 +77,6 @@
|
|||||||
"pleaseSelectMarket": "请先选择市场(需包含 clobTokenIds)",
|
"pleaseSelectMarket": "请先选择市场(需包含 clobTokenIds)",
|
||||||
"userError": "用户信息异常",
|
"userError": "用户信息异常",
|
||||||
"orderFailed": "下单失败",
|
"orderFailed": "下单失败",
|
||||||
"marketClosedTitle": "市场已关闭",
|
|
||||||
"marketClosedDesc": "本市场已结算,不再接受交易与下单。",
|
|
||||||
"marketClosedOutcome": "结果:{outcome}",
|
|
||||||
"marketClosedBanner": "该市场已关闭并完成结算。",
|
|
||||||
"expiration": {
|
"expiration": {
|
||||||
"5m": "5分钟",
|
"5m": "5分钟",
|
||||||
"15m": "15分钟",
|
"15m": "15分钟",
|
||||||
@ -110,8 +106,7 @@
|
|||||||
"yes": "是",
|
"yes": "是",
|
||||||
"no": "否",
|
"no": "否",
|
||||||
"priceZero": "0¢",
|
"priceZero": "0¢",
|
||||||
"moreActions": "更多操作",
|
"moreActions": "更多操作"
|
||||||
"settledSubMarketsTitle": "子市场(已结算)"
|
|
||||||
},
|
},
|
||||||
"searchPage": {
|
"searchPage": {
|
||||||
"title": "搜索",
|
"title": "搜索",
|
||||||
|
|||||||
@ -77,10 +77,6 @@
|
|||||||
"pleaseSelectMarket": "請先選擇市場(需包含 clobTokenIds)",
|
"pleaseSelectMarket": "請先選擇市場(需包含 clobTokenIds)",
|
||||||
"userError": "用戶資訊異常",
|
"userError": "用戶資訊異常",
|
||||||
"orderFailed": "下單失敗",
|
"orderFailed": "下單失敗",
|
||||||
"marketClosedTitle": "市場已關閉",
|
|
||||||
"marketClosedDesc": "本市場已結算,不再接受交易與下單。",
|
|
||||||
"marketClosedOutcome": "結果:{outcome}",
|
|
||||||
"marketClosedBanner": "該市場已關閉並完成結算。",
|
|
||||||
"expiration": {
|
"expiration": {
|
||||||
"5m": "5分鐘",
|
"5m": "5分鐘",
|
||||||
"15m": "15分鐘",
|
"15m": "15分鐘",
|
||||||
@ -110,8 +106,7 @@
|
|||||||
"yes": "是",
|
"yes": "是",
|
||||||
"no": "否",
|
"no": "否",
|
||||||
"priceZero": "0¢",
|
"priceZero": "0¢",
|
||||||
"moreActions": "更多操作",
|
"moreActions": "更多操作"
|
||||||
"settledSubMarketsTitle": "子市場(已結算)"
|
|
||||||
},
|
},
|
||||||
"searchPage": {
|
"searchPage": {
|
||||||
"title": "搜尋",
|
"title": "搜尋",
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { HOME_MAIN_SCROLL_STORAGE_KEY } from './scrollKeys'
|
|
||||||
import Home from '../views/Home.vue'
|
import Home from '../views/Home.vue'
|
||||||
import Trade from '../views/Trade.vue'
|
import Trade from '../views/Trade.vue'
|
||||||
import Login from '../views/Login.vue'
|
import Login from '../views/Login.vue'
|
||||||
@ -68,24 +67,6 @@ const router = createRouter({
|
|||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
const el = document.querySelector('[data-main-scroll]')
|
const el = document.querySelector('[data-main-scroll]')
|
||||||
if (el) {
|
if (el) {
|
||||||
const shouldRestoreHomeScroll =
|
|
||||||
to.name === 'home' &&
|
|
||||||
from?.name != null &&
|
|
||||||
(from.name === 'trade-detail' || from.name === 'event-markets')
|
|
||||||
|
|
||||||
if (shouldRestoreHomeScroll) {
|
|
||||||
const raw = sessionStorage.getItem(HOME_MAIN_SCROLL_STORAGE_KEY)
|
|
||||||
const top = raw != null ? parseInt(raw, 10) : NaN
|
|
||||||
if (Number.isFinite(top) && top >= 0) {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
el.scrollTo({ top, left: 0 })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedPosition && from?.name) {
|
if (savedPosition && from?.name) {
|
||||||
el.scrollTo({ top: savedPosition.top, left: savedPosition.left ?? 0 })
|
el.scrollTo({ top: savedPosition.top, left: savedPosition.left ?? 0 })
|
||||||
return
|
return
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
/** 离开首页时写入 [data-main-scroll].scrollTop,从详情/多市场返回首页时恢复 */
|
|
||||||
export const HOME_MAIN_SCROLL_STORAGE_KEY = 'pcv-home-main-scroll-top'
|
|
||||||
@ -94,17 +94,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- 可交易市场列表 -->
|
<!-- 市场列表 -->
|
||||||
<v-card
|
<v-card class="markets-list-card" elevation="0" rounded="lg">
|
||||||
v-if="activeMarkets.length > 0"
|
|
||||||
class="markets-list-card"
|
|
||||||
elevation="0"
|
|
||||||
rounded="lg"
|
|
||||||
>
|
|
||||||
<div class="markets-list">
|
<div class="markets-list">
|
||||||
<div
|
<div
|
||||||
v-for="(market, index) in activeMarkets"
|
v-for="(market, index) in markets"
|
||||||
:key="market.ID ?? market.id ?? index"
|
:key="market.ID ?? index"
|
||||||
class="market-row"
|
class="market-row"
|
||||||
:class="{ selected: selectedMarketIndex === index }"
|
:class="{ selected: selectedMarketIndex === index }"
|
||||||
@click="goToTradeDetail(market)"
|
@click="goToTradeDetail(market)"
|
||||||
@ -139,46 +134,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- 已结算子市场(与 design vb1xr 一致:标题 + 行内结果角标) -->
|
|
||||||
<v-card
|
|
||||||
v-if="closedMarkets.length > 0"
|
|
||||||
class="markets-settled-card"
|
|
||||||
elevation="0"
|
|
||||||
rounded="lg"
|
|
||||||
>
|
|
||||||
<div class="markets-settled-head">
|
|
||||||
{{ t('eventMarkets.settledSubMarketsTitle') }}
|
|
||||||
</div>
|
|
||||||
<div class="markets-settled-list">
|
|
||||||
<div
|
|
||||||
v-for="(market, index) in closedMarkets"
|
|
||||||
:key="market.ID ?? market.id ?? `c-${index}`"
|
|
||||||
class="markets-settled-row"
|
|
||||||
@click="goToTradeDetail(market)"
|
|
||||||
>
|
|
||||||
<div class="markets-settled-row-text">
|
|
||||||
<span class="markets-settled-question">{{
|
|
||||||
market.question || t('eventMarkets.marketPlaceholder')
|
|
||||||
}}</span>
|
|
||||||
<span class="markets-settled-vol">{{ formatVolumeLine(market.volume) }}</span>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="settledWinnerPill(market)"
|
|
||||||
class="markets-settled-pill"
|
|
||||||
:class="settledPillClass(market)"
|
|
||||||
>
|
|
||||||
{{ settledWinnerPill(market)!.label }}
|
|
||||||
</span>
|
|
||||||
<v-icon class="markets-settled-chevron" size="18">mdi-chevron-right</v-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<!-- 右侧:购买组件(桌面端显示;移动端用底部栏+弹窗) -->
|
<!-- 右侧:购买组件(桌面端显示;移动端用底部栏+弹窗) -->
|
||||||
<v-col v-if="!isMobile" cols="12" class="trade-col">
|
<v-col v-if="!isMobile" cols="12" class="trade-col">
|
||||||
<div v-if="activeMarkets.length > 0" class="trade-sidebar">
|
<div v-if="markets.length > 0" class="trade-sidebar">
|
||||||
<TradeComponent
|
<TradeComponent
|
||||||
:market="tradeMarketPayload"
|
:market="tradeMarketPayload"
|
||||||
:initial-option="tradeInitialOption"
|
:initial-option="tradeInitialOption"
|
||||||
@ -189,7 +149,7 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- 移动端:单 market 时显示固定底部 Yes/No 栏 + 三点菜单(Merge/Split) -->
|
<!-- 移动端:单 market 时显示固定底部 Yes/No 栏 + 三点菜单(Merge/Split) -->
|
||||||
<template v-if="isMobile && activeMarkets.length === 1">
|
<template v-if="isMobile && markets.length === 1">
|
||||||
<div class="mobile-trade-bar-spacer" aria-hidden="true"></div>
|
<div class="mobile-trade-bar-spacer" aria-hidden="true"></div>
|
||||||
<div class="mobile-trade-bar">
|
<div class="mobile-trade-bar">
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -240,7 +200,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- 移动端交易弹窗:单 market 与多 market 均需(多 market 时通过列表 Buy Yes/No 打开) -->
|
<!-- 移动端交易弹窗:单 market 与多 market 均需(多 market 时通过列表 Buy Yes/No 打开) -->
|
||||||
<template v-if="isMobile && activeMarkets.length > 0">
|
<template v-if="isMobile && markets.length > 0">
|
||||||
<v-bottom-sheet v-model="tradeSheetOpen" content-class="event-markets-trade-sheet">
|
<v-bottom-sheet v-model="tradeSheetOpen" content-class="event-markets-trade-sheet">
|
||||||
<TradeComponent
|
<TradeComponent
|
||||||
v-if="tradeSheetRenderContent"
|
v-if="tradeSheetRenderContent"
|
||||||
@ -277,8 +237,6 @@ import {
|
|||||||
findPmEvent,
|
findPmEvent,
|
||||||
getMarketId,
|
getMarketId,
|
||||||
getClobTokenId,
|
getClobTokenId,
|
||||||
getPmMarketSettledWinner,
|
|
||||||
isPmMarketClosed,
|
|
||||||
type FindPmEventParams,
|
type FindPmEventParams,
|
||||||
type PmEventListItem,
|
type PmEventListItem,
|
||||||
type PmEventMarketItem,
|
type PmEventMarketItem,
|
||||||
@ -324,13 +282,9 @@ const markets = computed(() => {
|
|||||||
const list = eventDetail.value?.markets ?? []
|
const list = eventDetail.value?.markets ?? []
|
||||||
return list.length > 0 ? list : []
|
return list.length > 0 ? list : []
|
||||||
})
|
})
|
||||||
/** 仍可交易的市场(closed !== true) */
|
const selectedMarket = computed(() => markets.value[selectedMarketIndex.value] ?? null)
|
||||||
const activeMarkets = computed(() => markets.value.filter((m) => !isPmMarketClosed(m)))
|
/** 移动端底部栏显示的市场(选中项或首个),仅在 markets.length > 0 时使用 */
|
||||||
/** 已结算/已关闭的子市场 */
|
const barMarket = computed(() => selectedMarket.value ?? markets.value[0])
|
||||||
const closedMarkets = computed(() => markets.value.filter((m) => isPmMarketClosed(m)))
|
|
||||||
const selectedMarket = computed(() => activeMarkets.value[selectedMarketIndex.value] ?? null)
|
|
||||||
/** 移动端底部栏显示的市场(选中项或首个),仅在有可交易市场时使用 */
|
|
||||||
const barMarket = computed(() => selectedMarket.value ?? activeMarkets.value[0])
|
|
||||||
/** 传给购买组件的市场数据(当前选中的市场) */
|
/** 传给购买组件的市场数据(当前选中的市场) */
|
||||||
const tradeMarketPayload = computed(() => {
|
const tradeMarketPayload = computed(() => {
|
||||||
const m = selectedMarket.value
|
const m = selectedMarket.value
|
||||||
@ -362,26 +316,6 @@ function formatVolume(volume: number | undefined): string {
|
|||||||
return `$${Math.round(volume)} Vol.`
|
return `$${Math.round(volume)} Vol.`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 已结算列表 secondary 行:Vol. $12.4M / k(与设计稿一致) */
|
|
||||||
function formatVolumeLine(volume: number | undefined): string {
|
|
||||||
if (volume == null || !Number.isFinite(volume)) return 'Vol. —'
|
|
||||||
if (volume >= 1_000_000) return `Vol. $${(volume / 1_000_000).toFixed(1)}M`
|
|
||||||
if (volume >= 1000) return `Vol. $${(volume / 1000).toFixed(1)}k`
|
|
||||||
return `Vol. $${Math.round(volume)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function settledWinnerPill(market: PmEventMarketItem) {
|
|
||||||
return getPmMarketSettledWinner(market)
|
|
||||||
}
|
|
||||||
|
|
||||||
function settledPillClass(market: PmEventMarketItem): string {
|
|
||||||
const w = getPmMarketSettledWinner(market)
|
|
||||||
if (!w) return 'markets-settled-pill--neutral'
|
|
||||||
if (w.winnerIndex === 0) return 'markets-settled-pill--yes'
|
|
||||||
if (w.winnerIndex === 1) return 'markets-settled-pill--no'
|
|
||||||
return 'markets-settled-pill--neutral'
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatExpiresAt(endDate: string | undefined): string {
|
function formatExpiresAt(endDate: string | undefined): string {
|
||||||
if (!endDate) return ''
|
if (!endDate) return ''
|
||||||
try {
|
try {
|
||||||
@ -647,13 +581,20 @@ function noPrice(market: PmEventMarketItem): string {
|
|||||||
return `${Math.round(p * 100)}¢`
|
return `${Math.round(p * 100)}¢`
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToTradeDetail(_market: PmEventMarketItem, _side?: 'yes' | 'no') {
|
function goToTradeDetail(market: PmEventMarketItem, side?: 'yes' | 'no') {
|
||||||
void _market
|
const eventId = route.params.id
|
||||||
void _side
|
const marketId = market.ID != null ? String(market.ID) : undefined
|
||||||
const slug = eventDetail.value?.slug?.trim()
|
router.push({
|
||||||
const eventKey = slug || String(route.params.id ?? '').trim()
|
path: `/trade-detail/${eventId}`,
|
||||||
if (!eventKey) return
|
query: {
|
||||||
router.push({ name: 'trade-detail', params: { id: eventKey } })
|
title: market.question ?? eventDetail.value?.title,
|
||||||
|
marketId,
|
||||||
|
marketInfo: formatVolume(market.volume),
|
||||||
|
chance: String(marketChance(market)),
|
||||||
|
...(side && { side }),
|
||||||
|
...(eventDetail.value?.slug && { slug: eventDetail.value.slug }),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadEventDetail() {
|
async function loadEventDetail() {
|
||||||
@ -666,9 +607,10 @@ async function loadEventDetail() {
|
|||||||
}
|
}
|
||||||
const numId = parseInt(idStr, 10)
|
const numId = parseInt(idStr, 10)
|
||||||
const isNumericId = Number.isFinite(numId) && String(numId) === idStr && numId >= 1
|
const isNumericId = Number.isFinite(numId) && String(numId) === idStr && numId >= 1
|
||||||
|
const slugFromQuery = (route.query.slug as string)?.trim()
|
||||||
const params: FindPmEventParams = {
|
const params: FindPmEventParams = {
|
||||||
id: isNumericId ? numId : undefined,
|
id: isNumericId ? numId : undefined,
|
||||||
slug: isNumericId ? undefined : idStr,
|
slug: isNumericId ? slugFromQuery || undefined : idStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
detailError.value = null
|
detailError.value = null
|
||||||
@ -704,16 +646,6 @@ async function loadEventDetail() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
activeMarkets,
|
|
||||||
(list) => {
|
|
||||||
if (selectedMarketIndex.value >= list.length) {
|
|
||||||
selectedMarketIndex.value = Math.max(0, list.length - 1)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadEventDetail()
|
loadEventDetail()
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
@ -1150,96 +1082,6 @@ watch(
|
|||||||
color: #cc0000 !important;
|
color: #cc0000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 已结算子市场列表(Pencil vb1xr) */
|
|
||||||
.markets-settled-card {
|
|
||||||
margin-top: 16px;
|
|
||||||
border: 1px solid #e7e7e7;
|
|
||||||
border-radius: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: none;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-head {
|
|
||||||
padding: 14px 16px 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #111827;
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 14px 16px;
|
|
||||||
border-bottom: 1px solid #f3f4f6;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-row:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-row:hover {
|
|
||||||
background-color: #f9fafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-row-text {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-question {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #111827;
|
|
||||||
line-height: 1.35;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-vol {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-pill {
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 999px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-pill--yes {
|
|
||||||
background: #d1fae5;
|
|
||||||
color: #065f46;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-pill--no {
|
|
||||||
background: #fee2e2;
|
|
||||||
color: #991b1b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-pill--neutral {
|
|
||||||
background: #f3f4f6;
|
|
||||||
color: #374151;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markets-settled-chevron {
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: #d1d5db;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移动端底部交易栏 */
|
/* 移动端底部交易栏 */
|
||||||
.mobile-trade-bar-spacer {
|
.mobile-trade-bar-spacer {
|
||||||
height: 72px;
|
height: 72px;
|
||||||
|
|||||||
@ -227,8 +227,6 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
watch,
|
watch,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { onBeforeRouteLeave } from 'vue-router'
|
|
||||||
import { HOME_MAIN_SCROLL_STORAGE_KEY } from '../router/scrollKeys'
|
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import MarketCard from '../components/MarketCard.vue'
|
import MarketCard from '../components/MarketCard.vue'
|
||||||
import TradeComponent from '../components/TradeComponent.vue'
|
import TradeComponent from '../components/TradeComponent.vue'
|
||||||
@ -646,13 +644,6 @@ function checkScrollLoad() {
|
|||||||
if (scrollHeight - scrollTop - clientHeight < SCROLL_LOAD_THRESHOLD) loadMore()
|
if (scrollHeight - scrollTop - clientHeight < SCROLL_LOAD_THRESHOLD) loadMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeRouteLeave(() => {
|
|
||||||
const el = getMainScrollEl()
|
|
||||||
if (el) {
|
|
||||||
sessionStorage.setItem(HOME_MAIN_SCROLL_STORAGE_KEY, String((el as HTMLElement).scrollTop))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.initialSearchExpanded) expandSearch()
|
if (props.initialSearchExpanded) expandSearch()
|
||||||
loadCategory()
|
loadCategory()
|
||||||
|
|||||||
@ -111,8 +111,9 @@ function formatTradingFee(rate: number): string {
|
|||||||
/** 与接口 needDeposit / accumulated 同一套口径:大额按 USDC 6 位小数,否则按美元数值 */
|
/** 与接口 needDeposit / accumulated 同一套口径:大额按 USDC 6 位小数,否则按美元数值 */
|
||||||
function depositRawToUsd(raw: number): number {
|
function depositRawToUsd(raw: number): number {
|
||||||
if (!Number.isFinite(raw) || raw <= 0) return 0
|
if (!Number.isFinite(raw) || raw <= 0) return 0
|
||||||
return raw / 1_000_000
|
return raw >= 1_000_000_000 ? raw / 1_000_000 : raw
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseUserAccumulatedUsd(user: Record<string, unknown>): number {
|
function parseUserAccumulatedUsd(user: Record<string, unknown>): number {
|
||||||
const v = user.accumulated ?? user.Accumulated
|
const v = user.accumulated ?? user.Accumulated
|
||||||
if (typeof v === 'number' && Number.isFinite(v)) return depositRawToUsd(v)
|
if (typeof v === 'number' && Number.isFinite(v)) return depositRawToUsd(v)
|
||||||
|
|||||||
@ -302,15 +302,27 @@ function openResult(item: SearchResultItem) {
|
|||||||
const card = mapEventItemToCard(item.raw)
|
const card = mapEventItemToCard(item.raw)
|
||||||
if (!card.id) return
|
if (!card.id) return
|
||||||
|
|
||||||
const segment = (card.slug?.trim() || card.id || '').trim()
|
|
||||||
if (!segment) return
|
|
||||||
|
|
||||||
if (card.displayType === 'multi' && (card.outcomes?.length ?? 0) > 1) {
|
if (card.displayType === 'multi' && (card.outcomes?.length ?? 0) > 1) {
|
||||||
router.push({ name: 'event-markets', params: { id: segment } })
|
router.push({
|
||||||
|
path: `/event/${card.id}/markets`,
|
||||||
|
query: { ...(card.slug && { slug: card.slug }) },
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push({ name: 'trade-detail', params: { id: segment } })
|
router.push({
|
||||||
|
path: `/trade-detail/${card.id}`,
|
||||||
|
query: {
|
||||||
|
title: card.marketTitle,
|
||||||
|
imageUrl: card.imageUrl || undefined,
|
||||||
|
category: card.category || undefined,
|
||||||
|
marketInfo: card.marketInfo || undefined,
|
||||||
|
expiresAt: card.expiresAt || undefined,
|
||||||
|
chance: String(card.chanceValue),
|
||||||
|
...(card.marketId && { marketId: card.marketId }),
|
||||||
|
...(card.slug && { slug: card.slug }),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -61,9 +61,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else> {{ currentChance }}% {{ t('common.chance') }} </template>
|
<template v-else> {{ currentChance }}% {{ t('common.chance') }} </template>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="currentMarketClosed" class="chart-closed-banner">
|
|
||||||
{{ t('trade.marketClosedBanner') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 图表区域 -->
|
<!-- 图表区域 -->
|
||||||
@ -95,23 +92,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- 移动端:无右侧交易栏时展示已关闭说明 -->
|
|
||||||
<v-card
|
|
||||||
v-if="isMobile && currentMarketClosed"
|
|
||||||
class="market-closed-mobile-card"
|
|
||||||
elevation="0"
|
|
||||||
rounded="lg"
|
|
||||||
>
|
|
||||||
<div class="market-closed-pane market-closed-pane--mobile">
|
|
||||||
<v-icon size="40" color="grey-darken-1">mdi-lock-outline</v-icon>
|
|
||||||
<h2 class="market-closed-pane-title">{{ t('trade.marketClosedTitle') }}</h2>
|
|
||||||
<p class="market-closed-pane-desc">{{ t('trade.marketClosedDesc') }}</p>
|
|
||||||
<p v-if="settledOutcomeLabel" class="market-closed-pane-outcome">
|
|
||||||
{{ t('trade.marketClosedOutcome', { outcome: settledOutcomeLabel }) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
|
|
||||||
<!-- 持仓 / 限价(订单簿上方) -->
|
<!-- 持仓 / 限价(订单簿上方) -->
|
||||||
<v-card class="positions-orders-card" elevation="0" rounded="lg">
|
<v-card class="positions-orders-card" elevation="0" rounded="lg">
|
||||||
<v-tabs
|
<v-tabs
|
||||||
@ -170,7 +150,6 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
class="position-sell-btn"
|
class="position-sell-btn"
|
||||||
:disabled="
|
:disabled="
|
||||||
currentMarketClosed ||
|
|
||||||
!(pos.availableSharesNum != null && pos.availableSharesNum > 0)
|
!(pos.availableSharesNum != null && pos.availableSharesNum > 0)
|
||||||
"
|
"
|
||||||
@click="openSellFromPosition(pos)"
|
@click="openSellFromPosition(pos)"
|
||||||
@ -220,7 +199,7 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- Order Book Section -->
|
<!-- Order Book Section -->
|
||||||
<v-card v-if="!currentMarketClosed" class="order-book-card" elevation="0" rounded="lg">
|
<v-card class="order-book-card" elevation="0" rounded="lg">
|
||||||
<OrderBook
|
<OrderBook
|
||||||
:asks-yes="orderBookAsksYes"
|
:asks-yes="orderBookAsksYes"
|
||||||
:bids-yes="orderBookBidsYes"
|
:bids-yes="orderBookBidsYes"
|
||||||
@ -278,18 +257,11 @@
|
|||||||
|
|
||||||
<!-- 右侧:交易组件(固定宽度),传入当前市场以便 Split 调用拆单接口;移动端隐藏,改用底部栏+弹窗 -->
|
<!-- 右侧:交易组件(固定宽度),传入当前市场以便 Split 调用拆单接口;移动端隐藏,改用底部栏+弹窗 -->
|
||||||
<v-col v-if="!isMobile" cols="12" class="trade-col">
|
<v-col v-if="!isMobile" cols="12" class="trade-col">
|
||||||
<div v-if="currentMarketClosed" class="trade-sidebar market-closed-pane">
|
<div class="trade-sidebar">
|
||||||
<v-icon size="44" color="grey-darken-1">mdi-lock-outline</v-icon>
|
|
||||||
<h2 class="market-closed-pane-title">{{ t('trade.marketClosedTitle') }}</h2>
|
|
||||||
<p class="market-closed-pane-desc">{{ t('trade.marketClosedDesc') }}</p>
|
|
||||||
<p v-if="settledOutcomeLabel" class="market-closed-pane-outcome">
|
|
||||||
{{ t('trade.marketClosedOutcome', { outcome: settledOutcomeLabel }) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="tradeMarketPayload" class="trade-sidebar">
|
|
||||||
<TradeComponent
|
<TradeComponent
|
||||||
ref="tradeComponentRef"
|
ref="tradeComponentRef"
|
||||||
:market="tradeMarketPayload"
|
:market="tradeMarketPayload"
|
||||||
|
:initial-option="tradeInitialOption"
|
||||||
:positions="tradePositionsForComponent"
|
:positions="tradePositionsForComponent"
|
||||||
@merge-success="onMergeSuccess"
|
@merge-success="onMergeSuccess"
|
||||||
@split-success="onSplitSuccess"
|
@split-success="onSplitSuccess"
|
||||||
@ -298,7 +270,7 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<!-- 移动端:固定底部 Yes/No 栏 + 三点菜单(Merge/Split) -->
|
<!-- 移动端:固定底部 Yes/No 栏 + 三点菜单(Merge/Split) -->
|
||||||
<template v-if="isMobile && tradeMarketPayload && !currentMarketClosed">
|
<template v-if="isMobile && tradeMarketPayload">
|
||||||
<div class="mobile-trade-bar-spacer" aria-hidden="true"></div>
|
<div class="mobile-trade-bar-spacer" aria-hidden="true"></div>
|
||||||
<div class="mobile-trade-bar">
|
<div class="mobile-trade-bar">
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -409,8 +381,6 @@ import {
|
|||||||
findPmEvent,
|
findPmEvent,
|
||||||
getMarketId,
|
getMarketId,
|
||||||
getClobTokenId,
|
getClobTokenId,
|
||||||
getPmMarketSettledWinner,
|
|
||||||
isPmMarketClosed,
|
|
||||||
type FindPmEventParams,
|
type FindPmEventParams,
|
||||||
type PmEventListItem,
|
type PmEventListItem,
|
||||||
} from '../api/event'
|
} from '../api/event'
|
||||||
@ -516,9 +486,10 @@ async function loadEventDetail() {
|
|||||||
}
|
}
|
||||||
const numId = parseInt(idStr, 10)
|
const numId = parseInt(idStr, 10)
|
||||||
const isNumericId = Number.isFinite(numId) && String(numId) === idStr && numId >= 1
|
const isNumericId = Number.isFinite(numId) && String(numId) === idStr && numId >= 1
|
||||||
|
const slugFromQuery = (route.query.slug as string)?.trim()
|
||||||
const params: FindPmEventParams = {
|
const params: FindPmEventParams = {
|
||||||
id: isNumericId ? numId : undefined,
|
id: isNumericId ? numId : undefined,
|
||||||
slug: isNumericId ? undefined : idStr,
|
slug: isNumericId ? slugFromQuery || undefined : idStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
detailError.value = null
|
detailError.value = null
|
||||||
@ -564,15 +535,18 @@ function onRefresh({ done }: { done: () => void }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标题、成交量、到期日:仅使用接口详情(路径只带 slug/id,无 query)
|
// 标题、成交量、到期日:优先接口详情,其次卡片 query,最后占位
|
||||||
const marketTitle = computed(() => eventDetail.value?.title ?? '')
|
const marketTitle = computed(() => {
|
||||||
|
if (eventDetail.value?.title) return eventDetail.value.title
|
||||||
|
return (route.query.title as string) || 'U.S. anti-cartel ground operation in Mexico by March 31?'
|
||||||
|
})
|
||||||
const marketVolume = computed(() => {
|
const marketVolume = computed(() => {
|
||||||
if (eventDetail.value?.volume != null) return formatVolume(eventDetail.value.volume)
|
if (eventDetail.value?.volume != null) return formatVolume(eventDetail.value.volume)
|
||||||
return formatVolume(undefined)
|
return (route.query.marketInfo as string) || '$398,719'
|
||||||
})
|
})
|
||||||
const marketExpiresAt = computed(() => {
|
const marketExpiresAt = computed(() => {
|
||||||
if (eventDetail.value?.endDate) return formatExpiresAt(eventDetail.value.endDate)
|
if (eventDetail.value?.endDate) return formatExpiresAt(eventDetail.value.endDate)
|
||||||
return ''
|
return (route.query.expiresAt as string) || 'Mar 31, 2026'
|
||||||
})
|
})
|
||||||
const resolutionDate = computed(() => {
|
const resolutionDate = computed(() => {
|
||||||
const s = marketExpiresAt.value
|
const s = marketExpiresAt.value
|
||||||
@ -619,19 +593,17 @@ function setChartMode(mode: 'yesno' | 'crypto') {
|
|||||||
updateChartData()
|
updateChartData()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 当前市场(用于交易组件与 Split 拆单):取接口返回的第一个市场 */
|
/** 当前市场(用于交易组件与 Split 拆单):query.marketId 匹配或取第一个 */
|
||||||
const currentMarket = computed(() => {
|
const currentMarket = computed(() => {
|
||||||
const list = eventDetail.value?.markets ?? []
|
const list = eventDetail.value?.markets ?? []
|
||||||
return list[0] ?? null
|
if (list.length === 0) return null
|
||||||
})
|
const qId = route.query.marketId
|
||||||
|
if (qId != null && String(qId).trim() !== '') {
|
||||||
const currentMarketClosed = computed(() => isPmMarketClosed(currentMarket.value))
|
const qStr = String(qId).trim()
|
||||||
|
const found = list.find((m) => getMarketId(m) === qStr)
|
||||||
const settledOutcomeLabel = computed(() => {
|
if (found) return found
|
||||||
const m = currentMarket.value
|
}
|
||||||
if (!m) return ''
|
return list[0]
|
||||||
const w = getPmMarketSettledWinner(m)
|
|
||||||
return w?.label ?? ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- CLOB WebSocket 订单簿与成交 ---
|
// --- CLOB WebSocket 订单簿与成交 ---
|
||||||
@ -889,6 +861,23 @@ const tradeMarketPayload = computed(() => {
|
|||||||
bestBidNoCents,
|
bestBidNoCents,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const qId = route.query.marketId
|
||||||
|
if (qId != null && String(qId).trim() !== '') {
|
||||||
|
return {
|
||||||
|
marketId: String(qId).trim(),
|
||||||
|
yesPrice,
|
||||||
|
noPrice,
|
||||||
|
title: (route.query.title as string) || undefined,
|
||||||
|
bestBidYesCents,
|
||||||
|
bestBidNoCents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const tradeInitialOption = computed(() => {
|
||||||
|
const side = route.query.side
|
||||||
|
if (side === 'yes' || side === 'no') return side
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1227,7 +1216,12 @@ const currentChance = computed(() => {
|
|||||||
const d = data.value
|
const d = data.value
|
||||||
const last = d.length > 0 ? d[d.length - 1] : undefined
|
const last = d.length > 0 ? d[d.length - 1] : undefined
|
||||||
if (last != null) return last[1]
|
if (last != null) return last[1]
|
||||||
return 0
|
const q = route.query.chance
|
||||||
|
if (q != null) {
|
||||||
|
const n = Number(q)
|
||||||
|
if (Number.isFinite(n)) return Math.min(100, Math.max(0, Math.round(n)))
|
||||||
|
}
|
||||||
|
return 20
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineColor = '#2563eb'
|
const lineColor = '#2563eb'
|
||||||
@ -1972,59 +1966,6 @@ onUnmounted(() => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-closed-banner {
|
|
||||||
margin: 8px 0 0;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #92400e;
|
|
||||||
background: #fffbeb;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-pane {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
padding: 28px 20px;
|
|
||||||
border: 1px solid #e7e7e7;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-pane--mobile {
|
|
||||||
padding: 20px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-pane-title {
|
|
||||||
margin: 12px 0 8px;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #111827;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-pane-desc {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #6b7280;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-pane-outcome {
|
|
||||||
margin: 14px 0 0;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #111827;
|
|
||||||
}
|
|
||||||
|
|
||||||
.market-closed-mobile-card {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #e7e7e7;
|
|
||||||
box-shadow: none !important;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
.trade-detail-row {
|
.trade-detail-row {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
|||||||
@ -767,10 +767,17 @@ function canOpenTradeDetail(opts: { tradeEventId?: string; tradeEventSlug?: stri
|
|||||||
return !!(opts.tradeEventId?.trim() || opts.tradeEventSlug?.trim())
|
return !!(opts.tradeEventId?.trim() || opts.tradeEventSlug?.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
function openTradeDetailFromWallet(input: { tradeEventId?: string; tradeEventSlug?: string }) {
|
function openTradeDetailFromWallet(input: {
|
||||||
|
tradeEventId?: string
|
||||||
|
tradeEventSlug?: string
|
||||||
|
marketId?: string
|
||||||
|
title?: string
|
||||||
|
}) {
|
||||||
const loc = buildTradeDetailPushOptions({
|
const loc = buildTradeDetailPushOptions({
|
||||||
eventId: input.tradeEventId,
|
eventId: input.tradeEventId,
|
||||||
eventSlug: input.tradeEventSlug,
|
eventSlug: input.tradeEventSlug,
|
||||||
|
marketId: input.marketId,
|
||||||
|
title: input.title,
|
||||||
})
|
})
|
||||||
if (loc) router.push(loc)
|
if (loc) router.push(loc)
|
||||||
}
|
}
|
||||||
@ -780,6 +787,8 @@ function onPositionRowClick(pos: Position) {
|
|||||||
openTradeDetailFromWallet({
|
openTradeDetailFromWallet({
|
||||||
tradeEventId: pos.tradeEventId,
|
tradeEventId: pos.tradeEventId,
|
||||||
tradeEventSlug: pos.tradeEventSlug,
|
tradeEventSlug: pos.tradeEventSlug,
|
||||||
|
marketId: pos.marketID,
|
||||||
|
title: pos.market,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,6 +797,8 @@ function onOpenOrderRowClick(ord: OpenOrder) {
|
|||||||
openTradeDetailFromWallet({
|
openTradeDetailFromWallet({
|
||||||
tradeEventId: ord.tradeEventId,
|
tradeEventId: ord.tradeEventId,
|
||||||
tradeEventSlug: ord.tradeEventSlug,
|
tradeEventSlug: ord.tradeEventSlug,
|
||||||
|
marketId: ord.detailMarketId,
|
||||||
|
title: ord.market,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,6 +813,8 @@ function onHistoryRowClick(h: HistoryItem) {
|
|||||||
openTradeDetailFromWallet({
|
openTradeDetailFromWallet({
|
||||||
tradeEventId: h.tradeEventId,
|
tradeEventId: h.tradeEventId,
|
||||||
tradeEventSlug: h.tradeEventSlug,
|
tradeEventSlug: h.tradeEventSlug,
|
||||||
|
marketId: h.detailMarketId,
|
||||||
|
title: h.market,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1161,12 +1174,7 @@ function getWithdrawStatusLabel(status: string | undefined): string {
|
|||||||
const s = (status ?? '').toLowerCase()
|
const s = (status ?? '').toLowerCase()
|
||||||
if (s === WITHDRAW_STATUS.PENDING || s === '0' || s === 'pending')
|
if (s === WITHDRAW_STATUS.PENDING || s === '0' || s === 'pending')
|
||||||
return t('wallet.withdrawStatusPending')
|
return t('wallet.withdrawStatusPending')
|
||||||
if (
|
if (s === WITHDRAW_STATUS.SUCCESS || s === '1' || s === 'success')
|
||||||
s === WITHDRAW_STATUS.SUCCESS ||
|
|
||||||
s === WITHDRAW_STATUS.PROCESSED ||
|
|
||||||
s === '1' ||
|
|
||||||
s === 'success'
|
|
||||||
)
|
|
||||||
return t('wallet.withdrawStatusSuccess')
|
return t('wallet.withdrawStatusSuccess')
|
||||||
if (s === WITHDRAW_STATUS.REJECTED || s === '2' || s === 'rejected')
|
if (s === WITHDRAW_STATUS.REJECTED || s === '2' || s === 'rejected')
|
||||||
return t('wallet.withdrawStatusRejected')
|
return t('wallet.withdrawStatusRejected')
|
||||||
@ -1178,13 +1186,7 @@ function getWithdrawStatusLabel(status: string | undefined): string {
|
|||||||
function getWithdrawStatusClass(status: string | undefined): string {
|
function getWithdrawStatusClass(status: string | undefined): string {
|
||||||
const s = (status ?? '').toLowerCase()
|
const s = (status ?? '').toLowerCase()
|
||||||
if (s === WITHDRAW_STATUS.PENDING || s === '0' || s === 'pending') return 'status-pending'
|
if (s === WITHDRAW_STATUS.PENDING || s === '0' || s === 'pending') return 'status-pending'
|
||||||
if (
|
if (s === WITHDRAW_STATUS.SUCCESS || s === '1' || s === 'success') return 'status-success'
|
||||||
s === WITHDRAW_STATUS.SUCCESS ||
|
|
||||||
s === WITHDRAW_STATUS.PROCESSED ||
|
|
||||||
s === '1' ||
|
|
||||||
s === 'success'
|
|
||||||
)
|
|
||||||
return 'status-success'
|
|
||||||
if (s === WITHDRAW_STATUS.REJECTED || s === '2' || s === 'rejected') return 'status-rejected'
|
if (s === WITHDRAW_STATUS.REJECTED || s === '2' || s === 'rejected') return 'status-rejected'
|
||||||
if (s === WITHDRAW_STATUS.FAILED || s === '3' || s === 'failed') return 'status-failed'
|
if (s === WITHDRAW_STATUS.FAILED || s === '3' || s === 'failed') return 'status-failed'
|
||||||
return ''
|
return ''
|
||||||
@ -2729,17 +2731,12 @@ async function submitAuthorize() {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.withdrawal-status-pill.status-pending,
|
.withdrawal-status-pill.status-pending,
|
||||||
|
.withdrawal-status-pill.status-success,
|
||||||
.withdrawal-status-pill.status-failed {
|
.withdrawal-status-pill.status-failed {
|
||||||
background: #f3f4f6;
|
background: #f3f4f6;
|
||||||
color: #374151;
|
color: #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
.withdrawal-status-pill.status-success {
|
|
||||||
background: #dcfce7;
|
|
||||||
color: #166534;
|
|
||||||
border: 1px solid #bbf7d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.withdrawal-status-pill.status-rejected {
|
.withdrawal-status-pill.status-rejected {
|
||||||
background: #fef2f2;
|
background: #fef2f2;
|
||||||
color: #b91c1c;
|
color: #b91c1c;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user