新增:交易历史记录
This commit is contained in:
parent
642f7990dc
commit
3688ce656d
@ -87,13 +87,31 @@
|
||||
class="search-field"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
/>
|
||||
<template v-if="activeTab === 'history'">
|
||||
<v-btn variant="outlined" size="small" class="filter-btn">
|
||||
<v-icon size="18">mdi-filter</v-icon>
|
||||
Current value
|
||||
All
|
||||
</v-btn>
|
||||
<v-btn variant="outlined" size="small" class="filter-btn">
|
||||
<v-icon size="18">mdi-sort</v-icon>
|
||||
Newest
|
||||
</v-btn>
|
||||
<v-btn variant="outlined" size="small" class="filter-btn" icon>
|
||||
<v-icon size="18">mdi-calendar</v-icon>
|
||||
</v-btn>
|
||||
<v-btn variant="outlined" size="small" class="filter-btn">
|
||||
<v-icon size="18">mdi-download</v-icon>
|
||||
Export
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-btn v-else variant="outlined" size="small" class="filter-btn">
|
||||
<v-icon size="18">mdi-filter</v-icon>
|
||||
Market
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-card class="table-card" elevation="0" rounded="lg">
|
||||
<v-table class="positions-table">
|
||||
<!-- 持仓 -->
|
||||
<v-table v-if="activeTab === 'positions'" class="wallet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">MARKET</th>
|
||||
@ -110,13 +128,11 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="positions.length === 0">
|
||||
<td colspan="5" class="empty-cell">
|
||||
No positions found.
|
||||
</td>
|
||||
<tr v-if="filteredPositions.length === 0">
|
||||
<td colspan="5" class="empty-cell">No positions found.</td>
|
||||
</tr>
|
||||
<tr v-for="pos in positions" :key="pos.id">
|
||||
<td>{{ pos.market }}</td>
|
||||
<tr v-for="pos in paginatedPositions" :key="pos.id">
|
||||
<td class="cell-market">{{ pos.market }}</td>
|
||||
<td>{{ pos.avgNow }}</td>
|
||||
<td>{{ pos.bet }}</td>
|
||||
<td>{{ pos.toWin }}</td>
|
||||
@ -124,6 +140,87 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
<!-- 未成交 -->
|
||||
<v-table v-else-if="activeTab === 'orders'" class="wallet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">MARKET</th>
|
||||
<th class="text-left">SIDE</th>
|
||||
<th class="text-left">OUTCOME</th>
|
||||
<th class="text-left">PRICE</th>
|
||||
<th class="text-left">FILLED</th>
|
||||
<th class="text-left">TOTAL</th>
|
||||
<th class="text-left">EXPIRATION</th>
|
||||
<th class="text-right">ACTION</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="filteredOpenOrders.length === 0">
|
||||
<td colspan="8" class="empty-cell">No open orders found.</td>
|
||||
</tr>
|
||||
<tr v-for="ord in paginatedOpenOrders" :key="ord.id">
|
||||
<td class="cell-market">{{ ord.market }}</td>
|
||||
<td>
|
||||
<span :class="ord.side === 'Yes' ? 'side-yes' : 'side-no'">{{ ord.side }}</span>
|
||||
</td>
|
||||
<td>{{ ord.outcome }}</td>
|
||||
<td>{{ ord.price }}</td>
|
||||
<td>{{ ord.filled }}</td>
|
||||
<td>{{ ord.total }}</td>
|
||||
<td>{{ ord.expiration }}</td>
|
||||
<td class="text-right">
|
||||
<v-btn variant="text" size="small" color="error" @click="cancelOrder(ord.id)">Cancel</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
<!-- 历史记录 -->
|
||||
<v-table v-else class="wallet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">ACTIVITY</th>
|
||||
<th class="text-left">MARKET</th>
|
||||
<th class="text-left">VALUE</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="filteredHistory.length === 0">
|
||||
<td colspan="3" class="empty-cell">You haven't traded any polymarkets yet</td>
|
||||
</tr>
|
||||
<tr v-for="h in paginatedHistory" :key="h.id">
|
||||
<td>
|
||||
<span :class="h.side === 'Yes' ? 'side-yes' : 'side-no'">{{ h.activity }}</span>
|
||||
</td>
|
||||
<td class="cell-market">{{ h.market }}</td>
|
||||
<td>{{ h.value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
<!-- 分页 -->
|
||||
<div v-if="currentListTotal > 0" class="pagination-bar">
|
||||
<span class="pagination-info">
|
||||
{{ currentPageStart }}–{{ currentPageEnd }} of {{ currentListTotal }}
|
||||
</span>
|
||||
<div class="pagination-controls">
|
||||
<v-select
|
||||
v-model="itemsPerPage"
|
||||
:items="pageSizeOptions"
|
||||
density="compact"
|
||||
hide-details
|
||||
variant="outlined"
|
||||
class="page-size-select"
|
||||
@update:model-value="page = 1"
|
||||
/>
|
||||
<v-pagination
|
||||
v-model="page"
|
||||
:length="currentTotalPages"
|
||||
:total-visible="5"
|
||||
density="comfortable"
|
||||
class="pagination"
|
||||
@update:model-value="onPageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
|
||||
@ -152,11 +249,100 @@ const portfolioBalance = computed(() => userStore.balance)
|
||||
const profitLoss = ref('0.00')
|
||||
const plRange = ref('ALL')
|
||||
const plTimeRanges = ['1D', '1W', '1M', 'ALL']
|
||||
const activeTab = ref('positions')
|
||||
const activeTab = ref<'positions' | 'orders' | 'history'>('positions')
|
||||
const search = ref('')
|
||||
const depositDialogOpen = ref(false)
|
||||
const withdrawDialogOpen = ref(false)
|
||||
const positions = ref<{ id: string; market: string; avgNow: string; bet: string; toWin: string; value: string }[]>([])
|
||||
|
||||
interface Position {
|
||||
id: string
|
||||
market: string
|
||||
avgNow: string
|
||||
bet: string
|
||||
toWin: string
|
||||
value: string
|
||||
}
|
||||
interface OpenOrder {
|
||||
id: string
|
||||
market: string
|
||||
side: 'Yes' | 'No'
|
||||
outcome: string
|
||||
price: string
|
||||
filled: string
|
||||
total: string
|
||||
expiration: string
|
||||
}
|
||||
interface HistoryItem {
|
||||
id: string
|
||||
market: string
|
||||
side: 'Yes' | 'No'
|
||||
activity: string
|
||||
value: string
|
||||
}
|
||||
|
||||
const positions = ref<Position[]>([
|
||||
{ id: 'p1', market: 'Will Bitcoin hit $100k by end of 2025?', avgNow: '72¢ → 75¢', bet: '$50', toWin: '$18', value: '$52' },
|
||||
{ id: 'p2', market: 'Will ETH merge complete by Q3?', avgNow: '45¢ → 48¢', bet: '$30', toWin: '$36', value: '$32' },
|
||||
])
|
||||
const openOrders = ref<OpenOrder[]>([
|
||||
{ id: 'o1', market: 'Will Bitcoin hit $100k by end of 2025?', side: 'Yes', outcome: 'Yes', price: '70¢', filled: '0', total: '$70', expiration: 'Dec 31, 2025' },
|
||||
{ id: 'o2', market: 'Will ETH merge complete by Q3?', side: 'No', outcome: 'No', price: '52¢', filled: '25', total: '$26', expiration: 'Sep 30, 2025' },
|
||||
])
|
||||
const history = ref<HistoryItem[]>([
|
||||
{ id: 'h1', market: 'Will Bitcoin hit $100k by end of 2025?', side: 'Yes', activity: 'Buy Yes', value: '$68' },
|
||||
{ id: 'h2', market: 'Will ETH merge complete by Q3?', side: 'No', activity: 'Sell No', value: '$35.20' },
|
||||
])
|
||||
|
||||
function matchSearch(text: string): boolean {
|
||||
const q = search.value.trim().toLowerCase()
|
||||
return !q || text.toLowerCase().includes(q)
|
||||
}
|
||||
const filteredPositions = computed(() => positions.value.filter((p) => matchSearch(p.market)))
|
||||
const filteredOpenOrders = computed(() => openOrders.value.filter((o) => matchSearch(o.market)))
|
||||
const filteredHistory = computed(() => history.value.filter((h) => matchSearch(h.market)))
|
||||
|
||||
const page = ref(1)
|
||||
const itemsPerPage = ref(10)
|
||||
const pageSizeOptions = [5, 10, 25, 50]
|
||||
|
||||
function paginate<T>(list: T[]) {
|
||||
const start = (page.value - 1) * itemsPerPage.value
|
||||
return list.slice(start, start + itemsPerPage.value)
|
||||
}
|
||||
const paginatedPositions = computed(() => paginate(filteredPositions.value))
|
||||
const paginatedOpenOrders = computed(() => paginate(filteredOpenOrders.value))
|
||||
const paginatedHistory = computed(() => paginate(filteredHistory.value))
|
||||
|
||||
const totalPagesPositions = computed(() => Math.max(1, Math.ceil(filteredPositions.value.length / itemsPerPage.value)))
|
||||
const totalPagesOrders = computed(() => Math.max(1, Math.ceil(filteredOpenOrders.value.length / itemsPerPage.value)))
|
||||
const totalPagesHistory = computed(() => Math.max(1, Math.ceil(filteredHistory.value.length / itemsPerPage.value)))
|
||||
|
||||
const currentListTotal = computed(() => {
|
||||
if (activeTab.value === 'positions') return filteredPositions.value.length
|
||||
if (activeTab.value === 'orders') return filteredOpenOrders.value.length
|
||||
return filteredHistory.value.length
|
||||
})
|
||||
const currentTotalPages = computed(() => {
|
||||
if (activeTab.value === 'positions') return totalPagesPositions.value
|
||||
if (activeTab.value === 'orders') return totalPagesOrders.value
|
||||
return totalPagesHistory.value
|
||||
})
|
||||
const currentPageStart = computed(() => currentListTotal.value === 0 ? 0 : (page.value - 1) * itemsPerPage.value + 1)
|
||||
const currentPageEnd = computed(() => Math.min(page.value * itemsPerPage.value, currentListTotal.value))
|
||||
|
||||
watch(activeTab, () => { page.value = 1 })
|
||||
watch([currentListTotal, itemsPerPage], () => {
|
||||
const maxPage = currentTotalPages.value
|
||||
if (page.value > maxPage) page.value = Math.max(1, maxPage)
|
||||
})
|
||||
|
||||
function onPageChange() {
|
||||
// 可选:翻页后滚动到表格顶部
|
||||
}
|
||||
|
||||
function cancelOrder(id: string) {
|
||||
openOrders.value = openOrders.value.filter((o) => o.id !== id)
|
||||
}
|
||||
|
||||
const plChartRef = ref<HTMLElement | null>(null)
|
||||
let plChartInstance: ECharts | null = null
|
||||
@ -418,10 +604,12 @@ function onWithdrawSuccess() {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.positions-table {
|
||||
.positions-table,
|
||||
.wallet-table {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wallet-table th,
|
||||
.positions-table th {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
@ -437,15 +625,74 @@ function onWithdrawSuccess() {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.positions-table td {
|
||||
.positions-table td,
|
||||
.wallet-table td {
|
||||
padding: 12px 16px;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.cell-market {
|
||||
max-width: 280px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.side-yes {
|
||||
color: #059669;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.side-no {
|
||||
color: #dc2626;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
text-align: center;
|
||||
color: #6b7280;
|
||||
padding: 48px 16px !important;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pagination-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
min-width: 88px;
|
||||
width: 88px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.page-size-select :deep(.v-field) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.page-size-select :deep(.v-field__input) {
|
||||
min-width: 2ch;
|
||||
}
|
||||
|
||||
.pagination :deep(.v-pagination__item),
|
||||
.pagination :deep(.v-pagination__prev),
|
||||
.pagination :deep(.v-pagination__next) {
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user