- 首页双入口(智能选股/策略回测):引入 vue-router,顶部导航 - 智能选股:自然语言 -> LLM 解析结构化条件(智谱 GLM,OpenAI 兼容,/v4 兼容)-> SQL 快照预筛 + pandas 指标过滤(复用 indicators 单一事实源) - 条件模型:指标 vs 常数/指标(value_indicator,如 DIF>DEA、close<布林下轨)、lookback+match 表达连续N天/近N天任一天、市值/PE/PB/换手率快照条件、默认排除 ST/退市/北交所 - 全市场数据同步:按 trade_date 批量拉取未复权日线(与回测 candles qfq 隔离),交易日历/股票列表本地缓存,daily_basic 仅最新截面,Tushare 限频兜底(分钟级重试/小时级降级) - 存储:DATABASE_URL 切远程 PostgreSQL(cirry.cn/stock),本地 SQLite 已移除 - .env 入库(私有仓库);smoke_test 扩展选股链路 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
2.1 KiB
Vue
57 lines
2.1 KiB
Vue
<script setup lang="ts">
|
||
import { computed } from 'vue';
|
||
import Button from 'primevue/button';
|
||
import type { ScreenerSyncStatus } from '@/api/types';
|
||
|
||
const props = defineProps<{ status: ScreenerSyncStatus | null }>();
|
||
const emit = defineEmits<{ (e: 'sync'): void }>();
|
||
|
||
function fmtDate(s?: string | null) {
|
||
if (!s) return '—';
|
||
return s.slice(0, 10);
|
||
}
|
||
|
||
// 数据是否可筛(有日线)。快照缺失则纯指标仍可用,给黄色提示。
|
||
const freshness = computed(() => {
|
||
const s = props.status;
|
||
if (!s) return { tone: 'none', text: '正在检查数据状态…' };
|
||
if (!s.ready) return { tone: 'warn', text: '全市场数据尚未同步,请先点击右侧「同步数据」' };
|
||
const snapMissing = s.stats.snapshot_rows === 0;
|
||
const d = s.stats.dates;
|
||
const base = `数据截至 ${fmtDate(s.last_trade_date)} · 共 ${d} 个交易日 · ${s.stats.stocks} 只股票`;
|
||
if (snapMissing) return { tone: 'warn', text: `${base}(每日指标暂不可用:市值/市盈率条件不可用,纯指标条件不受影响)` };
|
||
return { tone: 'ok', text: base };
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="sync-bar">
|
||
<span :class="freshness.tone === 'ok' ? 'ok' : freshness.tone === 'warn' ? 'warn' : ''">
|
||
<i class="pi" :class="freshness.tone === 'ok' ? 'pi-check-circle' : 'pi-info-circle'" style="margin-right: 5px;"></i>
|
||
{{ freshness.text }}
|
||
</span>
|
||
|
||
<template v-if="status && status.running">
|
||
<span class="sync-progress">
|
||
<i class="pi pi-spin pi-spinner"></i>
|
||
<span class="txt">{{ status.step || '同步中…' }}({{ status.done_days }}/{{ status.total_days }})</span>
|
||
</span>
|
||
</template>
|
||
<template v-else>
|
||
<span class="spacer"></span>
|
||
<Button
|
||
label="同步市场数据"
|
||
icon="pi pi-refresh"
|
||
size="small"
|
||
severity="secondary"
|
||
:disabled="status?.running"
|
||
@click="emit('sync')"
|
||
/>
|
||
</template>
|
||
|
||
<div v-if="status && status.error" class="warn" style="flex-basis: 100%; margin-top: 4px;">
|
||
<i class="pi pi-exclamation-triangle" style="margin-right: 5px;"></i>{{ status.error }}
|
||
</div>
|
||
</div>
|
||
</template>
|