新增:对接详情接口
This commit is contained in:
parent
def6b95b5b
commit
f0c1be71cb
@ -8,38 +8,72 @@ export interface PageResult<T> {
|
|||||||
total: number
|
total: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 接口 list 单项结构(与 /PmEvent/getPmEventPublic 返回一致) */
|
/**
|
||||||
|
* Event 单项结构(与 doc.json definitions["polymarket.PmEvent"] 对齐)
|
||||||
|
* 用于 /PmEvent/getPmEventPublic 列表项 与 /PmEvent/findPmEvent 的 data
|
||||||
|
*/
|
||||||
export interface PmEventListItem {
|
export interface PmEventListItem {
|
||||||
ID: number
|
ID: number
|
||||||
CreatedAt?: string
|
createdAt?: string
|
||||||
UpdatedAt?: string
|
updatedAt?: string
|
||||||
ticker?: string
|
|
||||||
slug?: string
|
slug?: string
|
||||||
|
ticker?: string
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description?: string
|
||||||
resolutionSource?: string
|
resolutionSource?: string
|
||||||
startDate?: string
|
startDate?: string
|
||||||
endDate?: string
|
endDate?: string
|
||||||
|
creationDate?: string
|
||||||
|
closedTime?: string
|
||||||
|
startTime?: string
|
||||||
image?: string
|
image?: string
|
||||||
icon?: string
|
icon?: string
|
||||||
active?: boolean
|
active?: boolean
|
||||||
|
archived?: boolean
|
||||||
closed?: boolean
|
closed?: boolean
|
||||||
|
featured?: boolean
|
||||||
|
new?: boolean
|
||||||
|
restricted?: boolean
|
||||||
|
enableOrderBook?: boolean
|
||||||
|
competitive?: number
|
||||||
|
liquidity?: number
|
||||||
|
liquidityAmm?: number
|
||||||
|
liquidityClob?: number
|
||||||
|
openInterest?: number
|
||||||
volume?: number
|
volume?: number
|
||||||
markets?: Array<{
|
commentCount?: number
|
||||||
|
seriesSlug?: string
|
||||||
|
markets?: PmEventMarketItem[]
|
||||||
|
series?: PmEventSeriesItem[]
|
||||||
|
tags?: PmEventTagItem[]
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 对应 definitions polymarket.PmMarket 常用字段;outcomePrices 首项为 Yes 价格 */
|
||||||
|
export interface PmEventMarketItem {
|
||||||
ID?: number
|
ID?: number
|
||||||
question?: string
|
question?: string
|
||||||
outcomePrices?: string[]
|
slug?: string
|
||||||
|
outcomePrices?: string[] | number[]
|
||||||
endDate?: string
|
endDate?: string
|
||||||
volume?: number
|
volume?: number
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}>
|
}
|
||||||
series?: Array<{
|
|
||||||
|
/** 对应 definitions polymarket.PmSeries 常用字段 */
|
||||||
|
export interface PmEventSeriesItem {
|
||||||
ID?: number
|
ID?: number
|
||||||
ticker?: string
|
ticker?: string
|
||||||
title?: string
|
title?: string
|
||||||
|
slug?: string
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}>
|
}
|
||||||
tags?: Array<{ label?: string; slug?: string; [key: string]: unknown }>
|
|
||||||
|
/** 对应 definitions polymarket.PmTag 常用字段 */
|
||||||
|
export interface PmEventTagItem {
|
||||||
|
label?: string
|
||||||
|
slug?: string
|
||||||
|
ID?: number
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +110,33 @@ export async function getPmEventPublic(
|
|||||||
return get<PmEventListResponse>('/PmEvent/getPmEventPublic', query)
|
return get<PmEventListResponse>('/PmEvent/getPmEventPublic', query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /PmEvent/findPmEvent 响应体(200)
|
||||||
|
* doc.json: responses["200"].schema = allOf [ response.Response, { data: polymarket.PmEvent, msg } ]
|
||||||
|
*/
|
||||||
|
export interface PmEventDetailResponse {
|
||||||
|
code: number
|
||||||
|
data: PmEventListItem
|
||||||
|
msg: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用 id 查询 Event 详情
|
||||||
|
* GET /PmEvent/findPmEvent
|
||||||
|
*
|
||||||
|
* 请求参数(Query):
|
||||||
|
* - ID: number,必填,Event 主键
|
||||||
|
* 鉴权:需在 headers 中传 x-token
|
||||||
|
*
|
||||||
|
* 响应(200):PmEventDetailResponse { code, data: PmEventListItem, msg }
|
||||||
|
*/
|
||||||
|
export async function findPmEvent(
|
||||||
|
id: number,
|
||||||
|
config?: { headers?: Record<string, string> }
|
||||||
|
): Promise<PmEventDetailResponse> {
|
||||||
|
return get<PmEventDetailResponse>('/PmEvent/findPmEvent', { ID: id }, config)
|
||||||
|
}
|
||||||
|
|
||||||
/** 首页卡片项(与 mapEventItemToCard 返回结构一致,用于缓存) */
|
/** 首页卡片项(与 mapEventItemToCard 返回结构一致,用于缓存) */
|
||||||
export interface EventCardItem {
|
export interface EventCardItem {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ref, computed } from 'vue'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
|
id?: number | string
|
||||||
headerImg?: string
|
headerImg?: string
|
||||||
nickName?: string
|
nickName?: string
|
||||||
userName?: string
|
userName?: string
|
||||||
|
|||||||
@ -7,7 +7,10 @@
|
|||||||
<v-card class="chart-card polymarket-chart" elevation="0" rounded="lg">
|
<v-card class="chart-card polymarket-chart" elevation="0" rounded="lg">
|
||||||
<!-- 顶部:标题、当前概率、Past / 日期 -->
|
<!-- 顶部:标题、当前概率、Past / 日期 -->
|
||||||
<div class="chart-header">
|
<div class="chart-header">
|
||||||
<h1 class="chart-title">{{ marketTitle }}</h1>
|
<h1 class="chart-title">
|
||||||
|
{{ detailLoading && !eventDetail ? '加载中...' : marketTitle }}
|
||||||
|
</h1>
|
||||||
|
<p v-if="detailError" class="chart-error">{{ detailError }}</p>
|
||||||
<div class="chart-controls-row">
|
<div class="chart-controls-row">
|
||||||
<v-btn variant="text" size="small" class="past-btn">Past ▾</v-btn>
|
<v-btn variant="text" size="small" class="past-btn">Past ▾</v-btn>
|
||||||
<v-btn class="date-pill" size="small" rounded="pill">{{ resolutionDate }}</v-btn>
|
<v-btn class="date-pill" size="small" rounded="pill">{{ resolutionDate }}</v-btn>
|
||||||
@ -124,6 +127,8 @@ import * as echarts from 'echarts'
|
|||||||
import type { ECharts } from 'echarts'
|
import type { ECharts } from 'echarts'
|
||||||
import OrderBook from '../components/OrderBook.vue'
|
import OrderBook from '../components/OrderBook.vue'
|
||||||
import TradeComponent from '../components/TradeComponent.vue'
|
import TradeComponent from '../components/TradeComponent.vue'
|
||||||
|
import { findPmEvent, type PmEventListItem } from '../api/event'
|
||||||
|
import { useUserStore } from '../stores/user'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分时图服务端推送数据格式约定
|
* 分时图服务端推送数据格式约定
|
||||||
@ -153,13 +158,71 @@ export type ChartSnapshot = { range?: string; data: ChartPoint[] }
|
|||||||
export type ChartIncrement = { point: ChartPoint }
|
export type ChartIncrement = { point: ChartPoint }
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
// 卡片传入:标题、成交量、到期日
|
// 详情接口 GET /PmEvent/findPmEvent 返回的数据
|
||||||
const marketTitle = computed(
|
const eventDetail = ref<PmEventListItem | null>(null)
|
||||||
() => (route.query.title as string) || 'U.S. anti-cartel ground operation in Mexico by March 31?'
|
const detailLoading = ref(false)
|
||||||
)
|
const detailError = ref<string | null>(null)
|
||||||
const marketVolume = computed(() => (route.query.marketInfo as string) || '$398,719')
|
|
||||||
const marketExpiresAt = computed(() => (route.query.expiresAt as string) || 'Mar 31, 2026')
|
function formatVolume(volume: number | undefined): string {
|
||||||
|
if (volume == null || !Number.isFinite(volume)) return '$0 Vol.'
|
||||||
|
if (volume >= 1000) return `$${(volume / 1000).toFixed(1)}k Vol.`
|
||||||
|
return `$${Math.round(volume)} Vol.`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatExpiresAt(endDate: string | undefined): string {
|
||||||
|
if (!endDate) return ''
|
||||||
|
try {
|
||||||
|
const d = new Date(endDate)
|
||||||
|
if (Number.isNaN(d.getTime())) return endDate
|
||||||
|
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
||||||
|
} catch {
|
||||||
|
return endDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadEventDetail() {
|
||||||
|
const idRaw = route.params.id
|
||||||
|
const id = typeof idRaw === 'string' ? parseInt(idRaw, 10) : Number(idRaw)
|
||||||
|
if (!Number.isFinite(id) || id < 1) {
|
||||||
|
detailError.value = '无效的 ID'
|
||||||
|
eventDetail.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
detailError.value = null
|
||||||
|
detailLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await findPmEvent(id, {
|
||||||
|
headers: userStore.token ? { 'x-token': userStore.token } : undefined
|
||||||
|
})
|
||||||
|
if (res.code === 0 || res.code === 200) {
|
||||||
|
eventDetail.value = res.data ?? null
|
||||||
|
} else {
|
||||||
|
detailError.value = res.msg || '加载失败'
|
||||||
|
eventDetail.value = null
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
detailError.value = e instanceof Error ? e.message : '加载失败'
|
||||||
|
eventDetail.value = null
|
||||||
|
} finally {
|
||||||
|
detailLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标题、成交量、到期日:优先接口详情,其次卡片 query,最后占位
|
||||||
|
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(() => {
|
||||||
|
if (eventDetail.value?.volume != null) return formatVolume(eventDetail.value.volume)
|
||||||
|
return (route.query.marketInfo as string) || '$398,719'
|
||||||
|
})
|
||||||
|
const marketExpiresAt = computed(() => {
|
||||||
|
if (eventDetail.value?.endDate) return formatExpiresAt(eventDetail.value.endDate)
|
||||||
|
return (route.query.expiresAt as string) || 'Mar 31, 2026'
|
||||||
|
})
|
||||||
const resolutionDate = computed(() => {
|
const resolutionDate = computed(() => {
|
||||||
const s = marketExpiresAt.value
|
const s = marketExpiresAt.value
|
||||||
return s ? s.replace(/,?\s*\d{4}$/, '').trim() || 'Mar 31' : 'Mar 31'
|
return s ? s.replace(/,?\s*\d{4}$/, '').trim() || 'Mar 31' : 'Mar 31'
|
||||||
@ -272,9 +335,21 @@ let chartInstance: ECharts | null = null
|
|||||||
let dynamicInterval: number | undefined
|
let dynamicInterval: number | undefined
|
||||||
|
|
||||||
const currentChance = computed(() => {
|
const currentChance = computed(() => {
|
||||||
|
const ev = eventDetail.value
|
||||||
|
const market = ev?.markets?.[0]
|
||||||
|
if (market?.outcomePrices?.[0] != null) {
|
||||||
|
const p = parseFloat(String(market.outcomePrices[0]))
|
||||||
|
if (Number.isFinite(p)) return Math.min(100, Math.max(0, Math.round(p * 100)))
|
||||||
|
}
|
||||||
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
|
||||||
return last != null ? last[1] : 20
|
if (last != null) return last[1]
|
||||||
|
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'
|
||||||
@ -441,7 +516,14 @@ const handleResize = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
() => loadEventDetail(),
|
||||||
|
{ immediate: false }
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
loadEventDetail()
|
||||||
initChart()
|
initChart()
|
||||||
startDynamicUpdate()
|
startDynamicUpdate()
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
@ -623,6 +705,12 @@ onUnmounted(() => {
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-error {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #dc2626;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-controls-row {
|
.chart-controls-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user