Restyle the web UI as a dark trading terminal

- Single palette (web/src/styles/palette.ts) taken from the Stitch
  design, exposed as ap-* UnoCSS colors and --ap-* CSS variables and
  used for Element Plus dark theme overrides, KLineChart and the news
  iframe; zero radius, JetBrains Mono / Space Grotesk bundled locally
- New sidebar and header layout; trading page, plan card and analysis
  drawer follow the design; other pages use the same panels
- Status API reports today's closed trades, wins and net PnL, shown as
  today's PnL and win rate on the trading page
This commit is contained in:
eezd committed 2026-10-15 18:28:59 +08:00
1 parent 665c12fc31
commit d5fa78f1ad
31 files changed
+1199 -505

No files matched your search

@@ -124,7 +124,7 @@ loadLatest()
{{ formatTime(source.last_success_at) }}
</el-descriptions-item>
<el-descriptions-item label="连续失败">
<span :class="{ 'text-red-500 font-bold': source.consecutive_failures > 0 }">{{ source.consecutive_failures }}</span>
<span :class="{ 'text-ap-down font-bold': source.consecutive_failures > 0 }">{{ source.consecutive_failures }}</span>
</el-descriptions-item>
<el-descriptions-item label="下次运行">
{{ formatTime(source.paused_until && source.paused_until > (source.process?.next_run_at ?? "") ? source.paused_until : source.process?.next_run_at) }}
@@ -157,7 +157,7 @@ loadLatest()
</div>
</el-descriptions-item>
<el-descriptions-item v-if="source.last_error" label="最近错误" :span="4">
<span class="whitespace-pre-wrap text-xs text-red-500">{{ source.last_error }}</span>
<span class="whitespace-pre-wrap text-xs text-ap-down">{{ source.last_error }}</span>
</el-descriptions-item>
</el-descriptions>
@@ -171,7 +171,7 @@ loadLatest()
</template>
<div v-for="item in latest" :key="item.id" class="mb-2 text-sm">
<span class="mr-2 text-xs text-[var(--el-text-color-secondary)]">{{ formatTime(item.published_at, "MM-DD HH:mm") }}</span>
<el-link :href="item.url" target="_blank">
<el-link :href="item.url" target="_blank" class="font-sans">
{{ item.title || item.excerpt.slice(0, 60) }}
</el-link>
</div>
@@ -189,10 +189,10 @@ loadLatest()
<el-switch v-model="autoScroll" active-text="自动滚动" size="small" />
</div>
</template>
<div ref="logBox" class="min-h-0 flex-1 overflow-auto rounded bg-[#1e1e1e] p-2 text-xs text-[#d4d4d4] font-mono leading-5">
<div ref="logBox" class="min-h-0 flex-1 overflow-auto border border-ap-line border-solid bg-ap-sunken p-2 text-xs text-ap-text font-mono leading-5">
<div v-for="log in visibleLogs" :key="log.id" class="whitespace-pre-wrap break-all">
<span class="text-[#808080]">{{ formatTime(log.ts) }}</span>
<span :class="{ 'text-yellow-400': log.level === 'WARNING', 'text-red-400': log.level === 'ERROR' || log.level === 'CRITICAL' }" class="mx-2">{{ log.level }}</span>{{ log.message }}
<span class="text-ap-faint">{{ formatTime(log.ts) }}</span>
<span :class="{ 'text-ap-warn': log.level === 'WARNING', 'text-ap-down': log.level === 'ERROR' || log.level === 'CRITICAL' }" class="mx-2">{{ log.level }}</span>{{ log.message }}
</div>
</div>
</el-card>
+5 -5
View File
@@ -73,14 +73,14 @@ refresh().then(() => Promise.all(info.value!.sources.map(s => loadHistory(s.code
<template>
<div v-if="info" class="h-full flex flex-col gap-3">
<div class="flex items-center gap-3">
<div class="flex items-center gap-3 border border-ap-line border-solid bg-ap-panel px-3 py-2">
<el-tag :type="info.manager_alive ? 'success' : 'danger'">
{{ info.manager_alive ? "管理进程运行中" : "管理进程未运行" }}
<i class="mr-1.5 inline-block h-1.5 w-1.5 bg-current" :class="{ 'animate-pulse': info.manager_alive }" />{{ info.manager_alive ? "管理进程运行中" : "管理进程未运行" }}
</el-tag>
<el-tag :type="connected ? 'success' : 'danger'">
{{ connected ? "日志推送已连接" : "日志推送断开" }}
<i class="mr-1.5 inline-block h-1.5 w-1.5 bg-current" />{{ connected ? "日志推送已连接" : "日志推送断开" }}
</el-tag>
<span class="text-sm text-[var(--el-text-color-secondary)]">数据库 {{ formatBytes(info.db_size_bytes) }}</span>
<span class="text-xs text-ap-muted">数据库 <b class="text-ap-text">{{ formatBytes(info.db_size_bytes) }}</b></span>
<div class="flex-1" />
<el-button type="danger" :loading="restarting.size > 0" @click="restart('all')">
重启全部采集
@@ -89,7 +89,7 @@ refresh().then(() => Promise.all(info.value!.sources.map(s => loadHistory(s.code
<el-tabs v-model="activeTab" type="border-card" class="crawler-tabs min-h-0 flex flex-1 flex-col">
<el-tab-pane v-for="source in info.sources" :key="source.code" :name="source.code" class="h-full">
<template #label>
<span :class="{ 'text-red-500 font-bold': source.consecutive_failures >= FAILURE_ALERT }">
<span :class="{ 'text-ap-down font-bold': source.consecutive_failures >= FAILURE_ALERT }">
{{ source.name }}
</span>
<el-badge v-if="source.consecutive_failures >= FAILURE_ALERT" :value="source.consecutive_failures" class="ml-1" />