修改:增加close市场过滤

This commit is contained in:
马丁 2026-07-04 21:35:07 +08:00
parent 18ac89a0df
commit 537f3030e7
3 changed files with 29 additions and 77 deletions

View File

@ -74,6 +74,13 @@ export function isPmMarketClosed(m: PmEventMarketItem | null | undefined): boole
return m?.closed === true return m?.closed === true
} }
/** 过滤掉已关闭的子市场(服务端公开接口已过滤,保留供本地兜底) */
export function filterOpenMarkets(
markets: PmEventMarketItem[] | undefined,
): PmEventMarketItem[] {
return (markets ?? []).filter((m) => !isPmMarketClosed(m))
}
/** /**
* outcomePrices outcomes 01 1100 * outcomePrices outcomes 01 1100
*/ */
@ -430,8 +437,20 @@ function mapMarketToOutcome(m: PmEventMarketItem): EventCardOutcome {
*/ */
export function mapEventItemToCard(item: PmEventListItem): EventCardItem { export function mapEventItemToCard(item: PmEventListItem): EventCardItem {
const markets = item.markets ?? [] const markets = item.markets ?? []
if (markets.length === 0) {
return buildEventCardItem(item, markets, false, undefined)
}
const multi = markets.length > 1 const multi = markets.length > 1
const firstMarket = markets[0] const firstMarket = markets[0]
return buildEventCardItem(item, markets, multi, firstMarket)
}
function buildEventCardItem(
item: PmEventListItem,
markets: PmEventMarketItem[],
multi: boolean,
firstMarket: PmEventMarketItem | undefined,
): EventCardItem {
const firstPrices = firstMarket ? parseOutcomePrices(firstMarket) : { yesPrice: 0.5, noPrice: 0.5 } const firstPrices = firstMarket ? parseOutcomePrices(firstMarket) : { yesPrice: 0.5, noPrice: 0.5 }
return { return {

View File

@ -118,14 +118,14 @@
<!-- 可交易市场列表 --> <!-- 可交易市场列表 -->
<v-card <v-card
v-if="activeMarkets.length > 0" v-if="markets.length > 0"
class="markets-list-card" class="markets-list-card"
elevation="0" elevation="0"
rounded="lg" 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 ?? market.id ?? index"
class="market-row" class="market-row"
:class="{ selected: selectedMarketIndex === index }" :class="{ selected: selectedMarketIndex === index }"
@ -161,46 +161,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"
@ -210,7 +175,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
@ -261,7 +226,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"
@ -297,8 +262,6 @@ import {
findPmEvent, findPmEvent,
getMarketId, getMarketId,
getClobTokenId, getClobTokenId,
getPmMarketSettledWinner,
isPmMarketClosed,
type FindPmEventParams, type FindPmEventParams,
type PmEventListItem, type PmEventListItem,
type PmEventMarketItem, type PmEventMarketItem,
@ -340,14 +303,7 @@ const pendingMergeSplitDialog = ref<'merge' | 'split' | null>(null)
const mobileMenuOpen = ref(false) const mobileMenuOpen = ref(false)
/** TradeComponent 引用,用于从底部栏触发 Merge/Split */ /** TradeComponent 引用,用于从底部栏触发 Merge/Split */
const tradeComponentRef = ref<InstanceType<typeof TradeComponent> | null>(null) const tradeComponentRef = ref<InstanceType<typeof TradeComponent> | null>(null)
const markets = computed(() => { const markets = computed(() => eventDetail.value?.markets ?? [])
const list = eventDetail.value?.markets ?? []
return list.length > 0 ? list : []
})
/** 仍可交易的市场closed !== true */
const activeMarkets = computed(() => markets.value.filter((m) => !isPmMarketClosed(m)))
/** 已结算/已关闭的子市场 */
const closedMarkets = computed(() => markets.value.filter((m) => isPmMarketClosed(m)))
/** 多市场时描述默认折叠,避免首屏过长 */ /** 多市场时描述默认折叠,避免首屏过长 */
const isMultiMarket = computed(() => markets.value.length > 1) const isMultiMarket = computed(() => markets.value.length > 1)
const RULES_DESC_COLLAPSE_MIN_LEN = 80 const RULES_DESC_COLLAPSE_MIN_LEN = 80
@ -357,9 +313,9 @@ const rulesDescriptionCollapsible = computed(() => {
const d = eventDetail.value?.description?.trim() ?? '' const d = eventDetail.value?.description?.trim() ?? ''
return d.length > RULES_DESC_COLLAPSE_MIN_LEN return d.length > RULES_DESC_COLLAPSE_MIN_LEN
}) })
const selectedMarket = computed(() => activeMarkets.value[selectedMarketIndex.value] ?? null) const selectedMarket = computed(() => markets.value[selectedMarketIndex.value] ?? null)
/** 移动端底部栏显示的市场(选中项或首个),仅在有可交易市场时使用 */ /** 移动端底部栏显示的市场(选中项或首个),仅在有可交易市场时使用 */
const barMarket = computed(() => selectedMarket.value ?? activeMarkets.value[0]) const barMarket = computed(() => selectedMarket.value ?? markets.value[0])
/** 传给购买组件的市场数据(当前选中的市场) */ /** 传给购买组件的市场数据(当前选中的市场) */
const tradeMarketPayload = computed(() => { const tradeMarketPayload = computed(() => {
const m = selectedMarket.value const m = selectedMarket.value
@ -391,26 +347,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 {
@ -710,7 +646,7 @@ async function loadEventDetail() {
} }
watch( watch(
activeMarkets, markets,
(list) => { (list) => {
if (selectedMarketIndex.value >= list.length) { if (selectedMarketIndex.value >= list.length) {
selectedMarketIndex.value = Math.max(0, list.length - 1) selectedMarketIndex.value = Math.max(0, list.length - 1)

View File

@ -517,10 +517,7 @@ function setChartMode(mode: 'yesno' | 'crypto') {
} }
/** 当前市场(用于交易组件与 Split 拆单):取接口返回的第一个市场 */ /** 当前市场(用于交易组件与 Split 拆单):取接口返回的第一个市场 */
const currentMarket = computed(() => { const currentMarket = computed(() => eventDetail.value?.markets?.[0] ?? null)
const list = eventDetail.value?.markets ?? []
return list[0] ?? null
})
const currentMarketClosed = computed(() => isPmMarketClosed(currentMarket.value)) const currentMarketClosed = computed(() => isPmMarketClosed(currentMarket.value))