This commit is contained in:
2026-09-09 15:07:58 +08:00
parent d656c05b3d
commit 71a0f6e404
31 changed files with 3657 additions and 9 deletions

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import {
addWatchlist, clearTrades, getStockDividends, getStockPreview, getTrades, getWatchlist as getWatchlistApi,
importTrades, removeWatchlist,
addHolding, addWatchlist, clearTrades, getHoldings as getHoldingsApi, getStockDividends, getStockPreview,
getTrades, getWatchlist as getWatchlistApi, importTrades, removeHolding, removeWatchlist,
} from '@/api/client';
import type {
ChartLayoutPrefs, PreviewResponse, ScreenerItemOut, StockDividendRecord, StockFinanceRecord,
@@ -21,6 +21,7 @@ const props = defineProps<{
const emit = defineEmits<{
(e: 'close'): void;
(e: 'watched-change'): void;
(e: 'held-change'): void;
/** 浮层内切股(键盘 ↑/↓、侧栏点击)时上报当前 ts_code父组件据此同步路由 */
(e: 'change', code: string): void;
}>();
@@ -215,6 +216,31 @@ async function toggleWatch() {
}
}
// ---------- 持仓股(标记进「持仓」分类) ----------
const held = ref(false);
const heldBusy = ref(false);
const heldSet = ref<Set<string>>(new Set());
async function refreshHeld() {
try {
heldSet.value = new Set(await getHoldingsApi());
} catch { /* 未登录等场景忽略 */ }
held.value = heldSet.value.has(active.value);
}
async function toggleHeld() {
if (heldBusy.value) return;
heldBusy.value = true;
try {
const list = held.value
? await removeHolding(active.value)
: await addHolding(active.value);
heldSet.value = new Set(list);
held.value = heldSet.value.has(active.value);
emit('held-change');
} catch { /* 忽略 */ } finally {
heldBusy.value = false;
}
}
// ---------- 实盘交易点(交割单导入) ----------
const trades = ref<UserTrade[]>([]);
const showTrades = ref(true);
@@ -504,7 +530,12 @@ watch(timeframe, () => load(active.value));
watch(active, (code) => {
watched.value = watchedSet.value.has(code);
}, { immediate: true });
// 切股时同步持仓状态
watch(active, (code) => {
held.value = heldSet.value.has(code);
}, { immediate: true });
void refreshWatched();
void refreshHeld();
function moveActive(delta: number) {
const list = filteredItems.value;
@@ -584,6 +615,19 @@ const fmtListDate = (s?: string | null) => (s && s.length === 8 ? `${s.slice(0,
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
</button>
<!-- 持仓标记 -->
<button
type="button"
class="shrink-0 rounded p-1 transition-colors hover:bg-[#26272E] hover:text-white disabled:opacity-50"
:class="held ? 'text-emerald-500' : 'text-[#C3C9D2]'"
:title="held ? '移出持仓' : '加入持仓'"
:disabled="heldBusy"
@click="toggleHeld"
>
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="7" width="20" height="14" rx="2" ry="2" /><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" />
</svg>
</button>
<div class="flex items-baseline gap-2">
<span class="text-base font-semibold text-[#E8EAED]">{{ header.name }}</span>
<span class="text-[13px] text-[#9BA3AE]">{{ active }}</span>