Compare commits
No commits in common. "25455e1a813a7c342f1f29f798c37052677d92e3" and "2174abc9d37da5a92319b1d5ddec8a60c163abf7" have entirely different histories.
25455e1a81
...
2174abc9d3
@ -11,43 +11,15 @@ description: Interprets the XTrader API from the Swagger 2.0 spec at https://api
|
|||||||
|
|
||||||
## ⚠️ 强制执行:接口接入三步流程
|
## ⚠️ 强制执行:接口接入三步流程
|
||||||
|
|
||||||
**接入任意 XTrader 接口时,必须严格按以下顺序执行。收到「对接 XXX 接口」请求后,立即按此流程执行,不得跳过、不得调换、不得合并。**
|
**接入任意 XTrader 接口时,必须严格按以下顺序执行,不得跳过、不得调换、不得合并步骤。**
|
||||||
|
|
||||||
### 强制动作序列(必须依次执行)
|
| 步骤 | 动作 | 强制要求 |
|
||||||
|
|------|------|----------|
|
||||||
|
| **第一步** | 从 doc.json 整理并**在对话中输出**请求参数表、响应参数表、definitions 完整结构 | 在输出第一步结果**之前**,不得写任何业务代码;必须用 `mcp_web_fetch` 或 curl 获取 doc.json,解析 `paths` 与 `definitions` |
|
||||||
|
| **第二步** | 根据第一步整理出的结构,在 `src/api/` 中定义 TypeScript 类型 | 必须等第一步输出完成后再执行;Model 必须与 definitions 对应 |
|
||||||
|
| **第三步** | 实现请求函数并集成到页面 | 必须等第二步完成后再执行 |
|
||||||
|
|
||||||
1. **收到对接请求** → 立即用 `mcp_web_fetch` 或 `curl` 获取 `https://api.xtrader.vip/swagger/doc.json`
|
**违反后果**:若跳过第一步直接写代码,会导致类型与接口文档不一致、遗漏字段或误用参数。
|
||||||
2. **第一步** → 解析 `paths["<path>"]["<method>"]` 与 `definitions`,**在对话中输出**:
|
|
||||||
- 请求参数表(Query/Body/鉴权)
|
|
||||||
- 响应参数表(200 schema)
|
|
||||||
- data 的 `$ref` 对应 definitions 的**完整字段表**
|
|
||||||
- 输出后**明确标注「第一步完成」**
|
|
||||||
3. **第二步** → 在 `src/api/` 中根据第一步表格定义 TypeScript 类型,**不得在第一步完成前执行**
|
|
||||||
4. **第三步** → 实现请求函数并集成到页面,**不得在第二步完成前执行**
|
|
||||||
|
|
||||||
### 禁止行为
|
|
||||||
|
|
||||||
- ❌ 在对话中输出第一步结果**之前**写任何 `src/api/` 或 `src/views/` 业务代码
|
|
||||||
- ❌ 跳过第一步直接定义类型或实现请求
|
|
||||||
- ❌ 合并步骤(如边输出边写代码)
|
|
||||||
|
|
||||||
### 第一步输出模板(必须包含)
|
|
||||||
|
|
||||||
```
|
|
||||||
## 第一步:GET/POST <path> 请求与响应参数
|
|
||||||
|
|
||||||
### 1. 请求参数
|
|
||||||
| 类型 | 名称 | 类型 | 必填 | 说明 |
|
|
||||||
| ... |
|
|
||||||
|
|
||||||
### 2. 响应参数(200)
|
|
||||||
根结构:{ code, data, msg }
|
|
||||||
|
|
||||||
### 3. definitions["xxx"] 完整字段
|
|
||||||
| 字段 | 类型 | 说明 |
|
|
||||||
| ... |
|
|
||||||
|
|
||||||
第一步完成。
|
|
||||||
```
|
|
||||||
|
|
||||||
## 规范地址与格式
|
## 规范地址与格式
|
||||||
|
|
||||||
|
|||||||
2
.env
2
.env
@ -1,6 +1,6 @@
|
|||||||
# API 基础地址,不设置时默认 https://api.xtrader.vip
|
# API 基础地址,不设置时默认 https://api.xtrader.vip
|
||||||
# 连接测试服务器 192.168.3.21:8888 时复制本文件为 .env 或 .env.local 并取消下一行注释:
|
# 连接测试服务器 192.168.3.21:8888 时复制本文件为 .env 或 .env.local 并取消下一行注释:
|
||||||
# VITE_API_BASE_URL=http://192.168.3.21:8888
|
VITE_API_BASE_URL=http://192.168.3.21:8888
|
||||||
|
|
||||||
# SSH 部署(npm run deploy),可选覆盖
|
# SSH 部署(npm run deploy),可选覆盖
|
||||||
# DEPLOY_HOST=38.246.250.238
|
# DEPLOY_HOST=38.246.250.238
|
||||||
|
|||||||
@ -12,7 +12,6 @@ const currentRoute = computed(() => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (userStore.isLoggedIn) {
|
if (userStore.isLoggedIn) {
|
||||||
userStore.fetchUserInfo()
|
|
||||||
userStore.fetchUsdcBalance()
|
userStore.fetchUsdcBalance()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,51 +2,6 @@ import { get } from './request'
|
|||||||
|
|
||||||
const USDC_DECIMALS = 1_000_000
|
const USDC_DECIMALS = 1_000_000
|
||||||
|
|
||||||
/**
|
|
||||||
* getUserInfo 返回的 data(definitions system.SysUser)
|
|
||||||
* doc.json definitions["system.SysUser"] 完整字段
|
|
||||||
*/
|
|
||||||
export interface UserInfoData {
|
|
||||||
ID?: number
|
|
||||||
userName?: string
|
|
||||||
nickName?: string
|
|
||||||
headerImg?: string
|
|
||||||
uuid?: string
|
|
||||||
authorityId?: number
|
|
||||||
authority?: unknown
|
|
||||||
authorities?: unknown[]
|
|
||||||
createdAt?: string
|
|
||||||
updatedAt?: string
|
|
||||||
email?: string
|
|
||||||
phone?: string
|
|
||||||
enable?: number
|
|
||||||
walletAddress?: string
|
|
||||||
externalWalletAddress?: string
|
|
||||||
walletPrivEnc?: string
|
|
||||||
promotionCode?: string
|
|
||||||
puserId?: number
|
|
||||||
remark?: string
|
|
||||||
originSetting?: Record<string, unknown>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetUserInfoResponse {
|
|
||||||
code: number
|
|
||||||
data?: UserInfoData
|
|
||||||
msg?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /user/getUserInfo
|
|
||||||
* 获取当前用户信息,需鉴权(x-token)
|
|
||||||
*/
|
|
||||||
export async function getUserInfo(
|
|
||||||
authHeaders: Record<string, string>,
|
|
||||||
): Promise<GetUserInfoResponse> {
|
|
||||||
return get<GetUserInfoResponse>('/user/getUserInfo', undefined, {
|
|
||||||
headers: authHeaders,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** getUsdcBalance 返回的 data 结构 */
|
/** getUsdcBalance 返回的 data 结构 */
|
||||||
export interface UsdcBalanceData {
|
export interface UsdcBalanceData {
|
||||||
amount: string
|
amount: string
|
||||||
|
|||||||
@ -177,8 +177,6 @@ const emit = defineEmits<{
|
|||||||
marketId?: string
|
marketId?: string
|
||||||
outcomeTitle?: string
|
outcomeTitle?: string
|
||||||
clobTokenIds?: string[]
|
clobTokenIds?: string[]
|
||||||
yesLabel?: string
|
|
||||||
noLabel?: string
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}>()
|
}>()
|
||||||
@ -302,8 +300,6 @@ function openTradeSingle(side: 'yes' | 'no') {
|
|||||||
title: props.marketTitle,
|
title: props.marketTitle,
|
||||||
marketId: props.marketId,
|
marketId: props.marketId,
|
||||||
clobTokenIds: props.clobTokenIds,
|
clobTokenIds: props.clobTokenIds,
|
||||||
yesLabel: props.yesLabel,
|
|
||||||
noLabel: props.noLabel,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +310,6 @@ function openTradeMulti(side: 'yes' | 'no', outcome: EventCardOutcome) {
|
|||||||
marketId: outcome.marketId,
|
marketId: outcome.marketId,
|
||||||
outcomeTitle: outcome.title,
|
outcomeTitle: outcome.title,
|
||||||
clobTokenIds: outcome.clobTokenIds,
|
clobTokenIds: outcome.clobTokenIds,
|
||||||
yesLabel: outcome.yesLabel,
|
|
||||||
noLabel: outcome.noLabel,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card class="order-book" elevation="0">
|
<v-card class="order-book">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="order-book-header">
|
<div class="order-book-header">
|
||||||
<h3 class="order-book-title">Order Book</h3>
|
<h3 class="order-book-title">Order Book</h3>
|
||||||
@ -198,8 +198,7 @@ const maxBidsTotal = computed(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: none;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
border: 1px solid #e7e7e7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal-progress-bar {
|
.horizontal-progress-bar {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 桌面端:完整交易卡片(扁平化) -->
|
<!-- 桌面端:完整交易卡片 -->
|
||||||
<v-card v-if="!mobile" class="trade-component" elevation="0">
|
<v-card v-if="!mobile" class="trade-component">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<v-tabs v-model="activeTab" class="buy-sell-tabs minimal-tabs" density="compact">
|
<v-tabs v-model="activeTab" class="buy-sell-tabs minimal-tabs" density="compact">
|
||||||
@ -44,7 +44,7 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>
|
>
|
||||||
{{ yesLabel }} {{ yesPriceCents }}¢
|
Yes {{ yesPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
@ -52,37 +52,10 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>
|
>
|
||||||
{{ noLabel }} {{ noPriceCents }}¢
|
No {{ noPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sell Market: Shares input + 25%/50%/Max -->
|
|
||||||
<template v-if="activeTab === 'sell'">
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Total and To Win (Buy模式) 或 You'll receive (Sell模式) -->
|
<!-- Total and To Win (Buy模式) 或 You'll receive (Sell模式) -->
|
||||||
<div class="total-section">
|
<div class="total-section">
|
||||||
<!-- Buy模式 -->
|
<!-- Buy模式 -->
|
||||||
@ -95,7 +68,7 @@
|
|||||||
<span class="label">To win</span>
|
<span class="label">To win</span>
|
||||||
<span class="to-win-value">
|
<span class="to-win-value">
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
||||||
{{ toWinValue }}
|
$20
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -105,29 +78,15 @@
|
|||||||
<span class="label">You'll receive</span>
|
<span class="label">You'll receive</span>
|
||||||
<span class="to-win-value">
|
<span class="to-win-value">
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
||||||
{{ totalPrice }}
|
${{ totalPrice }}
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
||||||
<!-- Action Button: Buy 余额足够显示 Buy Yes/No,不足显示 Deposit;Sell 只显示 Sell Yes/No -->
|
<!-- Action Button -->
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="activeTab === 'buy' && showDepositForBuy"
|
|
||||||
class="deposit-btn"
|
|
||||||
@click="deposit"
|
|
||||||
>
|
|
||||||
Deposit
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-else
|
|
||||||
class="action-btn"
|
class="action-btn"
|
||||||
:loading="orderLoading"
|
:loading="orderLoading"
|
||||||
:disabled="orderLoading"
|
:disabled="orderLoading"
|
||||||
@ -137,7 +96,7 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Balance <= 0: Show Deposit Interface (Buy) 或 Sell UI (Sell) -->
|
<!-- Balance <= 0: Show Deposit Interface -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- Price Options -->
|
<!-- Price Options -->
|
||||||
<div class="price-options">
|
<div class="price-options">
|
||||||
@ -147,7 +106,7 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>
|
>
|
||||||
{{ yesLabel }} {{ yesPriceCents }}¢
|
Yes {{ yesPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
@ -155,12 +114,11 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>
|
>
|
||||||
{{ noLabel }} {{ noPriceCents }}¢
|
No {{ noPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buy: Amount Section -->
|
<!-- Amount Section -->
|
||||||
<template v-if="activeTab === 'buy'">
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="amount-header">
|
<div class="amount-header">
|
||||||
<div>
|
<div>
|
||||||
@ -177,71 +135,11 @@
|
|||||||
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
||||||
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- To win(份数 × 1U) -->
|
<!-- Deposit Button -->
|
||||||
<div v-if="amount > 0" class="total-row amount-to-win-row">
|
|
||||||
<span class="label">To win</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ toWinValue }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Buy 余额不足时显示 Deposit -->
|
|
||||||
<v-btn class="deposit-btn" @click="deposit"> Deposit </v-btn>
|
<v-btn class="deposit-btn" @click="deposit"> Deposit </v-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Sell: Shares + To receive + Avg. Price,只显示 Sell Yes/No(无 Deposit) -->
|
|
||||||
<template v-else>
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="total-section">
|
|
||||||
<div class="total-row">
|
|
||||||
<span class="label">You'll receive</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ totalPrice }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Sell 只显示 Sell Yes/No -->
|
|
||||||
<v-btn
|
|
||||||
class="action-btn"
|
|
||||||
:loading="orderLoading"
|
|
||||||
:disabled="orderLoading"
|
|
||||||
@click="submitOrder"
|
|
||||||
>
|
|
||||||
{{ actionButtonText }}
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Limit Mode View -->
|
<!-- Limit Mode View -->
|
||||||
@ -254,7 +152,7 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>
|
>
|
||||||
{{ yesLabel }} {{ yesPriceCents }}¢
|
Yes {{ yesPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
@ -262,7 +160,7 @@
|
|||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>
|
>
|
||||||
{{ noLabel }} {{ noPriceCents }}¢
|
No {{ noPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -362,7 +260,7 @@
|
|||||||
<span class="label">To win</span>
|
<span class="label">To win</span>
|
||||||
<span class="to-win-value">
|
<span class="to-win-value">
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
||||||
{{ toWinValue }}
|
$20
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -372,7 +270,7 @@
|
|||||||
<span class="label">You'll receive</span>
|
<span class="label">You'll receive</span>
|
||||||
<span class="to-win-value">
|
<span class="to-win-value">
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
||||||
{{ totalPrice }}
|
${{ totalPrice }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -390,9 +288,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- 移动端且由首页卡片嵌入:只渲染交易表单,无底部栏、无内部 sheet(扁平化) -->
|
<!-- 移动端且由首页卡片嵌入:只渲染交易表单,无底部栏、无内部 sheet -->
|
||||||
<template v-else-if="embeddedInSheet">
|
<template v-else-if="embeddedInSheet">
|
||||||
<v-sheet class="trade-sheet-paper trade-sheet-paper--embedded" rounded="lg" elevation="0">
|
<v-sheet class="trade-sheet-paper trade-sheet-paper--embedded" rounded="lg">
|
||||||
<div class="trade-component trade-sheet-inner">
|
<div class="trade-component trade-sheet-inner">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<v-tabs v-model="activeTab" class="buy-sell-tabs minimal-tabs" density="compact">
|
<v-tabs v-model="activeTab" class="buy-sell-tabs minimal-tabs" density="compact">
|
||||||
@ -431,42 +329,16 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Sell Market: Shares input + 25%/50%/Max -->
|
|
||||||
<template v-if="activeTab === 'sell'">
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="total-section">
|
<div class="total-section">
|
||||||
<template v-if="activeTab === 'buy'">
|
<template v-if="activeTab === 'buy'">
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
@ -475,7 +347,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">To win</span
|
<span class="label">To win</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{ toWinValue }}</span
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> $20</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -483,29 +355,15 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">You'll receive</span
|
<span class="label">You'll receive</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> ${{
|
||||||
totalPrice
|
totalPrice
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="activeTab === 'buy' && showDepositForBuy"
|
|
||||||
class="deposit-btn"
|
|
||||||
@click="deposit"
|
|
||||||
>
|
|
||||||
Deposit
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-else
|
|
||||||
class="action-btn"
|
class="action-btn"
|
||||||
:loading="orderLoading"
|
:loading="orderLoading"
|
||||||
:disabled="orderLoading"
|
:disabled="orderLoading"
|
||||||
@ -520,18 +378,16 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Buy: Amount Section -->
|
|
||||||
<template v-if="activeTab === 'buy'">
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="amount-header">
|
<div class="amount-header">
|
||||||
<div>
|
<div>
|
||||||
@ -546,66 +402,9 @@
|
|||||||
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
||||||
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="amount > 0" class="total-row amount-to-win-row">
|
|
||||||
<span class="label">To win</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ toWinValue }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<v-btn class="deposit-btn" @click="deposit">Deposit</v-btn>
|
<v-btn class="deposit-btn" @click="deposit">Deposit</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<!-- Sell: Shares + To receive + Avg. Price -->
|
|
||||||
<template v-else>
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="total-section">
|
|
||||||
<div class="total-row">
|
|
||||||
<span class="label">You'll receive</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ totalPrice }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<v-btn
|
|
||||||
class="action-btn"
|
|
||||||
:loading="orderLoading"
|
|
||||||
:disabled="orderLoading"
|
|
||||||
@click="submitOrder"
|
|
||||||
>
|
|
||||||
{{ actionButtonText }}
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="price-options hide-in-mobile-sheet">
|
<div class="price-options hide-in-mobile-sheet">
|
||||||
@ -614,14 +413,14 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group limit-price-group">
|
<div class="input-group limit-price-group">
|
||||||
@ -707,7 +506,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">To win</span
|
<span class="label">To win</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{ toWinValue }}</span
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> $20</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -715,7 +514,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">You'll receive</span
|
<span class="label">You'll receive</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{ totalPrice }}</span
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> ${{ totalPrice }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -745,7 +544,7 @@
|
|||||||
block
|
block
|
||||||
@click="openSheet('yes')"
|
@click="openSheet('yes')"
|
||||||
>
|
>
|
||||||
Buy {{ yesLabel }} {{ yesPriceCents }}¢
|
Buy Yes {{ yesPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="mobile-bar-btn mobile-bar-no"
|
class="mobile-bar-btn mobile-bar-no"
|
||||||
@ -755,7 +554,7 @@
|
|||||||
block
|
block
|
||||||
@click="openSheet('no')"
|
@click="openSheet('no')"
|
||||||
>
|
>
|
||||||
Buy {{ noLabel }} {{ noPriceCents }}¢
|
Buy No {{ noPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -799,42 +598,16 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Sell Market: Shares input + 25%/50%/Max -->
|
|
||||||
<template v-if="activeTab === 'sell'">
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="total-section">
|
<div class="total-section">
|
||||||
<template v-if="activeTab === 'buy'">
|
<template v-if="activeTab === 'buy'">
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
@ -844,7 +617,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">To win</span
|
<span class="label">To win</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{ toWinValue }}</span
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> $20</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -852,29 +625,15 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">You'll receive</span
|
<span class="label">You'll receive</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> ${{
|
||||||
totalPrice
|
totalPrice
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="activeTab === 'buy' && showDepositForBuy"
|
|
||||||
class="deposit-btn"
|
|
||||||
@click="deposit"
|
|
||||||
>
|
|
||||||
Deposit
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-else
|
|
||||||
class="action-btn"
|
class="action-btn"
|
||||||
:loading="orderLoading"
|
:loading="orderLoading"
|
||||||
:disabled="orderLoading"
|
:disabled="orderLoading"
|
||||||
@ -889,18 +648,16 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Buy: Amount Section -->
|
|
||||||
<template v-if="activeTab === 'buy'">
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="amount-header">
|
<div class="amount-header">
|
||||||
<div>
|
<div>
|
||||||
@ -915,66 +672,9 @@
|
|||||||
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
<v-btn class="amount-btn" @click="adjustAmount(100)">+$100</v-btn>
|
||||||
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
<v-btn class="amount-btn" @click="setMaxAmount">Max</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="amount > 0" class="total-row amount-to-win-row">
|
|
||||||
<span class="label">To win</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ toWinValue }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<v-btn class="deposit-btn" @click="deposit">Deposit</v-btn>
|
<v-btn class="deposit-btn" @click="deposit">Deposit</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<!-- Sell: Shares + To receive + Avg. Price -->
|
|
||||||
<template v-else>
|
|
||||||
<div class="input-group shares-group">
|
|
||||||
<div class="shares-header">
|
|
||||||
<span class="label">Shares</span>
|
|
||||||
<div class="shares-input">
|
|
||||||
<v-text-field
|
|
||||||
:model-value="shares"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
class="shares-input-field"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="onSharesInput"
|
|
||||||
@keydown="onSharesKeydown"
|
|
||||||
@paste="onSharesPaste"
|
|
||||||
></v-text-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="shares-buttons">
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(25)">25%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
|
||||||
<v-btn class="share-btn" @click="setSharesPercentage(100)">Max</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="total-section">
|
|
||||||
<div class="total-row">
|
|
||||||
<span class="label">You'll receive</span>
|
|
||||||
<span class="to-win-value">
|
|
||||||
<v-icon size="16" color="green">mdi-currency-usd</v-icon>
|
|
||||||
{{ totalPrice }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="total-row avg-price-row">
|
|
||||||
<span class="label">
|
|
||||||
Avg. Price {{ avgPriceCents }}¢
|
|
||||||
<v-icon size="14" class="info-icon">mdi-information-outline</v-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<v-btn
|
|
||||||
class="action-btn"
|
|
||||||
:loading="orderLoading"
|
|
||||||
:disabled="orderLoading"
|
|
||||||
@click="submitOrder"
|
|
||||||
>
|
|
||||||
{{ actionButtonText }}
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="price-options hide-in-mobile-sheet">
|
<div class="price-options hide-in-mobile-sheet">
|
||||||
@ -983,14 +683,14 @@
|
|||||||
:class="{ active: selectedOption === 'yes' }"
|
:class="{ active: selectedOption === 'yes' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('yes')"
|
@click="handleOptionChange('yes')"
|
||||||
>{{ yesLabel }} {{ yesPriceCents }}¢</v-btn
|
>Yes {{ yesPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="no-btn"
|
class="no-btn"
|
||||||
:class="{ active: selectedOption === 'no' }"
|
:class="{ active: selectedOption === 'no' }"
|
||||||
text
|
text
|
||||||
@click="handleOptionChange('no')"
|
@click="handleOptionChange('no')"
|
||||||
>{{ noLabel }} {{ noPriceCents }}¢</v-btn
|
>No {{ noPriceCents }}¢</v-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group limit-price-group">
|
<div class="input-group limit-price-group">
|
||||||
@ -1075,7 +775,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">To win</span
|
<span class="label">To win</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{ toWinValue }}</span
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> $20</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -1083,7 +783,7 @@
|
|||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">You'll receive</span
|
<span class="label">You'll receive</span
|
||||||
><span class="to-win-value"
|
><span class="to-win-value"
|
||||||
><v-icon size="16" color="green">mdi-currency-usd</v-icon> {{
|
><v-icon size="16" color="green">mdi-currency-usd</v-icon> ${{
|
||||||
totalPrice
|
totalPrice
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
@ -1112,7 +812,7 @@
|
|||||||
content-class="merge-dialog"
|
content-class="merge-dialog"
|
||||||
transition="dialog-transition"
|
transition="dialog-transition"
|
||||||
>
|
>
|
||||||
<v-card class="merge-dialog-card" rounded="lg" elevation="0">
|
<v-card class="merge-dialog-card" rounded="lg">
|
||||||
<div class="merge-dialog-header">
|
<div class="merge-dialog-header">
|
||||||
<h3 class="merge-dialog-title">Merge shares</h3>
|
<h3 class="merge-dialog-title">Merge shares</h3>
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -1127,8 +827,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<v-card-text class="merge-dialog-body">
|
<v-card-text class="merge-dialog-body">
|
||||||
<p class="merge-dialog-desc">
|
<p class="merge-dialog-desc">
|
||||||
Merge a share of {{ yesLabel }} and {{ noLabel }} to get 1 USDC. You can do this to save
|
Merge a share of Yes and No to get 1 USDC. You can do this to save cost when trying to get
|
||||||
cost when trying to get rid of a position.
|
rid of a position.
|
||||||
</p>
|
</p>
|
||||||
<div class="merge-amount-row">
|
<div class="merge-amount-row">
|
||||||
<label class="merge-amount-label">Amount</label>
|
<label class="merge-amount-label">Amount</label>
|
||||||
@ -1147,7 +847,7 @@
|
|||||||
<button type="button" class="merge-max-link" @click="setMergeMax">Max</button>
|
<button type="button" class="merge-max-link" @click="setMergeMax">Max</button>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="!props.market?.marketId" class="merge-no-market">
|
<p v-if="!props.market?.marketId" class="merge-no-market">
|
||||||
Please select a market first (e.g. click Buy {{ yesLabel }}/{{ noLabel }} on a market).
|
Please select a market first (e.g. click Buy Yes/No on a market).
|
||||||
</p>
|
</p>
|
||||||
<p v-if="mergeError" class="merge-error">{{ mergeError }}</p>
|
<p v-if="mergeError" class="merge-error">{{ mergeError }}</p>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@ -1175,7 +875,7 @@
|
|||||||
content-class="split-dialog"
|
content-class="split-dialog"
|
||||||
transition="dialog-transition"
|
transition="dialog-transition"
|
||||||
>
|
>
|
||||||
<v-card class="split-dialog-card" rounded="lg" elevation="0">
|
<v-card class="split-dialog-card" rounded="lg">
|
||||||
<div class="split-dialog-header">
|
<div class="split-dialog-header">
|
||||||
<h3 class="split-dialog-title">Split</h3>
|
<h3 class="split-dialog-title">Split</h3>
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -1190,8 +890,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<v-card-text class="split-dialog-body">
|
<v-card-text class="split-dialog-body">
|
||||||
<p class="split-dialog-desc">
|
<p class="split-dialog-desc">
|
||||||
Use USDC to get one share of {{ yesLabel }} and one share of {{ noLabel }} for this
|
Use USDC to get one share of Yes and one share of No for this market. 1 USDC ≈ 1 complete
|
||||||
market. 1 USDC ≈ 1 complete set.
|
set.
|
||||||
</p>
|
</p>
|
||||||
<div class="split-amount-row">
|
<div class="split-amount-row">
|
||||||
<label class="split-amount-label">Amount (USDC)</label>
|
<label class="split-amount-label">Amount (USDC)</label>
|
||||||
@ -1207,7 +907,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="!props.market?.marketId" class="split-no-market">
|
<p v-if="!props.market?.marketId" class="split-no-market">
|
||||||
Please select a market first (e.g. click Buy {{ yesLabel }}/{{ noLabel }} on a market).
|
Please select a market first (e.g. click Buy Yes/No on a market).
|
||||||
</p>
|
</p>
|
||||||
<p v-if="splitError" class="split-error">{{ splitError }}</p>
|
<p v-if="splitError" class="split-error">{{ splitError }}</p>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@ -1281,8 +981,6 @@ export interface TradeMarketPayload {
|
|||||||
title?: string
|
title?: string
|
||||||
/** 与 outcomes/outcomePrices 顺序一致,用于下单 tokenId:0=Yes 1=No */
|
/** 与 outcomes/outcomePrices 顺序一致,用于下单 tokenId:0=Yes 1=No */
|
||||||
clobTokenIds?: string[]
|
clobTokenIds?: string[]
|
||||||
/** 选项展示文案,如 ["Yes","No"] 或 ["Up","Down"],用于 Buy Yes/No 按钮文字 */
|
|
||||||
outcomes?: string[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
@ -1373,8 +1071,6 @@ defineExpose({ openMergeDialog, openSplitDialog })
|
|||||||
|
|
||||||
const yesPriceCents = computed(() => (props.market ? Math.round(props.market.yesPrice * 100) : 19))
|
const yesPriceCents = computed(() => (props.market ? Math.round(props.market.yesPrice * 100) : 19))
|
||||||
const noPriceCents = computed(() => (props.market ? Math.round(props.market.noPrice * 100) : 82))
|
const noPriceCents = computed(() => (props.market ? Math.round(props.market.noPrice * 100) : 82))
|
||||||
const yesLabel = computed(() => props.market?.outcomes?.[0] ?? 'Yes')
|
|
||||||
const noLabel = computed(() => props.market?.outcomes?.[1] ?? 'No')
|
|
||||||
|
|
||||||
function openSheet(option: 'yes' | 'no') {
|
function openSheet(option: 'yes' | 'no') {
|
||||||
handleOptionChange(option)
|
handleOptionChange(option)
|
||||||
@ -1394,12 +1090,7 @@ const expirationOptions = ref(['5m', '15m', '30m', '1h', '2h', '4h', '8h', '12h'
|
|||||||
// Market mode state
|
// Market mode state
|
||||||
const isMarketMode = computed(() => limitType.value === 'Market')
|
const isMarketMode = computed(() => limitType.value === 'Market')
|
||||||
const amount = ref(0) // Market mode amount
|
const amount = ref(0) // Market mode amount
|
||||||
/** 余额(从 userStore 同步) */
|
const balance = ref(0) // Market mode balance
|
||||||
const balance = computed(() => {
|
|
||||||
const s = userStore.balance
|
|
||||||
const n = parseFloat(String(s ?? '0'))
|
|
||||||
return Number.isFinite(n) ? n : 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const orderLoading = ref(false)
|
const orderLoading = ref(false)
|
||||||
const orderError = ref('')
|
const orderError = ref('')
|
||||||
@ -1429,30 +1120,8 @@ const totalPrice = computed(() => {
|
|||||||
return (limitPrice.value * shares.value).toFixed(2)
|
return (limitPrice.value * shares.value).toFixed(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Sell 模式下的平均单价(¢) */
|
|
||||||
const avgPriceCents = computed(() => {
|
|
||||||
const p = limitPrice.value
|
|
||||||
return (p * 100).toFixed(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
/** To win = 份数 × 1U = shares × 1 USDC */
|
|
||||||
const toWinValue = computed(() => {
|
|
||||||
if (isMarketMode.value) {
|
|
||||||
const price =
|
|
||||||
selectedOption.value === 'yes'
|
|
||||||
? (props.market?.yesPrice ?? 0.5)
|
|
||||||
: (props.market?.noPrice ?? 0.5)
|
|
||||||
const sharesFromAmount = price > 0 ? amount.value / price : 0
|
|
||||||
return sharesFromAmount.toFixed(2)
|
|
||||||
}
|
|
||||||
return (shares.value * 1).toFixed(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
const actionButtonText = computed(() => {
|
const actionButtonText = computed(() => {
|
||||||
const label = selectedOption.value === 'yes' ? yesLabel.value : noLabel.value
|
return `${activeTab.value} ${selectedOption.value.charAt(0).toUpperCase() + selectedOption.value.slice(1)}`
|
||||||
const tab = activeTab.value
|
|
||||||
const tabCapitalized = tab.charAt(0).toUpperCase() + tab.slice(1)
|
|
||||||
return `${tabCapitalized} ${label}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function applyInitialOption(option: 'yes' | 'no') {
|
function applyInitialOption(option: 'yes' | 'no') {
|
||||||
@ -1606,17 +1275,6 @@ const setMaxAmount = () => {
|
|||||||
amount.value = balance.value
|
amount.value = balance.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Buy 模式:余额是否足够(>= 所需金额且不为 0)。cost:balance>0 时用 totalPrice,否则用 amount */
|
|
||||||
const canAffordBuy = computed(() => {
|
|
||||||
const bal = balance.value
|
|
||||||
if (bal <= 0) return false
|
|
||||||
const cost = bal > 0 ? parseFloat(totalPrice.value) || 0 : amount.value
|
|
||||||
return bal >= cost
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Buy 模式且余额不足时显示 Deposit,否则显示 Buy Yes/No */
|
|
||||||
const showDepositForBuy = computed(() => !canAffordBuy.value)
|
|
||||||
|
|
||||||
const deposit = () => {
|
const deposit = () => {
|
||||||
console.log('Depositing amount:', amount.value)
|
console.log('Depositing amount:', amount.value)
|
||||||
// 实际应用中,这里应该调用存款API
|
// 实际应用中,这里应该调用存款API
|
||||||
@ -1715,38 +1373,11 @@ async function submitOrder() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 扁平化:移除所有阴影 */
|
|
||||||
.trade-component,
|
|
||||||
.trade-sheet-paper,
|
|
||||||
.merge-dialog-card,
|
|
||||||
.split-dialog-card {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trade-component :deep(.v-btn),
|
|
||||||
.trade-component :deep(.v-btn::before),
|
|
||||||
.trade-sheet-inner :deep(.v-btn),
|
|
||||||
.trade-sheet-inner :deep(.v-btn::before),
|
|
||||||
.merge-dialog-card .v-btn,
|
|
||||||
.merge-dialog-card .v-btn::before,
|
|
||||||
.split-dialog-card .v-btn,
|
|
||||||
.split-dialog-card .v-btn::before {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trade-component :deep(.v-field),
|
|
||||||
.trade-sheet-inner :deep(.v-field),
|
|
||||||
.merge-dialog-card :deep(.v-field),
|
|
||||||
.split-dialog-card :deep(.v-field) {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trade-component {
|
.trade-component {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid #e7e7e7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -1779,7 +1410,6 @@ async function submitOrder() {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-item {
|
.more-item {
|
||||||
@ -1801,7 +1431,6 @@ async function submitOrder() {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.yes-btn.active {
|
.yes-btn.active {
|
||||||
@ -1816,12 +1445,11 @@ async function submitOrder() {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-btn.active {
|
.no-btn.active {
|
||||||
background-color: #f0b8b8;
|
background-color: #cc0000;
|
||||||
color: #cc0000;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
@ -1953,7 +1581,6 @@ async function submitOrder() {
|
|||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.matching-info {
|
.matching-info {
|
||||||
@ -1964,17 +1591,6 @@ async function submitOrder() {
|
|||||||
color: #3d8b40;
|
color: #3d8b40;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avg-price-row {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avg-price-row .info-icon {
|
|
||||||
margin-left: 4px;
|
|
||||||
vertical-align: middle;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expiration-header {
|
.expiration-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -1993,10 +1609,6 @@ async function submitOrder() {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expiration-select :deep(.v-field) {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.total-section {
|
.total-section {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-top: 1px solid #e0e0e0;
|
border-top: 1px solid #e0e0e0;
|
||||||
@ -2026,12 +1638,11 @@ async function submitOrder() {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #0066cc;
|
background-color: #0066cc;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-radius: 6px;
|
border-radius: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Market Mode Styles */
|
/* Market Mode Styles */
|
||||||
@ -2074,32 +1685,17 @@ async function submitOrder() {
|
|||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount-to-win-row {
|
|
||||||
margin-top: 12px;
|
|
||||||
padding-top: 12px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.deposit-btn {
|
.deposit-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #0066cc;
|
background-color: #0066cc;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-radius: 6px;
|
border-radius: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deposit-btn--secondary {
|
|
||||||
margin-top: 8px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #0066cc;
|
|
||||||
border: 1px solid #0066cc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 移动端底部交易栏(红框样式) */
|
/* 移动端底部交易栏(红框样式) */
|
||||||
@ -2227,7 +1823,6 @@ async function submitOrder() {
|
|||||||
.merge-dialog-card {
|
.merge-dialog-card {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.merge-dialog-header {
|
.merge-dialog-header {
|
||||||
@ -2330,14 +1925,12 @@ async function submitOrder() {
|
|||||||
.merge-submit-btn {
|
.merge-submit-btn {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Split dialog */
|
/* Split dialog */
|
||||||
.split-dialog-card {
|
.split-dialog-card {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
.split-dialog-header {
|
.split-dialog-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -2400,6 +1993,5 @@ async function submitOrder() {
|
|||||||
.split-submit-btn {
|
.split-submit-btn {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { getUsdcBalance, formatUsdcBalance, getUserInfo } from '@/api/user'
|
import { getUsdcBalance, formatUsdcBalance } from '@/api/user'
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
/** 用户 ID(API 可能返回 id 或 ID) */
|
/** 用户 ID(API 可能返回 id 或 ID) */
|
||||||
@ -89,30 +89,6 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 请求用户信息(需已登录),更新 store 中的 user */
|
|
||||||
async function fetchUserInfo() {
|
|
||||||
const headers = getAuthHeaders()
|
|
||||||
if (!headers) return
|
|
||||||
try {
|
|
||||||
const res = await getUserInfo(headers)
|
|
||||||
console.log('[fetchUserInfo] 接口响应:', JSON.stringify(res, null, 2))
|
|
||||||
if ((res.code === 0 || res.code === 200) && res.data) {
|
|
||||||
const u = res.data
|
|
||||||
user.value = {
|
|
||||||
id: u.ID,
|
|
||||||
ID: u.ID,
|
|
||||||
userName: u.userName,
|
|
||||||
nickName: u.nickName,
|
|
||||||
headerImg: u.headerImg,
|
|
||||||
...u,
|
|
||||||
}
|
|
||||||
if (token.value && user.value) saveToStorage(token.value, user.value)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[fetchUserInfo] 请求失败:', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
user,
|
user,
|
||||||
@ -123,6 +99,5 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
logout,
|
logout,
|
||||||
getAuthHeaders,
|
getAuthHeaders,
|
||||||
fetchUsdcBalance,
|
fetchUsdcBalance,
|
||||||
fetchUserInfo,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -86,7 +86,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openTrade(market, index, 'yes')"
|
@click="openTrade(market, index, 'yes')"
|
||||||
>
|
>
|
||||||
{{ yesLabel(market) }} {{ yesPrice(market) }}
|
Yes {{ yesPrice(market) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="buy-no-btn"
|
class="buy-no-btn"
|
||||||
@ -95,7 +95,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openTrade(market, index, 'no')"
|
@click="openTrade(market, index, 'no')"
|
||||||
>
|
>
|
||||||
{{ noLabel(market) }} {{ noPrice(market) }}
|
No {{ noPrice(market) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -125,7 +125,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openSheetWithOption('yes')"
|
@click="openSheetWithOption('yes')"
|
||||||
>
|
>
|
||||||
{{ barMarket ? yesLabel(barMarket) : 'Yes' }} {{ barMarket ? yesPrice(barMarket) : '0¢' }}
|
Yes {{ barMarket ? yesPrice(barMarket) : '0¢' }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="mobile-bar-btn mobile-bar-no"
|
class="mobile-bar-btn mobile-bar-no"
|
||||||
@ -133,7 +133,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openSheetWithOption('no')"
|
@click="openSheetWithOption('no')"
|
||||||
>
|
>
|
||||||
{{ barMarket ? noLabel(barMarket) : 'No' }} {{ barMarket ? noPrice(barMarket) : '0¢' }}
|
No {{ barMarket ? noPrice(barMarket) : '0¢' }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-menu
|
<v-menu
|
||||||
v-model="mobileMenuOpen"
|
v-model="mobileMenuOpen"
|
||||||
@ -239,7 +239,6 @@ const tradeMarketPayload = computed(() => {
|
|||||||
noPrice,
|
noPrice,
|
||||||
title: m.question,
|
title: m.question,
|
||||||
clobTokenIds: m.clobTokenIds,
|
clobTokenIds: m.clobTokenIds,
|
||||||
outcomes: m.outcomes,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -567,14 +566,6 @@ function marketChance(market: PmEventMarketItem): number {
|
|||||||
return Math.min(100, Math.max(0, Math.round(yesPrice * 100)))
|
return Math.min(100, Math.max(0, Math.round(yesPrice * 100)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function yesLabel(market: PmEventMarketItem): string {
|
|
||||||
return market?.outcomes?.[0] ?? 'Yes'
|
|
||||||
}
|
|
||||||
|
|
||||||
function noLabel(market: PmEventMarketItem): string {
|
|
||||||
return market?.outcomes?.[1] ?? 'No'
|
|
||||||
}
|
|
||||||
|
|
||||||
function yesPrice(market: PmEventMarketItem): string {
|
function yesPrice(market: PmEventMarketItem): string {
|
||||||
const raw = market?.outcomePrices?.[0]
|
const raw = market?.outcomePrices?.[0]
|
||||||
if (raw == null) return '0¢'
|
if (raw == null) return '0¢'
|
||||||
@ -743,14 +734,13 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分时图卡片(扁平化) */
|
/* 分时图卡片 */
|
||||||
.chart-card.polymarket-chart {
|
.chart-card.polymarket-chart {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 20px 24px 16px;
|
padding: 20px 24px 16px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #e7e7e7;
|
border: 1px solid #e7e7e7;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-header {
|
.chart-header {
|
||||||
@ -894,7 +884,6 @@ watch(
|
|||||||
border: 1px solid #e7e7e7;
|
border: 1px solid #e7e7e7;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.markets-list {
|
.markets-list {
|
||||||
|
|||||||
@ -506,14 +506,12 @@ const tradeDialogMarket = ref<{
|
|||||||
title: string
|
title: string
|
||||||
marketId?: string
|
marketId?: string
|
||||||
clobTokenIds?: string[]
|
clobTokenIds?: string[]
|
||||||
yesLabel?: string
|
|
||||||
noLabel?: string
|
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
const scrollRef = ref<HTMLElement | null>(null)
|
const scrollRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
function onCardOpenTrade(
|
function onCardOpenTrade(
|
||||||
side: 'yes' | 'no',
|
side: 'yes' | 'no',
|
||||||
market?: { id: string; title: string; marketId?: string; yesLabel?: string; noLabel?: string },
|
market?: { id: string; title: string; marketId?: string },
|
||||||
) {
|
) {
|
||||||
tradeDialogSide.value = side
|
tradeDialogSide.value = side
|
||||||
tradeDialogMarket.value = market ?? null
|
tradeDialogMarket.value = market ?? null
|
||||||
@ -528,11 +526,7 @@ const homeTradeMarketPayload = computed(() => {
|
|||||||
const chance = 50
|
const chance = 50
|
||||||
const yesPrice = Math.min(1, Math.max(0, chance / 100))
|
const yesPrice = Math.min(1, Math.max(0, chance / 100))
|
||||||
const noPrice = 1 - yesPrice
|
const noPrice = 1 - yesPrice
|
||||||
const outcomes =
|
return { marketId, yesPrice, noPrice, title: m.title, clobTokenIds: m.clobTokenIds }
|
||||||
m.yesLabel != null || m.noLabel != null
|
|
||||||
? [m.yesLabel ?? 'Yes', m.noLabel ?? 'No']
|
|
||||||
: undefined
|
|
||||||
return { marketId, yesPrice, noPrice, title: m.title, clobTokenIds: m.clobTokenIds, outcomes }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const sentinelRef = ref<HTMLElement | null>(null)
|
const sentinelRef = ref<HTMLElement | null>(null)
|
||||||
|
|||||||
@ -177,13 +177,13 @@ const connectWithWallet = async () => {
|
|||||||
walletAddress,
|
walletAddress,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
// 调试:打印登录信息数据结构
|
console.log('Login API response:', loginData)
|
||||||
console.log('[walletLogin] 登录响应:', JSON.stringify(loginData, null, 2))
|
|
||||||
|
|
||||||
if (loginData.code === 0 && loginData.data) {
|
if (loginData.code === 0 && loginData.data) {
|
||||||
const { token, user } = loginData.data
|
userStore.setUser({
|
||||||
console.log('[walletLogin] 存入 store 的 user:', JSON.stringify(user, null, 2))
|
token: loginData.data.token,
|
||||||
userStore.setUser({ token, user })
|
user: loginData.data.user,
|
||||||
|
})
|
||||||
userStore.fetchUsdcBalance()
|
userStore.fetchUsdcBalance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -130,7 +130,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openSheetWithOption('yes')"
|
@click="openSheetWithOption('yes')"
|
||||||
>
|
>
|
||||||
{{ yesLabel }} {{ yesPriceCents }}¢
|
Yes {{ yesPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
class="mobile-bar-btn mobile-bar-no"
|
class="mobile-bar-btn mobile-bar-no"
|
||||||
@ -138,7 +138,7 @@
|
|||||||
rounded="sm"
|
rounded="sm"
|
||||||
@click="openSheetWithOption('no')"
|
@click="openSheetWithOption('no')"
|
||||||
>
|
>
|
||||||
{{ noLabel }} {{ noPriceCents }}¢
|
No {{ noPriceCents }}¢
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-menu
|
<v-menu
|
||||||
v-model="mobileMenuOpen"
|
v-model="mobileMenuOpen"
|
||||||
@ -332,7 +332,6 @@ const tradeMarketPayload = computed(() => {
|
|||||||
noPrice,
|
noPrice,
|
||||||
title: m.question,
|
title: m.question,
|
||||||
clobTokenIds: m.clobTokenIds,
|
clobTokenIds: m.clobTokenIds,
|
||||||
outcomes: m.outcomes,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const qId = route.query.marketId
|
const qId = route.query.marketId
|
||||||
@ -373,8 +372,6 @@ const yesPriceCents = computed(() =>
|
|||||||
const noPriceCents = computed(() =>
|
const noPriceCents = computed(() =>
|
||||||
tradeMarketPayload.value ? Math.round(tradeMarketPayload.value.noPrice * 100) : 0
|
tradeMarketPayload.value ? Math.round(tradeMarketPayload.value.noPrice * 100) : 0
|
||||||
)
|
)
|
||||||
const yesLabel = computed(() => currentMarket.value?.outcomes?.[0] ?? 'Yes')
|
|
||||||
const noLabel = computed(() => currentMarket.value?.outcomes?.[1] ?? 'No')
|
|
||||||
|
|
||||||
function openSheetWithOption(side: 'yes' | 'no') {
|
function openSheetWithOption(side: 'yes' | 'no') {
|
||||||
tradeInitialOptionFromBar.value = side
|
tradeInitialOptionFromBar.value = side
|
||||||
@ -924,14 +921,13 @@ onUnmounted(() => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Polymarket 样式分时图卡片(扁平化) */
|
/* Polymarket 样式分时图卡片 */
|
||||||
.chart-card.polymarket-chart {
|
.chart-card.polymarket-chart {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
padding: 20px 24px 16px;
|
padding: 20px 24px 16px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #e7e7e7;
|
border: 1px solid #e7e7e7;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-header {
|
.chart-header {
|
||||||
@ -1031,12 +1027,11 @@ onUnmounted(() => {
|
|||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Order Book Card Styles(扁平化) */
|
/* Order Book Card Styles */
|
||||||
.order-book-card {
|
.order-book-card {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #e7e7e7;
|
border: 1px solid #e7e7e7;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左右布局:左侧弹性,右侧固定 */
|
/* 左右布局:左侧弹性,右侧固定 */
|
||||||
@ -1146,12 +1141,11 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Comments / Top Holders / Activity(扁平化) */
|
/* Comments / Top Holders / Activity */
|
||||||
.activity-card {
|
.activity-card {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-tabs {
|
.detail-tabs {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user