优化:文档更新
This commit is contained in:
parent
a29dac0146
commit
fa5e0dccfe
@ -8,7 +8,7 @@ Event(预测市场事件)相关接口与类型定义,对接 XTrader API
|
|||||||
|
|
||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- `getPmEventPublic`:分页获取公开事件列表(无需鉴权)
|
- `getPmEventPublic`:分页获取公开事件列表(无需鉴权);请求时固定传入 **startDateMax**、**endDateMin** 为当前时间戳(Unix 秒),**startDateMin**、**endDateMax** 不传
|
||||||
- `findPmEvent`:按 id/slug 查询事件详情(需鉴权)
|
- `findPmEvent`:按 id/slug 查询事件详情(需鉴权)
|
||||||
- `mapEventItemToCard`:将 `PmEventListItem` 转为 `EventCardItem`(供 MarketCard 使用)
|
- `mapEventItemToCard`:将 `PmEventListItem` 转为 `EventCardItem`(供 MarketCard 使用)
|
||||||
- 内存缓存:`getEventListCache`、`setEventListCache`、`clearEventListCache`,用于列表切换页面时复用
|
- 内存缓存:`getEventListCache`、`setEventListCache`、`clearEventListCache`,用于列表切换页面时复用
|
||||||
|
|||||||
87
docs/api/historyRecord.md
Normal file
87
docs/api/historyRecord.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# historyRecord.ts
|
||||||
|
|
||||||
|
**路径**:`src/api/historyRecord.ts`
|
||||||
|
|
||||||
|
## 功能用途
|
||||||
|
|
||||||
|
历史记录接口,用于 Wallet 页「History」Tab。**Wallet 使用 GET /hr/getHistoryRecordListClient**(需鉴权、按当前用户分页);另提供 GET /hr/getHistoryRecordPublic(不需要鉴权)备用。
|
||||||
|
|
||||||
|
## 核心能力
|
||||||
|
|
||||||
|
- `getHistoryRecordListClient`:客户端分页获取历史记录(需鉴权,传 userId、page、pageSize)
|
||||||
|
- `getHistoryRecordPublic`:分页获取历史记录(无需鉴权)
|
||||||
|
- `getHistoryRecordList`:从响应 data 中解析 list 并映射为 `HistoryDisplayItem[]`,同时返回 total
|
||||||
|
- `mapHistoryRecordToDisplayItem`:单条 `HistoryRecordItem` → 钱包展示项(与 Wallet.vue HistoryItem 一致)
|
||||||
|
|
||||||
|
## GET /hr/getHistoryRecordListClient(Wallet 使用)
|
||||||
|
|
||||||
|
需鉴权:x-token、x-user-id。
|
||||||
|
|
||||||
|
### 请求参数(Query)
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| page | number | 页码 |
|
||||||
|
| pageSize | number | 每页条数 |
|
||||||
|
| userId | number | 用户 ID |
|
||||||
|
| keyword | string | 关键字 |
|
||||||
|
| slug | string | 标识 |
|
||||||
|
| title | string | 标题 |
|
||||||
|
| name | string | 名称 |
|
||||||
|
| bio | string | 简介 |
|
||||||
|
| createdAtRange | string[] | 创建时间范围 |
|
||||||
|
|
||||||
|
### 响应
|
||||||
|
|
||||||
|
`{ code, data, msg }`,`data` 为 `PageResult<HistoryRecordItem>`(list、page、pageSize、total)。
|
||||||
|
|
||||||
|
## GET /hr/getHistoryRecordPublic
|
||||||
|
|
||||||
|
### 请求参数(Query,可选)
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| page | number | 页码 |
|
||||||
|
| pageSize | number | 每页条数 |
|
||||||
|
| keyword | string | 关键字 |
|
||||||
|
| slug | string | 标识 |
|
||||||
|
| title | string | 标题 |
|
||||||
|
| name | string | 名称 |
|
||||||
|
| bio | string | 简介 |
|
||||||
|
| createdAtRange | string[] | 创建时间范围 |
|
||||||
|
|
||||||
|
### 响应
|
||||||
|
|
||||||
|
`{ code, data, msg }`。`data` 可能为 `PageResult<HistoryRecordItem>`(list、page、pageSize、total)或 `HistoryRecordItem[]`。
|
||||||
|
|
||||||
|
### HistoryRecordItem(与 doc.json polymarket.HistoryRecord 对齐)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| ID | number | 主键 |
|
||||||
|
| title | string | 标题 |
|
||||||
|
| name | string | 名称 |
|
||||||
|
| eventSlug | string | 事件标识 |
|
||||||
|
| outcome | string | 结果(Yes/No 等) |
|
||||||
|
| side | string | 方向 |
|
||||||
|
| type | string | 类型 |
|
||||||
|
| price | number | 价格 |
|
||||||
|
| size | number | 大小 |
|
||||||
|
| createdAt | string | 创建时间 |
|
||||||
|
| timestamp | number | 时间戳(秒) |
|
||||||
|
| 其他 | - | asset, bio, conditionId, icon, slug, transactionHash 等 |
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { getHistoryRecordPublic, getHistoryRecordList } from '@/api/historyRecord'
|
||||||
|
|
||||||
|
const res = await getHistoryRecordPublic({ page: 1, pageSize: 10 })
|
||||||
|
const { list, total } = getHistoryRecordList(res.data)
|
||||||
|
// list 为 HistoryDisplayItem[],可直接用于 Wallet 历史列表
|
||||||
|
```
|
||||||
|
|
||||||
|
## 扩展方式
|
||||||
|
|
||||||
|
- 若后端固定返回分页结构,可收紧 `HistoryRecordPublicResponse['data']` 类型为 `PageResult<HistoryRecordItem>`
|
||||||
|
- 展示字段(market、activity、value、timeAgo 等)可在 `mapHistoryRecordToDisplayItem` 中按实际接口字段再调整
|
||||||
27
docs/api/jwt.md
Normal file
27
docs/api/jwt.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# jwt.ts
|
||||||
|
|
||||||
|
**路径**:`src/api/jwt.ts`
|
||||||
|
|
||||||
|
## 功能用途
|
||||||
|
|
||||||
|
JWT 相关接口,用于退出登录时将当前 token 加入黑名单,使 token 失效。
|
||||||
|
|
||||||
|
## 核心能力
|
||||||
|
|
||||||
|
- `jsonInBlacklist`:POST /jwt/jsonInBlacklist,需鉴权(x-token、x-user-id)
|
||||||
|
|
||||||
|
## POST /jwt/jsonInBlacklist
|
||||||
|
|
||||||
|
### 请求
|
||||||
|
|
||||||
|
- 方法:POST
|
||||||
|
- 鉴权:需在 headers 中传 x-token、x-user-id
|
||||||
|
- Body:无
|
||||||
|
|
||||||
|
### 响应
|
||||||
|
|
||||||
|
`{ code, msg }`,标准 ApiResponse。
|
||||||
|
|
||||||
|
### 使用场景
|
||||||
|
|
||||||
|
退出登录时由 `useUserStore().logout()` 调用,在清空本地 token 前先请求该接口,使服务端将当前 JWT 加入黑名单。若请求失败(如网络错误、401),仍会执行本地登出。
|
||||||
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- `getPositionList`:分页获取持仓列表(需鉴权 x-token、x-user-id);返回项含 `needClaim`、`market`(内嵌市场 question、outcomes、outcomePrices)
|
- `getPositionList`:分页获取持仓列表(需鉴权 x-token、x-user-id);返回项含 `market`(内嵌市场 question、outcomes、outcomePrices、**closed**)
|
||||||
- `mapPositionToDisplayItem`:将接口项转为展示结构;`market` 优先用 `market.question`,否则用 marketID;`avgNow` 有 `market.outcomePrices` 时展示「AVG → NOW」格式;`iconChar`/`iconClass`/`imageUrl` 用于展示图标(market.image 优先)
|
- `mapPositionToDisplayItem`:将接口项转为展示结构;`market` 优先用 `market.question`,否则用 marketID;`avgNow` 有 `market.outcomePrices` 时展示「AVG → NOW」格式;`iconChar`/`iconClass`/`imageUrl` 用于展示图标(market.image 优先);**marketClosed** 取自 `market.closed`,用于钱包侧判断可结算/可领取
|
||||||
|
|
||||||
## GET /clob/position/getPositionList
|
## GET /clob/position/getPositionList
|
||||||
|
|
||||||
@ -43,8 +43,7 @@
|
|||||||
| cost | string | 成本 |
|
| cost | string | 成本 |
|
||||||
| outcome | string | 方向 |
|
| outcome | string | 方向 |
|
||||||
| version | number | 版本号 |
|
| version | number | 版本号 |
|
||||||
| needClaim | boolean | 是否待领取结算 |
|
| market | ClobPositionMarket | 内嵌市场详情(question、outcomes、outcomePrices、closed 等) |
|
||||||
| market | ClobPositionMarket | 内嵌市场详情(question、outcomes、outcomePrices 等) |
|
|
||||||
| createdAt | string | 创建时间 |
|
| createdAt | string | 创建时间 |
|
||||||
| updatedAt | string | 更新时间 |
|
| updatedAt | string | 更新时间 |
|
||||||
|
|
||||||
@ -60,6 +59,7 @@
|
|||||||
| outcomes | string[] | 选项(如 ["Up", "Down"]) |
|
| outcomes | string[] | 选项(如 ["Up", "Down"]) |
|
||||||
| outcomePrices | string[] \| number[] | 各选项当前价格 |
|
| outcomePrices | string[] \| number[] | 各选项当前价格 |
|
||||||
| clobTokenIds | string[] | CLOB Token ID 列表 |
|
| clobTokenIds | string[] | CLOB Token ID 列表 |
|
||||||
|
| closed | boolean | 市场是否已关闭,true 表示可结算/可领取 |
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
|
|||||||
69
docs/api/priceHistory.md
Normal file
69
docs/api/priceHistory.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# priceHistory.ts
|
||||||
|
|
||||||
|
**路径**:`src/api/priceHistory.ts`
|
||||||
|
|
||||||
|
## 功能用途
|
||||||
|
|
||||||
|
价格历史公开接口,用于 TradeDetail 页面的 Yes/No 折线图。对接 **GET /pmPriceHistory/getPmPriceHistoryPublic**(无需鉴权)。
|
||||||
|
|
||||||
|
## 核心能力
|
||||||
|
|
||||||
|
- `getPmPriceHistoryPublic`:按市场 ID 分页获取价格历史
|
||||||
|
- `priceHistoryToChartData`:将接口返回的 `list` 转为 ECharts 使用的 `[timestamp_ms, value_0_100][]`
|
||||||
|
|
||||||
|
## GET /pmPriceHistory/getPmPriceHistoryPublic
|
||||||
|
|
||||||
|
### 请求参数(Query)
|
||||||
|
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| market | string | 是 | 传 YES 对应的 clobTokenId(即当前市场 clobTokenIds[0]) |
|
||||||
|
| page | number | 否 | 页码,默认 1 |
|
||||||
|
| pageSize | number | 否 | 每页条数,默认 500 |
|
||||||
|
| interval | string | 否 | 数据间隔 |
|
||||||
|
| time | number | 否 | 时间筛选 |
|
||||||
|
| createdAtRange | string[] | 否 | 创建时间范围 |
|
||||||
|
| fidelity | number | 否 | 数据精度 |
|
||||||
|
| keyword | string | 否 | 关键字 |
|
||||||
|
| order | string | 否 | 排序 |
|
||||||
|
| sort | string | 否 | 排序字段 |
|
||||||
|
| price | number | 否 | 价格筛选 |
|
||||||
|
|
||||||
|
### 响应
|
||||||
|
|
||||||
|
`{ code, data, msg }`,`data` 为 `PageResult<PmPriceHistoryItem>`(`list`、`page`、`pageSize`、`total`)。
|
||||||
|
|
||||||
|
### PmPriceHistoryItem(list 单项)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| ID | number | 主键 |
|
||||||
|
| market | string | 市场标识 |
|
||||||
|
| price | number | 价格(0–1 小数或 0–100,由 priceHistoryToChartData 统一为 0–100) |
|
||||||
|
| time | number | Unix 秒时间戳 |
|
||||||
|
| interval | string | 数据间隔 |
|
||||||
|
| fidelity | number | 数据精度 |
|
||||||
|
| createdAt | string | 创建时间 |
|
||||||
|
| updatedAt | string | 更新时间 |
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
getPmPriceHistoryPublic,
|
||||||
|
priceHistoryToChartData,
|
||||||
|
type PmPriceHistoryItem,
|
||||||
|
} from '@/api/priceHistory'
|
||||||
|
|
||||||
|
const res = await getPmPriceHistoryPublic({
|
||||||
|
market: marketId,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 500,
|
||||||
|
})
|
||||||
|
const chartData = priceHistoryToChartData(res.data?.list ?? [])
|
||||||
|
```
|
||||||
|
|
||||||
|
## 扩展方式
|
||||||
|
|
||||||
|
- 按时间范围(1H/6H/1D 等)传 `interval` 或 `createdAtRange` 需与后端约定取值
|
||||||
|
- 若后端返回的 `price` 固定为 0–100,`priceHistoryToChartData` 已兼容(≤1 时乘 100)
|
||||||
@ -9,7 +9,7 @@
|
|||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- Trade Up / Trade Down Tab
|
- Trade Up / Trade Down Tab
|
||||||
- Asks、Bids 列表,带 `HorizontalProgressBar` 深度条
|
- Asks、Bids 列表,带 `HorizontalProgressBar` 深度条(买卖两边共用同一最大值 `maxOrderBookTotal`,取两边累计总量中的最大值,便于对比深度)
|
||||||
- Last price、Spread 展示
|
- Last price、Spread 展示
|
||||||
- Live / 连接中 状态展示(均通过 i18n 国际化)
|
- Live / 连接中 状态展示(均通过 i18n 国际化)
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
- `isLoggedIn`、`avatarUrl`:派生状态
|
- `isLoggedIn`、`avatarUrl`:派生状态
|
||||||
- `balance`:USDC 余额显示(如 "0.00"),支持 **UserSocket** 实时推送更新
|
- `balance`:USDC 余额显示(如 "0.00"),支持 **UserSocket** 实时推送更新
|
||||||
- `setUser`:设置登录数据并持久化,登录成功后自动连接 UserSocket
|
- `setUser`:设置登录数据并持久化,登录成功后自动连接 UserSocket
|
||||||
- `logout`:清空并断开 UserSocket
|
- `logout`:退出登录;先调用 **POST /jwt/jsonInBlacklist** 将当前 JWT 加入黑名单,再清空本地 token/user 并断开 UserSocket;接口失败时仍执行本地登出
|
||||||
- `getAuthHeaders`:返回 `{ 'x-token', 'x-user-id' }`,未登录时返回 `undefined`
|
- `getAuthHeaders`:返回 `{ 'x-token', 'x-user-id' }`,未登录时返回 `undefined`
|
||||||
- `fetchUserInfo`、`fetchUsdcBalance`:拉取并更新用户信息与余额;`fetchUserInfo` 兼容多种 API 字段名(id/ID、userName/username 等)
|
- `fetchUserInfo`、`fetchUsdcBalance`:拉取并更新用户信息与余额;`fetchUserInfo` 兼容多种 API 字段名(id/ID、userName/username 等)
|
||||||
- 内部 `parseUserId`:从 API 返回的 user 对象解析 id/ID,兼容 number 与 string,供 `setUser` 与 `fetchUserInfo` 复用
|
- 内部 `parseUserId`:从 API 返回的 user 对象解析 id/ID,兼容 number 与 string,供 `setUser` 与 `fetchUserInfo` 复用
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- 分时图:ECharts 渲染,支持 Past、时间粒度切换;**加密货币事件**可切换 YES/NO 分时图与加密货币价格走势图(CoinGecko 实时数据)
|
- 分时图:ECharts 渲染,支持 Past、时间粒度切换(1H/6H/1D/1W/1M/ALL);**Yes/No 模式**数据来自 **GET /pmPriceHistory/getPmPriceHistoryPublic**(market 传 clobTokenIds[0]),接口返回 `time`(Unix 秒)、`price`(0–1)转成 `[timestamp_ms, value_0_100][]` 后缓存在 `rawChartData`,**分时**为前端按当前选中范围过滤:1H=最近 1 小时、6H=6 小时、1D=1 天、1W=7 天、1M=30 天、ALL=全部,切换时间范围不重复请求;**加密货币事件**可切换 YES/NO 分时图与加密货币价格走势图(CoinGecko 实时数据)
|
||||||
- 订单簿:`OrderBook` 组件,通过 **ClobSdk** 对接 CLOB WebSocket 实时数据(全量快照、增量更新、成交推送);份额接口按 6 位小数传(1_000_000 = 1 share),`priceSizeToRows` 与 `mergeDelta` 会将 raw 值除以 `ORDER_BOOK_SIZE_SCALE` 转为展示值
|
- 订单簿:`OrderBook` 组件,通过 **ClobSdk** 对接 CLOB WebSocket 实时数据(全量快照、增量更新、成交推送);份额接口按 6 位小数传(1_000_000 = 1 share),`priceSizeToRows` 与 `mergeDelta` 会将 raw 值除以 `ORDER_BOOK_SIZE_SCALE` 转为展示值
|
||||||
- 交易:`TradeComponent`,传入 `market`、`initialOption`、`positions`(持仓数据)
|
- 交易:`TradeComponent`,传入 `market`、`initialOption`、`positions`(持仓数据)
|
||||||
- 持仓列表:通过 `getPositionList` 获取当前市场持仓,传递给 `TradeComponent` 用于计算可合并份额
|
- 持仓列表:通过 `getPositionList` 获取当前市场持仓,传递给 `TradeComponent` 用于计算可合并份额
|
||||||
@ -36,7 +36,7 @@
|
|||||||
## 扩展方式
|
## 扩展方式
|
||||||
|
|
||||||
1. **订单簿**:已通过 `sdk/clobSocket.ts` 的 ClobSdk 对接 CLOB WebSocket,使用 **Yes/No token ID** 订阅 `price_size_all`、`price_size_delta`、`trade` 消息
|
1. **订单簿**:已通过 `sdk/clobSocket.ts` 的 ClobSdk 对接 CLOB WebSocket,使用 **Yes/No token ID** 订阅 `price_size_all`、`price_size_delta`、`trade` 消息
|
||||||
2. **分时图**:可接入 WebSocket 推送的图表数据;加密货币事件已支持 YES/NO 分时与加密货币价格图切换(`src/api/cryptoChart.ts`)
|
2. **分时图**:Yes/NO 折线图仅使用真实接口 `getPmPriceHistoryPublic`,无模拟数据与定时器;事件详情加载完成后自动请求并展示,无 marketId 或接口无数据时展示空图;加密货币事件已支持 YES/NO 分时与加密货币价格图切换(`src/api/cryptoChart.ts`)
|
||||||
3. **Comments**:对接评论接口,替换 placeholder
|
3. **Comments**:对接评论接口,替换 placeholder
|
||||||
4. **Top Holders**:对接持仓接口
|
4. **Top Holders**:对接持仓接口
|
||||||
5. **Activity**:已对接 CLOB `trade` 消息,实时追加成交记录
|
5. **Activity**:已对接 CLOB `trade` 消息,实时追加成交记录
|
||||||
|
|||||||
@ -10,8 +10,9 @@
|
|||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- Portfolio 卡片:余额、Deposit/Withdraw 按钮
|
- Portfolio 卡片:余额、Deposit/Withdraw 按钮
|
||||||
- Profit/Loss 卡片:时间范围切换、ECharts 图表
|
- Profit/Loss 卡片:时间范围切换(1D/1W/1M/ALL)、ECharts 资产变化折线图;数据格式为 `[timestamp_ms, pnl][]`,**暂无接口时全部显示为 0**(真实时间轴 + 数值 0),有接口后在此处对接
|
||||||
- Tab:Positions、Open orders、History、Withdrawals(提现记录)
|
- Tab:Positions、Open orders、**History**(历史记录来自 **GET /hr/getHistoryRecordListClient**,`src/api/historyRecord.ts`,需鉴权、按当前用户分页)、Withdrawals(提现记录)
|
||||||
|
- **可结算/领取**:未结算项(unsettledItems)由持仓中有 `marketID`、`tokenID` 且 **所属 market.closed=true** 的项组成,用于「领取结算」按钮;不再使用 needClaim 判断
|
||||||
- Withdrawals:分页列表,状态筛选(全部/审核中/提现成功/审核不通过/提现失败),对接 GET /pmset/getPmSettlementRequestsListClient
|
- Withdrawals:分页列表,状态筛选(全部/审核中/提现成功/审核不通过/提现失败),对接 GET /pmset/getPmSettlementRequestsListClient
|
||||||
- DepositDialog、WithdrawDialog 组件
|
- DepositDialog、WithdrawDialog 组件
|
||||||
- **401 权限错误**:取消订单等接口失败时,通过 `useAuthError().formatAuthError` 统一提示「请先登录」或「权限不足」
|
- **401 权限错误**:取消订单等接口失败时,通过 `useAuthError().formatAuthError` 统一提示「请先登录」或「权限不足」
|
||||||
|
|||||||
@ -110,6 +110,7 @@ export interface PmEventListResponse {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /PmEvent/getPmEventPublic 请求参数(与 doc.json 对齐)
|
* GET /PmEvent/getPmEventPublic 请求参数(与 doc.json 对齐)
|
||||||
|
* startDateMin、endDateMax 不传;startDateMax、endDateMin 传当前时间戳(秒)
|
||||||
*/
|
*/
|
||||||
export interface GetPmEventListParams {
|
export interface GetPmEventListParams {
|
||||||
page?: number
|
page?: number
|
||||||
@ -147,6 +148,7 @@ export async function getPmEventPublic(
|
|||||||
archived = false,
|
archived = false,
|
||||||
closed = false,
|
closed = false,
|
||||||
} = params
|
} = params
|
||||||
|
const nowTs = Math.floor(Date.now() / 1000)
|
||||||
const query = buildQuery({
|
const query = buildQuery({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
@ -156,6 +158,8 @@ export async function getPmEventPublic(
|
|||||||
active: active ? 'true' : 'false',
|
active: active ? 'true' : 'false',
|
||||||
archived: archived ? 'true' : 'false',
|
archived: archived ? 'true' : 'false',
|
||||||
closed: closed ? 'true' : 'false',
|
closed: closed ? 'true' : 'false',
|
||||||
|
startDateMax: nowTs,
|
||||||
|
endDateMin: nowTs,
|
||||||
})
|
})
|
||||||
return get<PmEventListResponse>('/PmEvent/getPmEventPublic', query)
|
return get<PmEventListResponse>('/PmEvent/getPmEventPublic', query)
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/api/jwt.ts
Normal file
18
src/api/jwt.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* JWT 相关接口
|
||||||
|
* POST /jwt/jsonInBlacklist:将当前 JWT 加入黑名单(退出登录时调用)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { post } from './request'
|
||||||
|
import type { ApiResponse } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JWT 加入黑名单(需鉴权:x-token)
|
||||||
|
* POST /jwt/jsonInBlacklist
|
||||||
|
* 退出登录时调用,使当前 token 失效
|
||||||
|
*/
|
||||||
|
export async function jsonInBlacklist(
|
||||||
|
config?: { headers?: Record<string, string> },
|
||||||
|
): Promise<ApiResponse> {
|
||||||
|
return post<ApiResponse>('/jwt/jsonInBlacklist', undefined, config)
|
||||||
|
}
|
||||||
@ -63,7 +63,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -94,7 +96,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -154,11 +158,7 @@
|
|||||||
|
|
||||||
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
<p v-if="orderError" class="order-error">{{ orderError }}</p>
|
||||||
<!-- Action Button: Buy 余额足够显示 Buy Yes/No,不足显示 {{ t('trade.deposit') }};Sell 只显示 Sell Yes/No -->
|
<!-- Action Button: Buy 余额足够显示 Buy Yes/No,不足显示 {{ t('trade.deposit') }};Sell 只显示 Sell Yes/No -->
|
||||||
<v-btn
|
<v-btn v-if="activeTab === 'buy' && showDepositForBuy" class="deposit-btn" @click="deposit">
|
||||||
v-if="activeTab === 'buy' && showDepositForBuy"
|
|
||||||
class="deposit-btn"
|
|
||||||
@click="deposit"
|
|
||||||
>
|
|
||||||
{{ t('trade.deposit') }}
|
{{ t('trade.deposit') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -201,7 +201,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -243,7 +245,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -352,7 +356,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span v-if="activeTab === 'sell'" class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span v-if="activeTab === 'sell'" class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -382,7 +388,9 @@
|
|||||||
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
<v-btn class="share-btn" @click="setSharesPercentage(50)">50%</v-btn>
|
||||||
<v-btn class="share-btn" @click="setMaxShares">{{ t('trade.max') }}</v-btn>
|
<v-btn class="share-btn" @click="setMaxShares">{{ t('trade.max') }}</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="sellSharesExceedsMax" class="shares-exceeds-max-hint">{{ t('trade.sharesExceedsMax', { max: maxAvailableShares }) }}</p>
|
<p v-if="sellSharesExceedsMax" class="shares-exceeds-max-hint">
|
||||||
|
{{ t('trade.sharesExceedsMax', { max: maxAvailableShares }) }}
|
||||||
|
</p>
|
||||||
<div v-if="activeTab === 'buy'" class="matching-info">
|
<div v-if="activeTab === 'buy'" class="matching-info">
|
||||||
<v-icon size="14">mdi-information</v-icon>
|
<v-icon size="14">mdi-information</v-icon>
|
||||||
<span>20.00 matching</span>
|
<span>20.00 matching</span>
|
||||||
@ -506,7 +514,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -537,7 +547,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -563,22 +575,23 @@
|
|||||||
<div class="total-section">
|
<div class="total-section">
|
||||||
<template v-if="activeTab === 'buy'">
|
<template v-if="activeTab === 'buy'">
|
||||||
<div v-if="!isMarketMode" class="total-row">
|
<div v-if="!isMarketMode" class="total-row">
|
||||||
<span class="label">{{ t('trade.total') }}</span><span class="total-value">${{ totalPrice }}</span>
|
<span class="label">{{ t('trade.total') }}</span
|
||||||
|
><span class="total-value">${{ totalPrice }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.toWin') }}</span
|
<span class="label">{{ t('trade.toWin') }}</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>
|
||||||
>
|
{{ toWinValue }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.youllReceive') }}</span
|
<span class="label">{{ t('trade.youllReceive') }}</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">
|
<div class="total-row avg-price-row">
|
||||||
@ -630,7 +643,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -669,7 +684,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -768,7 +785,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span v-if="activeTab === 'sell'" class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span v-if="activeTab === 'sell'" class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -820,13 +839,14 @@
|
|||||||
<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">
|
||||||
<span class="label">{{ t('trade.total') }}</span><span class="total-value">${{ totalPrice }}</span>
|
<span class="label">{{ t('trade.total') }}</span
|
||||||
|
><span class="total-value">${{ totalPrice }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.toWin') }}</span
|
<span class="label">{{ t('trade.toWin') }}</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> {{ toWinValue }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -936,7 +956,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -967,7 +989,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -996,20 +1020,20 @@
|
|||||||
<span class="label">Total</span
|
<span class="label">Total</span
|
||||||
><span class="total-value">${{ totalPrice }}</span>
|
><span class="total-value">${{ totalPrice }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.toWin') }}</span
|
<span class="label">{{ t('trade.toWin') }}</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>
|
||||||
>
|
{{ toWinValue }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.youllReceive') }}</span
|
<span class="label">{{ t('trade.youllReceive') }}</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">
|
<div class="total-row avg-price-row">
|
||||||
@ -1061,7 +1085,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.amount') }}</span>
|
<span class="label">{{ t('trade.amount') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -1100,7 +1126,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -1199,7 +1227,9 @@
|
|||||||
<div class="input-group shares-group">
|
<div class="input-group shares-group">
|
||||||
<div class="shares-header sell-shares-header">
|
<div class="shares-header sell-shares-header">
|
||||||
<span class="label">{{ t('trade.shares') }}</span>
|
<span class="label">{{ t('trade.shares') }}</span>
|
||||||
<span v-if="activeTab === 'sell'" class="max-shares-inline">{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span>
|
<span v-if="activeTab === 'sell'" class="max-shares-inline"
|
||||||
|
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="shares-input-wrapper">
|
<div class="shares-input-wrapper">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@ -1251,22 +1281,23 @@
|
|||||||
<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">
|
||||||
<span class="label">{{ t('trade.total') }}</span><span class="total-value">${{ totalPrice }}</span>
|
<span class="label">{{ t('trade.total') }}</span
|
||||||
|
><span class="total-value">${{ totalPrice }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.toWin') }}</span
|
<span class="label">{{ t('trade.toWin') }}</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>
|
||||||
>
|
{{ toWinValue }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span class="label">{{ t('trade.youllReceive') }}</span
|
<span class="label">{{ t('trade.youllReceive') }}</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>
|
||||||
@ -1324,7 +1355,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="merge-available">
|
<p class="merge-available">
|
||||||
{{ t('trade.mergeAvailableShares') }} {{ availableMergeShares }}
|
{{ t('trade.mergeAvailableShares') }} {{ availableMergeShares }}
|
||||||
<button type="button" class="merge-max-link" @click="setMergeMax">{{ t('trade.max') }}</button>
|
<button type="button" class="merge-max-link" @click="setMergeMax">
|
||||||
|
{{ t('trade.max') }}
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="!props.market?.marketId" class="merge-no-market">
|
<p v-if="!props.market?.marketId" class="merge-no-market">
|
||||||
{{ t('trade.mergeNoMarket', { yesLabel, noLabel }) }}
|
{{ t('trade.mergeNoMarket', { yesLabel, noLabel }) }}
|
||||||
@ -1498,7 +1531,13 @@ const props = withDefaults(
|
|||||||
/** 当前市场持仓列表,用于计算可合并份额 */
|
/** 当前市场持仓列表,用于计算可合并份额 */
|
||||||
positions?: TradePositionItem[]
|
positions?: TradePositionItem[]
|
||||||
}>(),
|
}>(),
|
||||||
{ initialOption: undefined, initialTab: undefined, embeddedInSheet: false, market: undefined, positions: () => [] },
|
{
|
||||||
|
initialOption: undefined,
|
||||||
|
initialTab: undefined,
|
||||||
|
embeddedInSheet: false,
|
||||||
|
market: undefined,
|
||||||
|
positions: () => [],
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// 移动端:底部栏与弹出层
|
// 移动端:底部栏与弹出层
|
||||||
@ -1575,7 +1614,7 @@ async function submitSplit() {
|
|||||||
splitError.value = ''
|
splitError.value = ''
|
||||||
try {
|
try {
|
||||||
const res = await pmMarketSplit(
|
const res = await pmMarketSplit(
|
||||||
{ marketID: marketId, usdcAmount: String(splitAmount.value) },
|
{ marketID: marketId, usdcAmount: String(splitAmount.value * 1000000) },
|
||||||
{ headers: userStore.getAuthHeaders() },
|
{ headers: userStore.getAuthHeaders() },
|
||||||
)
|
)
|
||||||
if (res.code === 0 || res.code === 200) {
|
if (res.code === 0 || res.code === 200) {
|
||||||
@ -1639,7 +1678,19 @@ const selectedOption = ref<'yes' | 'no'>(props.initialOption ?? 'no')
|
|||||||
const limitPrice = ref(0.82) // 内部存储 0–1,显示为美分与按钮一致
|
const limitPrice = ref(0.82) // 内部存储 0–1,显示为美分与按钮一致
|
||||||
const shares = ref(20) // 初始份额(正整数)
|
const shares = ref(20) // 初始份额(正整数)
|
||||||
const expirationTime = ref('5m') // 初始过期时间
|
const expirationTime = ref('5m') // 初始过期时间
|
||||||
const EXPIRATION_VALUES = ['5m', '15m', '30m', '1h', '2h', '4h', '8h', '12h', '1d', '2d', '3d'] as const
|
const EXPIRATION_VALUES = [
|
||||||
|
'5m',
|
||||||
|
'15m',
|
||||||
|
'30m',
|
||||||
|
'1h',
|
||||||
|
'2h',
|
||||||
|
'4h',
|
||||||
|
'8h',
|
||||||
|
'12h',
|
||||||
|
'1d',
|
||||||
|
'2d',
|
||||||
|
'3d',
|
||||||
|
] as const
|
||||||
const expirationOptions = computed(() =>
|
const expirationOptions = computed(() =>
|
||||||
EXPIRATION_VALUES.map((v) => ({ title: t(`trade.expiration.${v}`), value: v })),
|
EXPIRATION_VALUES.map((v) => ({ title: t(`trade.expiration.${v}`), value: v })),
|
||||||
)
|
)
|
||||||
@ -1944,7 +1995,10 @@ const adjustShares = (amount: number) => {
|
|||||||
|
|
||||||
/** 卖出时输入份额是否超过最大可卖 */
|
/** 卖出时输入份额是否超过最大可卖 */
|
||||||
const sellSharesExceedsMax = computed(
|
const sellSharesExceedsMax = computed(
|
||||||
() => activeTab.value === 'sell' && maxAvailableShares.value >= 0 && shares.value > maxAvailableShares.value,
|
() =>
|
||||||
|
activeTab.value === 'sell' &&
|
||||||
|
maxAvailableShares.value >= 0 &&
|
||||||
|
shares.value > maxAvailableShares.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 份额百分比调整方法(仅在Sell模式下使用)
|
// 份额百分比调整方法(仅在Sell模式下使用)
|
||||||
@ -2058,12 +2112,7 @@ async function submitOrder() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const uid = userStore?.user?.ID ?? 0
|
const uid = userStore?.user?.ID ?? 0
|
||||||
const userIdNum =
|
const userIdNum = typeof uid === 'number' ? uid : uid != null ? parseInt(String(uid), 10) : 0
|
||||||
typeof uid === 'number'
|
|
||||||
? uid
|
|
||||||
: uid != null
|
|
||||||
? parseInt(String(uid), 10)
|
|
||||||
: 0
|
|
||||||
if (!Number.isFinite(userIdNum) || userIdNum <= 0) {
|
if (!Number.isFinite(userIdNum) || userIdNum <= 0) {
|
||||||
orderError.value = t('trade.userError')
|
orderError.value = t('trade.userError')
|
||||||
isNoAvailableSharesError.value = false
|
isNoAvailableSharesError.value = false
|
||||||
@ -2096,9 +2145,7 @@ async function submitOrder() {
|
|||||||
? parseExpirationTimestamp(expirationTime.value)
|
? parseExpirationTimestamp(expirationTime.value)
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
const rawSize = isMarket && activeTab.value === 'buy'
|
const rawSize = isMarket && activeTab.value === 'buy' ? amount.value : clampShares(shares.value)
|
||||||
? amount.value
|
|
||||||
: clampShares(shares.value)
|
|
||||||
const sizeValue = Math.round(rawSize * 1_000_000)
|
const sizeValue = Math.round(rawSize * 1_000_000)
|
||||||
|
|
||||||
orderLoading.value = true
|
orderLoading.value = true
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ref, computed } from 'vue'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { getUsdcBalance, formatUsdcBalance, getUserInfo } from '@/api/user'
|
import { getUsdcBalance, formatUsdcBalance, getUserInfo } from '@/api/user'
|
||||||
import { getUserWsUrl, BASE_URL } from '@/api/request'
|
import { getUserWsUrl, BASE_URL } from '@/api/request'
|
||||||
|
import { jsonInBlacklist } from '@/api/jwt'
|
||||||
import { UserSdk, type BalanceData, type PositionData } from '../../sdk/userSocket'
|
import { UserSdk, type BalanceData, type PositionData } from '../../sdk/userSocket'
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
@ -49,11 +50,7 @@ function parseUserId(raw: { id?: number | string; ID?: number } | null | undefin
|
|||||||
} {
|
} {
|
||||||
const rawId = raw?.ID ?? raw?.id
|
const rawId = raw?.ID ?? raw?.id
|
||||||
const numId =
|
const numId =
|
||||||
typeof rawId === 'number'
|
typeof rawId === 'number' ? rawId : rawId != null ? parseInt(String(rawId), 10) : undefined
|
||||||
? rawId
|
|
||||||
: rawId != null
|
|
||||||
? parseInt(String(rawId), 10)
|
|
||||||
: undefined
|
|
||||||
return { id: rawId ?? numId, numId: Number.isFinite(numId) ? numId : undefined }
|
return { id: rawId ?? numId, numId: Number.isFinite(numId) ? numId : undefined }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +117,9 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 订阅 position_update 推送,返回取消订阅函数 */
|
/** 订阅 position_update 推送,返回取消订阅函数 */
|
||||||
function onPositionUpdate(cb: (data: PositionData & Record<string, unknown>) => void): () => void {
|
function onPositionUpdate(
|
||||||
|
cb: (data: PositionData & Record<string, unknown>) => void,
|
||||||
|
): () => void {
|
||||||
positionUpdateCallbacks.push(cb)
|
positionUpdateCallbacks.push(cb)
|
||||||
return () => {
|
return () => {
|
||||||
const i = positionUpdateCallbacks.indexOf(cb)
|
const i = positionUpdateCallbacks.indexOf(cb)
|
||||||
@ -147,7 +146,15 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
async function logout() {
|
||||||
|
const headers = getAuthHeaders()
|
||||||
|
if (headers) {
|
||||||
|
try {
|
||||||
|
await jsonInBlacklist({ headers })
|
||||||
|
} catch {
|
||||||
|
// 忽略失败,仍执行本地登出
|
||||||
|
}
|
||||||
|
}
|
||||||
disconnectUserSocket()
|
disconnectUserSocket()
|
||||||
token.value = ''
|
token.value = ''
|
||||||
user.value = null
|
user.value = null
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user