新增:钱包 History 默认展示并显示问题到期,支持筛选与日期范围。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d3f237168c
commit
c8cb4500a3
@ -52,6 +52,8 @@ export interface HistoryRecordItem {
|
||||
eventId?: number
|
||||
marketID?: string | number
|
||||
marketId?: string | number
|
||||
/** 问题/事件到期时间(列表接口关联填充) */
|
||||
endDate?: string
|
||||
}
|
||||
|
||||
/** GET /hr/getHistoryRecordPublic 请求参数 */
|
||||
@ -63,6 +65,8 @@ export interface GetHistoryRecordPublicParams {
|
||||
title?: string
|
||||
name?: string
|
||||
bio?: string
|
||||
/** 记录类型:TRADE / SPLIT / MERGE 等 */
|
||||
type?: string
|
||||
createdAtRange?: string[]
|
||||
}
|
||||
|
||||
@ -76,6 +80,8 @@ export interface GetHistoryRecordListClientParams {
|
||||
title?: string
|
||||
name?: string
|
||||
bio?: string
|
||||
/** 记录类型:TRADE / SPLIT / MERGE 等 */
|
||||
type?: string
|
||||
createdAtRange?: string[]
|
||||
isFunding?: boolean
|
||||
}
|
||||
@ -111,7 +117,8 @@ export async function getHistoryRecordListClient(
|
||||
title: params.title,
|
||||
name: params.name,
|
||||
bio: params.bio,
|
||||
createdAtRange: params.createdAtRange,
|
||||
type: params.type,
|
||||
'createdAtRange[]': params.createdAtRange,
|
||||
isFunding: params.isFunding === undefined ? undefined : String(params.isFunding),
|
||||
})
|
||||
return get<HistoryRecordListClientResponse>('/hr/getHistoryRecordListClient', query, config)
|
||||
@ -132,7 +139,8 @@ export async function getHistoryRecordPublic(
|
||||
title: params.title,
|
||||
name: params.name,
|
||||
bio: params.bio,
|
||||
createdAtRange: params.createdAtRange,
|
||||
type: params.type,
|
||||
'createdAtRange[]': params.createdAtRange,
|
||||
})
|
||||
return get<HistoryRecordPublicResponse>('/hr/getHistoryRecordPublic', query)
|
||||
}
|
||||
@ -164,6 +172,8 @@ export interface HistoryDisplayItem {
|
||||
tradeEventSlug?: string
|
||||
/** 跳转详情 query.marketId */
|
||||
detailMarketId?: string
|
||||
/** 问题到期时间展示文案,如 "Jul 26, 2026" */
|
||||
expiresAt?: string
|
||||
}
|
||||
|
||||
function formatTimeAgo(dateStr: string | undefined, timestamp?: number): string {
|
||||
@ -179,6 +189,18 @@ function formatTimeAgo(dateStr: string | undefined, timestamp?: number): string
|
||||
return new Date(ms).toLocaleDateString()
|
||||
}
|
||||
|
||||
function formatExpiresAt(endDate: string | undefined): string {
|
||||
if (!endDate) return ''
|
||||
try {
|
||||
const d = new Date(endDate)
|
||||
return !Number.isNaN(d.getTime())
|
||||
? d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
||||
: endDate
|
||||
} catch {
|
||||
return endDate
|
||||
}
|
||||
}
|
||||
|
||||
/** 将相对路径转为完整 URL */
|
||||
function toFullIconUrl(icon: string | undefined): string | undefined {
|
||||
if (!icon?.trim()) return undefined
|
||||
@ -226,6 +248,7 @@ export function mapHistoryRecordToDisplayItem(record: HistoryRecordItem): Histor
|
||||
const rawMk = record.marketID ?? record.marketId
|
||||
const detailMarketId =
|
||||
rawMk != null && String(rawMk).trim() !== '' ? String(rawMk).trim() : undefined
|
||||
const expiresAt = formatExpiresAt(record.endDate)
|
||||
return {
|
||||
id,
|
||||
market,
|
||||
@ -244,6 +267,7 @@ export function mapHistoryRecordToDisplayItem(record: HistoryRecordItem): Histor
|
||||
tradeEventId: tradeEventId || undefined,
|
||||
tradeEventSlug: tradeEventSlug || undefined,
|
||||
detailMarketId,
|
||||
expiresAt: expiresAt || undefined,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
610
src/components/WalletDateRangePicker.vue
Normal file
610
src/components/WalletDateRangePicker.vue
Normal file
@ -0,0 +1,610 @@
|
||||
<template>
|
||||
<div class="wallet-date-range">
|
||||
<v-btn
|
||||
class="wallet-filter-chip"
|
||||
:class="{ 'wallet-filter-chip--active': hasRange }"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
@click="openDialog"
|
||||
>
|
||||
<v-icon start size="16">mdi-calendar-month-outline</v-icon>
|
||||
<span class="wallet-filter-chip-label">{{ triggerLabel }}</span>
|
||||
<v-icon end size="16">mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-dialog v-model="dialogOpen" :max-width="mobile ? 360 : 560" scrim opacity="0.35">
|
||||
<v-card class="date-range-popover" elevation="8" rounded="lg">
|
||||
<div class="date-range-body" :class="{ 'date-range-body--mobile': mobile }">
|
||||
<div class="date-range-presets">
|
||||
<button
|
||||
v-for="preset in presets"
|
||||
:key="preset.key"
|
||||
type="button"
|
||||
class="date-range-preset"
|
||||
:class="{ 'date-range-preset--active': activePreset === preset.key }"
|
||||
@click="onPreset(preset.key)"
|
||||
>
|
||||
{{ preset.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="date-range-calendar">
|
||||
<div class="cal-header">
|
||||
<button type="button" class="cal-nav" aria-label="prev month" @click="shiftMonth(-1)">
|
||||
<v-icon size="18">mdi-chevron-left</v-icon>
|
||||
</button>
|
||||
<div class="cal-title">{{ monthTitle }}</div>
|
||||
<button type="button" class="cal-nav" aria-label="next month" @click="shiftMonth(1)">
|
||||
<v-icon size="18">mdi-chevron-right</v-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="cal-weekdays">
|
||||
<span v-for="w in weekdayLabels" :key="w" class="cal-weekday">{{ w }}</span>
|
||||
</div>
|
||||
|
||||
<div class="cal-grid">
|
||||
<button
|
||||
v-for="(cell, idx) in monthCells"
|
||||
:key="idx"
|
||||
type="button"
|
||||
class="cal-day"
|
||||
:class="dayClass(cell)"
|
||||
:disabled="!cell"
|
||||
@click="cell && onSelectDay(cell)"
|
||||
>
|
||||
<span v-if="cell" class="cal-day-num">{{ cell.day }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="cal-hint">{{ rangeHint }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="date-range-footer">
|
||||
<v-btn class="date-range-clear-btn" variant="outlined" size="small" @click="clearDraft">
|
||||
{{ t('wallet.clearDateRange') }}
|
||||
</v-btn>
|
||||
<div class="date-range-footer-actions">
|
||||
<v-btn variant="outlined" size="small" @click="onCancel">{{ t('common.cancel') }}</v-btn>
|
||||
<v-btn color="primary" variant="flat" size="small" @click="onConfirm">
|
||||
{{ t('wallet.setDate') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDisplay } from 'vuetify'
|
||||
|
||||
const props = defineProps({
|
||||
dateFrom: { type: String, default: '' },
|
||||
dateTo: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:dateFrom': [value: string]
|
||||
'update:dateTo': [value: string]
|
||||
}>()
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const { mobile } = useDisplay()
|
||||
|
||||
const dialogOpen = ref(false)
|
||||
const draftFrom = ref('')
|
||||
const draftTo = ref('')
|
||||
const activePreset = ref('')
|
||||
/** 当前展示月份的 1 号 */
|
||||
const viewMonth = ref<Date>(startOfMonth(new Date()))
|
||||
|
||||
type PresetKey =
|
||||
| 'today'
|
||||
| 'yesterday'
|
||||
| 'lastWeek'
|
||||
| 'lastMonth'
|
||||
| 'last3Months'
|
||||
| 'ytd'
|
||||
| 'lastYear'
|
||||
| 'all'
|
||||
|
||||
interface DayCell {
|
||||
ymd: string
|
||||
day: number
|
||||
inCurrentMonth: boolean
|
||||
}
|
||||
|
||||
const presets = computed(() => [
|
||||
{ key: 'today' as const, label: t('wallet.datePreset.today') },
|
||||
{ key: 'yesterday' as const, label: t('wallet.datePreset.yesterday') },
|
||||
{ key: 'lastWeek' as const, label: t('wallet.datePreset.lastWeek') },
|
||||
{ key: 'lastMonth' as const, label: t('wallet.datePreset.lastMonth') },
|
||||
{ key: 'last3Months' as const, label: t('wallet.datePreset.last3Months') },
|
||||
{ key: 'ytd' as const, label: t('wallet.datePreset.ytd') },
|
||||
{ key: 'lastYear' as const, label: t('wallet.datePreset.lastYear') },
|
||||
{ key: 'all' as const, label: t('wallet.datePreset.all') },
|
||||
])
|
||||
|
||||
const hasRange = computed(() => !!props.dateFrom || !!props.dateTo)
|
||||
|
||||
const triggerLabel = computed(() => {
|
||||
if (props.dateFrom && props.dateTo) {
|
||||
if (props.dateFrom === props.dateTo) return props.dateFrom
|
||||
return `${props.dateFrom} ~ ${props.dateTo}`
|
||||
}
|
||||
if (props.dateFrom) return `${props.dateFrom} ~`
|
||||
if (props.dateTo) return `~ ${props.dateTo}`
|
||||
return t('wallet.date')
|
||||
})
|
||||
|
||||
const rangeHint = computed(() => {
|
||||
if (draftFrom.value && draftTo.value) {
|
||||
return draftFrom.value === draftTo.value
|
||||
? draftFrom.value
|
||||
: `${draftFrom.value} ~ ${draftTo.value}`
|
||||
}
|
||||
if (draftFrom.value) return `${draftFrom.value} → …`
|
||||
return t('wallet.dateRangeHint')
|
||||
})
|
||||
|
||||
const weekdayLabels = computed(() => {
|
||||
const base = new Date(2024, 0, 7) // Sunday
|
||||
const fmt = new Intl.DateTimeFormat(locale.value || undefined, { weekday: 'narrow' })
|
||||
return Array.from({ length: 7 }, (_, i) => {
|
||||
const d = new Date(base)
|
||||
d.setDate(base.getDate() + i)
|
||||
return fmt.format(d)
|
||||
})
|
||||
})
|
||||
|
||||
const monthTitle = computed(() => {
|
||||
return new Intl.DateTimeFormat(locale.value || undefined, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
}).format(viewMonth.value)
|
||||
})
|
||||
|
||||
const monthCells = computed((): Array<DayCell | null> => {
|
||||
const first = viewMonth.value
|
||||
const year = first.getFullYear()
|
||||
const month = first.getMonth()
|
||||
const firstWeekday = first.getDay() // 0=Sun
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate()
|
||||
const cells: Array<DayCell | null> = []
|
||||
|
||||
for (let i = 0; i < firstWeekday; i++) cells.push(null)
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const ymd = formatLocalYmd(new Date(year, month, day))
|
||||
cells.push({ ymd, day, inCurrentMonth: true })
|
||||
}
|
||||
while (cells.length % 7 !== 0) cells.push(null)
|
||||
return cells
|
||||
})
|
||||
|
||||
function startOfMonth(date: Date): Date {
|
||||
return new Date(date.getFullYear(), date.getMonth(), 1)
|
||||
}
|
||||
|
||||
function formatLocalYmd(date: Date): string {
|
||||
const y = date.getFullYear()
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(date.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${d}`
|
||||
}
|
||||
|
||||
function parseYmd(ymd: string): Date | null {
|
||||
if (!ymd || !/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return null
|
||||
const parts = ymd.split('-').map(Number)
|
||||
const y = parts[0]
|
||||
const m = parts[1]
|
||||
const d = parts[2]
|
||||
if (y == null || m == null || d == null) return null
|
||||
const date = new Date(y, m - 1, d)
|
||||
if (Number.isNaN(date.getTime())) return null
|
||||
return date
|
||||
}
|
||||
|
||||
function syncDraftFromProps() {
|
||||
draftFrom.value = props.dateFrom || ''
|
||||
draftTo.value = props.dateTo || ''
|
||||
activePreset.value = ''
|
||||
const anchor = parseYmd(props.dateFrom || props.dateTo || '') ?? new Date()
|
||||
viewMonth.value = startOfMonth(anchor)
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
syncDraftFromProps()
|
||||
dialogOpen.value = true
|
||||
}
|
||||
|
||||
function shiftMonth(delta: number) {
|
||||
const cur = viewMonth.value
|
||||
viewMonth.value = new Date(cur.getFullYear(), cur.getMonth() + delta, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围选择:
|
||||
* - 无选中:点击设为起点
|
||||
* - 已有起点无终点:点击设为终点(自动校正先后)
|
||||
* - 已有完整范围:重新开始,点击作为新起点
|
||||
*/
|
||||
function onSelectDay(cell: DayCell) {
|
||||
activePreset.value = ''
|
||||
const ymd = cell.ymd
|
||||
|
||||
if (!draftFrom.value || (draftFrom.value && draftTo.value)) {
|
||||
draftFrom.value = ymd
|
||||
draftTo.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
// 已有起点,选终点
|
||||
if (ymd < draftFrom.value) {
|
||||
draftTo.value = draftFrom.value
|
||||
draftFrom.value = ymd
|
||||
} else {
|
||||
draftTo.value = ymd
|
||||
}
|
||||
}
|
||||
|
||||
function dayClass(cell: DayCell | null): Record<string, boolean> {
|
||||
if (!cell) return { 'cal-day--empty': true }
|
||||
const ymd = cell.ymd
|
||||
const from = draftFrom.value
|
||||
const to = draftTo.value
|
||||
const isStart = !!from && ymd === from
|
||||
const isEnd = !!to && ymd === to
|
||||
const inRange = !!from && !!to && ymd > from && ymd < to
|
||||
const isToday = ymd === formatLocalYmd(new Date())
|
||||
const isSingle = isStart && isEnd
|
||||
return {
|
||||
'cal-day--today': isToday,
|
||||
'cal-day--start': isStart,
|
||||
'cal-day--end': isEnd,
|
||||
'cal-day--in-range': inRange,
|
||||
'cal-day--single': isSingle,
|
||||
}
|
||||
}
|
||||
|
||||
function rangeFromPreset(key: PresetKey): { from: string; to: string } | null {
|
||||
const now = new Date()
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
|
||||
if (key === 'all') return null
|
||||
|
||||
if (key === 'today') {
|
||||
const s = formatLocalYmd(today)
|
||||
return { from: s, to: s }
|
||||
}
|
||||
if (key === 'yesterday') {
|
||||
const y = new Date(today)
|
||||
y.setDate(y.getDate() - 1)
|
||||
const s = formatLocalYmd(y)
|
||||
return { from: s, to: s }
|
||||
}
|
||||
if (key === 'lastWeek') {
|
||||
const end = new Date(today)
|
||||
end.setDate(end.getDate() - 1)
|
||||
const start = new Date(end)
|
||||
start.setDate(start.getDate() - 6)
|
||||
return { from: formatLocalYmd(start), to: formatLocalYmd(end) }
|
||||
}
|
||||
if (key === 'lastMonth') {
|
||||
const start = new Date(today.getFullYear(), today.getMonth() - 1, 1)
|
||||
const end = new Date(today.getFullYear(), today.getMonth(), 0)
|
||||
return { from: formatLocalYmd(start), to: formatLocalYmd(end) }
|
||||
}
|
||||
if (key === 'last3Months') {
|
||||
const end = today
|
||||
const start = new Date(today)
|
||||
start.setMonth(start.getMonth() - 3)
|
||||
return { from: formatLocalYmd(start), to: formatLocalYmd(end) }
|
||||
}
|
||||
if (key === 'ytd') {
|
||||
const start = new Date(today.getFullYear(), 0, 1)
|
||||
return { from: formatLocalYmd(start), to: formatLocalYmd(today) }
|
||||
}
|
||||
const start = new Date(today.getFullYear() - 1, 0, 1)
|
||||
const end = new Date(today.getFullYear() - 1, 11, 31)
|
||||
return { from: formatLocalYmd(start), to: formatLocalYmd(end) }
|
||||
}
|
||||
|
||||
function onPreset(key: PresetKey) {
|
||||
activePreset.value = key
|
||||
const range = rangeFromPreset(key)
|
||||
if (!range) {
|
||||
draftFrom.value = ''
|
||||
draftTo.value = ''
|
||||
return
|
||||
}
|
||||
draftFrom.value = range.from
|
||||
draftTo.value = range.to
|
||||
viewMonth.value = startOfMonth(parseYmd(range.from) ?? new Date())
|
||||
}
|
||||
|
||||
function clearDraft() {
|
||||
draftFrom.value = ''
|
||||
draftTo.value = ''
|
||||
activePreset.value = 'all'
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
dialogOpen.value = false
|
||||
}
|
||||
|
||||
function onConfirm() {
|
||||
// 只选了起点时,视为单日范围
|
||||
const from = draftFrom.value
|
||||
const to = draftTo.value || draftFrom.value
|
||||
if (!from) {
|
||||
emit('update:dateFrom', '')
|
||||
emit('update:dateTo', '')
|
||||
} else {
|
||||
emit('update:dateFrom', from)
|
||||
emit('update:dateTo', to)
|
||||
}
|
||||
dialogOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wallet-date-range {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.wallet-filter-chip {
|
||||
text-transform: none;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0;
|
||||
border-radius: 999px !important;
|
||||
border-color: #e5e7eb !important;
|
||||
color: #374151;
|
||||
background: #fff;
|
||||
min-height: 34px;
|
||||
padding-inline: 12px;
|
||||
}
|
||||
|
||||
.wallet-filter-chip--active {
|
||||
border-color: #2d5bff !important;
|
||||
color: #2d5bff;
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.wallet-filter-chip-label {
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.date-range-popover {
|
||||
border: 1px solid #e5e7eb;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.date-range-body {
|
||||
display: flex;
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.date-range-body--mobile {
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.date-range-presets {
|
||||
width: 132px;
|
||||
flex-shrink: 0;
|
||||
padding: 12px 8px;
|
||||
border-right: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.date-range-body--mobile .date-range-presets {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.date-range-preset {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
cursor: pointer;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.date-range-body--mobile .date-range-preset {
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.date-range-preset:hover {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.date-range-preset--active {
|
||||
background: #eef2ff;
|
||||
color: #2d5bff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.date-range-calendar {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 14px 16px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cal-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.cal-nav {
|
||||
appearance: none;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: #fff;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.cal-nav:hover {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.cal-weekdays,
|
||||
.cal-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.cal-weekday {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #9ca3af;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.cal-day {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
height: 36px;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.cal-day--empty {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cal-day-num {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cal-day:hover:not(.cal-day--empty) .cal-day-num {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.cal-day--today .cal-day-num {
|
||||
box-shadow: inset 0 0 0 1px #d1d5db;
|
||||
}
|
||||
|
||||
.cal-day--in-range {
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.cal-day--start,
|
||||
.cal-day--end,
|
||||
.cal-day--single {
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.cal-day--start {
|
||||
border-radius: 999px 0 0 999px;
|
||||
}
|
||||
|
||||
.cal-day--end {
|
||||
border-radius: 0 999px 999px 0;
|
||||
}
|
||||
|
||||
.cal-day--single {
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.cal-day--start .cal-day-num,
|
||||
.cal-day--end .cal-day-num,
|
||||
.cal-day--single .cal-day-num {
|
||||
background: #2d5bff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cal-day--start:hover .cal-day-num,
|
||||
.cal-day--end:hover .cal-day-num,
|
||||
.cal-day--single:hover .cal-day-num {
|
||||
background: #2d5bff;
|
||||
}
|
||||
|
||||
.cal-hint {
|
||||
min-height: 18px;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.date-range-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
background: #fff;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.date-range-footer-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.date-range-clear-btn,
|
||||
.date-range-footer-actions :deep(.v-btn) {
|
||||
text-transform: none;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
</style>
|
||||
@ -272,6 +272,25 @@
|
||||
"openOrders": "Open orders",
|
||||
"history": "History",
|
||||
"searchPlaceholder": "Search",
|
||||
"searchEventPlaceholder": "Search",
|
||||
"date": "Date",
|
||||
"dateFrom": "Start date",
|
||||
"dateTo": "End date",
|
||||
"clearFilters": "Clear",
|
||||
"clearDateRange": "Clear date range",
|
||||
"setDate": "Set date",
|
||||
"dateRangeHint": "Select start date, then end date",
|
||||
"typeFilter": "Type",
|
||||
"datePreset": {
|
||||
"today": "Today",
|
||||
"yesterday": "Yesterday",
|
||||
"lastWeek": "Last week",
|
||||
"lastMonth": "Last month",
|
||||
"last3Months": "Last 3 months",
|
||||
"ytd": "Year to date",
|
||||
"lastYear": "Last year",
|
||||
"all": "All"
|
||||
},
|
||||
"currentValue": "Current value",
|
||||
"closeLosses": "Close Losses",
|
||||
"all": "All",
|
||||
@ -283,6 +302,7 @@
|
||||
"noHistoryFound": "You haven't traded any polymarkets yet",
|
||||
"historySideBuy": "BUY",
|
||||
"historySideSell": "SELL",
|
||||
"historyTypeTrade": "Trade",
|
||||
"historyTypeSplit": "SPLIT",
|
||||
"historyTypeMerge": "MERGE",
|
||||
"historyTypeRedeem": "REDEEM",
|
||||
|
||||
@ -272,6 +272,25 @@
|
||||
"openOrders": "未約定",
|
||||
"history": "履歴",
|
||||
"searchPlaceholder": "検索",
|
||||
"searchEventPlaceholder": "検索",
|
||||
"date": "日付",
|
||||
"dateFrom": "開始日",
|
||||
"dateTo": "終了日",
|
||||
"clearFilters": "クリア",
|
||||
"clearDateRange": "日付範囲をクリア",
|
||||
"setDate": "日付を設定",
|
||||
"dateRangeHint": "開始日を選び、続けて終了日を選んでください",
|
||||
"typeFilter": "タイプ",
|
||||
"datePreset": {
|
||||
"today": "今日",
|
||||
"yesterday": "昨日",
|
||||
"lastWeek": "先週",
|
||||
"lastMonth": "先月",
|
||||
"last3Months": "直近3ヶ月",
|
||||
"ytd": "年初来",
|
||||
"lastYear": "昨年",
|
||||
"all": "すべて"
|
||||
},
|
||||
"currentValue": "現在価値",
|
||||
"closeLosses": "損失決済",
|
||||
"all": "全て",
|
||||
@ -283,6 +302,7 @@
|
||||
"noHistoryFound": "まだ取引履歴がありません",
|
||||
"historySideBuy": "買い",
|
||||
"historySideSell": "売り",
|
||||
"historyTypeTrade": "取引",
|
||||
"historyTypeSplit": "スプリット",
|
||||
"historyTypeMerge": "マージ",
|
||||
"historyTypeRedeem": "償還",
|
||||
|
||||
@ -272,6 +272,25 @@
|
||||
"openOrders": "미체결",
|
||||
"history": "내역",
|
||||
"searchPlaceholder": "검색",
|
||||
"searchEventPlaceholder": "검색",
|
||||
"date": "날짜",
|
||||
"dateFrom": "시작일",
|
||||
"dateTo": "종료일",
|
||||
"clearFilters": "초기화",
|
||||
"clearDateRange": "날짜 범위 지우기",
|
||||
"setDate": "날짜 설정",
|
||||
"dateRangeHint": "시작일을 선택한 뒤 종료일을 선택하세요",
|
||||
"typeFilter": "유형",
|
||||
"datePreset": {
|
||||
"today": "오늘",
|
||||
"yesterday": "어제",
|
||||
"lastWeek": "지난주",
|
||||
"lastMonth": "지난달",
|
||||
"last3Months": "최근 3개월",
|
||||
"ytd": "올해 누적",
|
||||
"lastYear": "작년",
|
||||
"all": "전체"
|
||||
},
|
||||
"currentValue": "현재 가치",
|
||||
"closeLosses": "손실 청산",
|
||||
"all": "전체",
|
||||
@ -283,6 +302,7 @@
|
||||
"noHistoryFound": "아직 거래 내역이 없습니다",
|
||||
"historySideBuy": "매수",
|
||||
"historySideSell": "매도",
|
||||
"historyTypeTrade": "거래",
|
||||
"historyTypeSplit": "스플릿",
|
||||
"historyTypeMerge": "머지",
|
||||
"historyTypeRedeem": "상환",
|
||||
|
||||
@ -272,6 +272,25 @@
|
||||
"openOrders": "未成交",
|
||||
"history": "历史",
|
||||
"searchPlaceholder": "搜索",
|
||||
"searchEventPlaceholder": "搜索",
|
||||
"date": "日期",
|
||||
"dateFrom": "开始日期",
|
||||
"dateTo": "结束日期",
|
||||
"clearFilters": "清空",
|
||||
"clearDateRange": "清空日期范围",
|
||||
"setDate": "设置日期",
|
||||
"dateRangeHint": "先点开始日期,再点结束日期",
|
||||
"typeFilter": "类型",
|
||||
"datePreset": {
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"lastWeek": "上周",
|
||||
"lastMonth": "上月",
|
||||
"last3Months": "近 3 个月",
|
||||
"ytd": "今年至今",
|
||||
"lastYear": "去年",
|
||||
"all": "全部"
|
||||
},
|
||||
"currentValue": "当前价值",
|
||||
"closeLosses": "平仓亏损",
|
||||
"all": "全部",
|
||||
@ -283,6 +302,7 @@
|
||||
"noHistoryFound": "您还未进行过任何交易",
|
||||
"historySideBuy": "买入",
|
||||
"historySideSell": "卖出",
|
||||
"historyTypeTrade": "交易",
|
||||
"historyTypeSplit": "拆分",
|
||||
"historyTypeMerge": "合并",
|
||||
"historyTypeRedeem": "赎回",
|
||||
|
||||
@ -272,6 +272,25 @@
|
||||
"openOrders": "未成交",
|
||||
"history": "歷史",
|
||||
"searchPlaceholder": "搜尋",
|
||||
"searchEventPlaceholder": "搜尋",
|
||||
"date": "日期",
|
||||
"dateFrom": "開始日期",
|
||||
"dateTo": "結束日期",
|
||||
"clearFilters": "清空",
|
||||
"clearDateRange": "清空日期範圍",
|
||||
"setDate": "設定日期",
|
||||
"dateRangeHint": "先點開始日期,再點結束日期",
|
||||
"typeFilter": "類型",
|
||||
"datePreset": {
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"lastWeek": "上週",
|
||||
"lastMonth": "上月",
|
||||
"last3Months": "近 3 個月",
|
||||
"ytd": "今年至今",
|
||||
"lastYear": "去年",
|
||||
"all": "全部"
|
||||
},
|
||||
"currentValue": "當前價值",
|
||||
"closeLosses": "平倉虧損",
|
||||
"all": "全部",
|
||||
@ -283,6 +302,7 @@
|
||||
"noHistoryFound": "您還未進行過任何交易",
|
||||
"historySideBuy": "買入",
|
||||
"historySideSell": "賣出",
|
||||
"historyTypeTrade": "交易",
|
||||
"historyTypeSplit": "拆分",
|
||||
"historyTypeMerge": "合併",
|
||||
"historyTypeRedeem": "贖回",
|
||||
|
||||
@ -49,13 +49,62 @@
|
||||
|
||||
<!-- 下方:Positions / Open orders / History -->
|
||||
<div class="wallet-section">
|
||||
<v-tabs v-model="activeTab" class="wallet-tabs" density="comfortable">
|
||||
<v-tab value="positions">{{ t('wallet.positions') }}</v-tab>
|
||||
<v-tab value="orders">{{ t('wallet.openOrders') }}</v-tab>
|
||||
<v-tab value="history">{{ t('wallet.history') }}</v-tab>
|
||||
<v-tab value="depositHistory">{{ t('deposit.depositHistory') }}</v-tab>
|
||||
<v-tab value="withdrawals">{{ t('wallet.withdrawals') }}</v-tab>
|
||||
</v-tabs>
|
||||
<div class="wallet-toolbar">
|
||||
<v-tabs v-model="activeTab" class="wallet-tabs" density="comfortable">
|
||||
<v-tab value="positions">{{ t('wallet.positions') }}</v-tab>
|
||||
<v-tab value="orders">{{ t('wallet.openOrders') }}</v-tab>
|
||||
<v-tab value="history">{{ t('wallet.history') }}</v-tab>
|
||||
<v-tab value="depositHistory">{{ t('deposit.depositHistory') }}</v-tab>
|
||||
<v-tab value="withdrawals">{{ t('wallet.withdrawals') }}</v-tab>
|
||||
</v-tabs>
|
||||
<div v-if="showListFilters" class="wallet-filters">
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
class="wallet-filter-search"
|
||||
density="compact"
|
||||
hide-details
|
||||
clearable
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
:placeholder="t('wallet.searchPlaceholder')"
|
||||
/>
|
||||
<v-menu v-if="activeTab === 'history'" location="bottom" offset="6">
|
||||
<template #activator="{ props: typeMenuProps }">
|
||||
<v-btn
|
||||
v-bind="typeMenuProps"
|
||||
class="wallet-filter-chip"
|
||||
:class="{ 'wallet-filter-chip--active': !!historyTypeFilter }"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
<v-icon start size="16">mdi-filter-variant</v-icon>
|
||||
{{ historyTypeFilterLabel }}
|
||||
<v-icon end size="16">mdi-chevron-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list class="wallet-filter-menu-list" density="compact" nav>
|
||||
<v-list-item
|
||||
v-for="opt in historyTypeOptions"
|
||||
:key="opt.value || 'all'"
|
||||
:active="historyTypeFilter === opt.value"
|
||||
@click="historyTypeFilter = opt.value"
|
||||
>
|
||||
<v-list-item-title>{{ opt.label }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<WalletDateRangePicker v-model:date-from="dateFrom" v-model:date-to="dateTo" />
|
||||
<v-btn
|
||||
v-if="hasActiveFilters"
|
||||
class="wallet-filter-clear"
|
||||
variant="text"
|
||||
size="small"
|
||||
@click="clearListFilters"
|
||||
>
|
||||
{{ t('wallet.clearFilters') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<v-card class="table-card wallet-design-card" elevation="0" rounded="lg">
|
||||
<template v-if="activeTab === 'positions'">
|
||||
<div class="positions-mobile-list">
|
||||
@ -226,8 +275,12 @@
|
||||
<span class="order-side-pill" :class="getHistoryTagClass(h)">
|
||||
{{ getHistoryTagLabel(h) }}
|
||||
</span>
|
||||
<span class="design-history-meta">{{ t('wallet.priceLabel') }}: {{ h.avgPrice || '—' }} ·
|
||||
{{ t('wallet.sharesLabel') }}: {{ h.shares || '—' }}</span>
|
||||
<span class="design-history-meta">
|
||||
<template v-if="h.expiresAt">{{ t('wallet.expirationLabel') }} {{ h.expiresAt }} ·
|
||||
</template>
|
||||
{{ t('wallet.priceLabel') }}: {{ h.avgPrice || '—' }} ·
|
||||
{{ t('wallet.sharesLabel') }}: {{ h.shares || '—' }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -422,6 +475,7 @@ import { useDisplay } from 'vuetify'
|
||||
const { t } = useI18n()
|
||||
import WalletDepositModule from '../components/WalletDepositModule.vue'
|
||||
import WalletDepositHistoryPanel from '../components/WalletDepositHistoryPanel.vue'
|
||||
import WalletDateRangePicker from '../components/WalletDateRangePicker.vue'
|
||||
import WithdrawDialog from '../components/WithdrawDialog.vue'
|
||||
import MarqueeTitle from '../components/MarqueeTitle.vue'
|
||||
import PnlCurveCard from '../components/PnlCurveCard.vue'
|
||||
@ -480,9 +534,12 @@ const localeStore = useLocaleStore()
|
||||
const portfolioBalance = computed(() => userStore.totalAssetValue)
|
||||
const positionsValueText = computed(() => userStore.positionsValue.toFixed(2))
|
||||
const activeTab = ref<'positions' | 'orders' | 'history' | 'depositHistory' | 'withdrawals'>(
|
||||
'positions',
|
||||
'history',
|
||||
)
|
||||
const search = ref('')
|
||||
const dateFrom = ref('')
|
||||
const dateTo = ref('')
|
||||
const historyTypeFilter = ref('')
|
||||
const withdrawStatusFilter = ref<string>('')
|
||||
const withdrawStatusOptions = computed(() => [
|
||||
{ label: t('wallet.withdrawStatusAll'), value: '' },
|
||||
@ -491,6 +548,77 @@ const withdrawStatusOptions = computed(() => [
|
||||
{ label: t('wallet.withdrawStatusRejected'), value: WITHDRAW_STATUS.REJECTED },
|
||||
{ label: t('wallet.withdrawStatusFailed'), value: WITHDRAW_STATUS.FAILED },
|
||||
])
|
||||
|
||||
const historyTypeOptions = computed(() => [
|
||||
{ label: t('wallet.all'), value: '' },
|
||||
{ label: t('wallet.historyTypeTrade'), value: HISTORY_RECORD_TYPE.TRADE },
|
||||
{ label: t('wallet.historyTypeSplit'), value: HISTORY_RECORD_TYPE.SPLIT },
|
||||
{ label: t('wallet.historyTypeMerge'), value: HISTORY_RECORD_TYPE.MERGE },
|
||||
{ label: t('wallet.historyTypeRedeem'), value: HISTORY_RECORD_TYPE.REDEEM },
|
||||
{ label: t('wallet.historyTypeReward'), value: HISTORY_RECORD_TYPE.REWARD },
|
||||
{ label: t('wallet.historyTypeConversion'), value: HISTORY_RECORD_TYPE.CONVERSION },
|
||||
{ label: t('wallet.historyTypeMakerRebate'), value: HISTORY_RECORD_TYPE.MAKER_REBATE },
|
||||
])
|
||||
|
||||
const historyTypeFilterLabel = computed(() => {
|
||||
const hit = historyTypeOptions.value.find((o) => o.value === historyTypeFilter.value)
|
||||
return hit?.label ?? t('wallet.all')
|
||||
})
|
||||
|
||||
const showListFilters = computed(
|
||||
() =>
|
||||
activeTab.value === 'positions' ||
|
||||
activeTab.value === 'orders' ||
|
||||
activeTab.value === 'history',
|
||||
)
|
||||
const hasActiveFilters = computed(
|
||||
() =>
|
||||
!!search.value.trim() ||
|
||||
!!dateFrom.value ||
|
||||
!!dateTo.value ||
|
||||
(activeTab.value === 'history' && !!historyTypeFilter.value),
|
||||
)
|
||||
|
||||
function clearListFilters() {
|
||||
search.value = ''
|
||||
dateFrom.value = ''
|
||||
dateTo.value = ''
|
||||
historyTypeFilter.value = ''
|
||||
}
|
||||
|
||||
/** 本地日界转 RFC3339(含 Z),供 gin 默认 time.Time 绑定 */
|
||||
function localDayBoundToRfc3339(ymd: string, endOfDay: boolean): string {
|
||||
const parts = ymd.split('-').map(Number)
|
||||
const y = parts[0] ?? 1970
|
||||
const m = parts[1] ?? 1
|
||||
const d = parts[2] ?? 1
|
||||
const date = endOfDay
|
||||
? new Date(y, m - 1, d, 23, 59, 59, 999)
|
||||
: new Date(y, m - 1, d, 0, 0, 0, 0)
|
||||
return date.toISOString()
|
||||
}
|
||||
|
||||
/** 时间范围:两端齐全时返回 [start, end](RFC3339);仅一端时补全另一端 */
|
||||
function getCreatedAtRange(): string[] | undefined {
|
||||
if (!dateFrom.value && !dateTo.value) return undefined
|
||||
const startYmd = dateFrom.value || '1970-01-01'
|
||||
const endYmd =
|
||||
dateTo.value ||
|
||||
(() => {
|
||||
const n = new Date()
|
||||
const y = n.getFullYear()
|
||||
const m = String(n.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(n.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${d}`
|
||||
})()
|
||||
return [localDayBoundToRfc3339(startYmd, false), localDayBoundToRfc3339(endYmd, true)]
|
||||
}
|
||||
|
||||
function getStartEndCreatedAt(): { startCreatedAt?: string; endCreatedAt?: string } {
|
||||
const range = getCreatedAtRange()
|
||||
if (!range) return {}
|
||||
return { startCreatedAt: range[0], endCreatedAt: range[1] }
|
||||
}
|
||||
/** 当前展示的持仓列表(mock 或 API) */
|
||||
const currentPositionList = computed(() => (USE_MOCK_WALLET ? positions.value : positionList.value))
|
||||
/** 未结算项:从持仓列表中筛出可领取的(有 marketID+tokenID,且所属市场已关闭 market.closed=true) */
|
||||
@ -645,6 +773,8 @@ interface HistoryItem {
|
||||
tradeEventId?: string
|
||||
tradeEventSlug?: string
|
||||
detailMarketId?: string
|
||||
/** 问题到期时间展示文案 */
|
||||
expiresAt?: string
|
||||
}
|
||||
|
||||
function canOpenTradeDetail(opts: { tradeEventId?: string; tradeEventSlug?: string }): boolean {
|
||||
@ -807,8 +937,9 @@ async function loadPositionList() {
|
||||
}
|
||||
positionLoading.value = true
|
||||
try {
|
||||
const { startCreatedAt, endCreatedAt } = getStartEndCreatedAt()
|
||||
const res = await getPositionList(
|
||||
{ page: page.value, pageSize: itemsPerPage.value, userID },
|
||||
{ page: page.value, pageSize: itemsPerPage.value, userID, startCreatedAt, endCreatedAt },
|
||||
{ headers },
|
||||
)
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
@ -850,12 +981,15 @@ async function loadOpenOrders() {
|
||||
}
|
||||
openOrderLoading.value = true
|
||||
try {
|
||||
const { startCreatedAt, endCreatedAt } = getStartEndCreatedAt()
|
||||
const res = await getOrderList(
|
||||
{
|
||||
page: page.value,
|
||||
pageSize: itemsPerPage.value,
|
||||
userID,
|
||||
status: OrderStatus.Live,
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
},
|
||||
{ headers },
|
||||
)
|
||||
@ -902,12 +1036,16 @@ async function loadHistoryOrders() {
|
||||
}
|
||||
historyLoading.value = true
|
||||
try {
|
||||
const eventName = search.value.trim()
|
||||
const res = await getHistoryRecordListClient(
|
||||
{
|
||||
page: page.value,
|
||||
pageSize: itemsPerPage.value,
|
||||
userId: userID,
|
||||
isFunding: false,
|
||||
keyword: eventName || undefined,
|
||||
type: historyTypeFilter.value || undefined,
|
||||
createdAtRange: getCreatedAtRange(),
|
||||
},
|
||||
{ headers },
|
||||
)
|
||||
@ -1067,8 +1205,12 @@ const filteredOpenOrders = computed(() => {
|
||||
})
|
||||
const filteredHistory = computed(() => {
|
||||
const list = USE_MOCK_WALLET ? history.value : historyList.value
|
||||
const type = historyTypeFilter.value
|
||||
return list.filter(
|
||||
(h) => matchSearch(h.market) && (USE_MOCK_WALLET ? !isFundingHistory(h) : true),
|
||||
(h) =>
|
||||
matchSearch(h.market) &&
|
||||
(!type || (h.recordType ?? '').toUpperCase() === type) &&
|
||||
(USE_MOCK_WALLET ? !isFundingHistory(h) : true),
|
||||
)
|
||||
})
|
||||
const page = ref(1)
|
||||
@ -1150,6 +1292,32 @@ watch(withdrawStatusFilter, () => {
|
||||
page.value = 1
|
||||
if (activeTab.value === 'withdrawals') loadWithdrawals()
|
||||
})
|
||||
|
||||
let searchReloadTimer: ReturnType<typeof setTimeout> | null = null
|
||||
function reloadFilteredLists() {
|
||||
if (USE_MOCK_WALLET) return
|
||||
if (activeTab.value === 'positions') loadPositionList()
|
||||
if (activeTab.value === 'orders') loadOpenOrders()
|
||||
if (activeTab.value === 'history') loadHistoryOrders()
|
||||
}
|
||||
|
||||
watch(search, () => {
|
||||
page.value = 1
|
||||
if (USE_MOCK_WALLET) return
|
||||
// History 走服务端模糊检索;持仓/挂单仍用前端 matchSearch
|
||||
if (activeTab.value !== 'history') return
|
||||
if (searchReloadTimer) clearTimeout(searchReloadTimer)
|
||||
searchReloadTimer = setTimeout(() => loadHistoryOrders(), 300)
|
||||
})
|
||||
watch([dateFrom, dateTo], () => {
|
||||
page.value = 1
|
||||
reloadFilteredLists()
|
||||
})
|
||||
watch(historyTypeFilter, () => {
|
||||
page.value = 1
|
||||
if (USE_MOCK_WALLET) return
|
||||
if (activeTab.value === 'history') loadHistoryOrders()
|
||||
})
|
||||
watch([currentListTotal, itemsPerPage], () => {
|
||||
const maxPage = currentTotalPages.value
|
||||
if (page.value > maxPage) page.value = Math.max(1, maxPage)
|
||||
@ -1271,7 +1439,11 @@ watch(
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (!USE_MOCK_WALLET && activeTab.value === 'positions') loadPositionList()
|
||||
if (USE_MOCK_WALLET) return
|
||||
if (activeTab.value === 'positions') loadPositionList()
|
||||
if (activeTab.value === 'history') loadHistoryOrders()
|
||||
if (activeTab.value === 'orders') loadOpenOrders()
|
||||
if (activeTab.value === 'withdrawals') loadWithdrawals()
|
||||
})
|
||||
|
||||
function onWithdrawSuccess() {
|
||||
@ -1537,8 +1709,18 @@ async function submitAuthorize() {
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wallet-tabs {
|
||||
.wallet-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.wallet-tabs {
|
||||
flex: 0 1 auto;
|
||||
margin-bottom: 0;
|
||||
min-width: 0;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
@ -1569,6 +1751,75 @@ async function submitAuthorize() {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wallet-filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1 1 220px;
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wallet-filter-search {
|
||||
flex: 1 1 160px;
|
||||
min-width: 140px;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.wallet-filter-search :deep(.v-field) {
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wallet-filter-search :deep(.v-field__outline) {
|
||||
--v-field-border-opacity: 1;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* 搜索图标左侧留白 */
|
||||
.wallet-filter-search :deep(.v-field__prepend-inner) {
|
||||
padding-inline-start: 10px;
|
||||
padding-inline-end: 2px;
|
||||
}
|
||||
|
||||
.wallet-filter-search :deep(.v-field__input) {
|
||||
padding-inline-start: 4px;
|
||||
}
|
||||
|
||||
.wallet-filter-chip {
|
||||
text-transform: none;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0;
|
||||
border-radius: 999px !important;
|
||||
border-color: #e5e7eb !important;
|
||||
color: #374151;
|
||||
background: #fff;
|
||||
min-height: 34px;
|
||||
padding-inline: 12px;
|
||||
}
|
||||
|
||||
.wallet-filter-chip--active {
|
||||
border-color: #2d5bff !important;
|
||||
color: #2d5bff;
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.wallet-filter-menu-list {
|
||||
min-width: 160px;
|
||||
border-radius: 12px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.wallet-filter-clear {
|
||||
text-transform: none;
|
||||
min-width: auto;
|
||||
padding-inline: 4px;
|
||||
color: #6b7280;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user