From 4dae5e5d5750ab5025ed5e2a6da0feb1c0fbc9ef Mon Sep 17 00:00:00 2001
From: dongzp_book <90fanhua@gmail.com>
Date: Thu, 23 Jul 2026 23:48:04 +0800
Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=85=E5=AD=98=E6=B3=84?=
=?UTF-8?q?=E9=9C=B2=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/baidu/mapWsQuote.ts | 76 +++++++++++----------
src/components/quote/ChartPanel.vue | 68 +++++++++++++++---
src/components/quote/LargeOrderAnalysis.vue | 48 +++++++++++--
src/composables/useAiAdvice.ts | 6 +-
src/stores/quota.ts | 8 ++-
5 files changed, 148 insertions(+), 58 deletions(-)
diff --git a/src/api/baidu/mapWsQuote.ts b/src/api/baidu/mapWsQuote.ts
index 9e8ba46..215bd4d 100644
--- a/src/api/baidu/mapWsQuote.ts
+++ b/src/api/baidu/mapWsQuote.ts
@@ -58,7 +58,7 @@ function upsertIntraday(
const price = toNum(priceRaw)
const avg = toNum(avgRaw)
const totalVolume = toNum(totalVolumeRaw)
- const list = [...points]
+ const list = points.slice()
const idx = list.findIndex((p) => p.time === time)
const othersSum = list.reduce((s, p, i) => (i === idx ? s : s + p.volume), 0)
@@ -73,8 +73,11 @@ function upsertIntraday(
return list
}
-/** Merge tick detailinfos into quote.trades (newest first, dedupe, cap 200). */
-export function applyWsTick(quote: QuoteData, data: BaiduWsTickData): QuoteData {
+/**
+ * Merge tick detailinfos into quote.trades (newest first, dedupe, cap 200).
+ * Mutates quote in place so Vue only invalidates trade-dependent effects.
+ */
+export function applyWsTick(quote: QuoteData, data: BaiduWsTickData): void {
const incoming: TradeTick[] = [...(data.detailinfos ?? [])]
.reverse()
.map((t) => ({
@@ -93,26 +96,27 @@ export function applyWsTick(quote: QuoteData, data: BaiduWsTickData): QuoteData
merged.push(t)
if (merged.length >= MAX_TRADES) break
}
- return { ...quote, trades: merged }
+ quote.trades = merged
}
-/** Apply snapshot fields onto quote (price, book, pankou, intraday point). */
-export function applyWsSnapshot(quote: QuoteData, data: BaiduWsSnapshotData): QuoteData {
- const next: QuoteData = { ...quote }
-
+/**
+ * Apply snapshot fields onto quote (price, book, pankou, intraday point).
+ * Mutates quote in place — do not replace quote.value, or every tick rebuilds charts.
+ */
+export function applyWsSnapshot(quote: QuoteData, data: BaiduWsSnapshotData): void {
if (data.cur) {
const c = data.cur
- if (c.price != null) next.last = toNum(c.price, next.last)
- if (c.increase != null) next.change = toNum(c.increase, next.change)
- if (c.ratio != null) next.changePercent = toNum(c.ratio, next.changePercent)
- if (c.avgPrice != null) next.avg = toNum(c.avgPrice, next.avg)
- if (c.status) next.status = c.status
+ if (c.price != null) quote.last = toNum(c.price, quote.last)
+ if (c.increase != null) quote.change = toNum(c.increase, quote.change)
+ if (c.ratio != null) quote.changePercent = toNum(c.ratio, quote.changePercent)
+ if (c.avgPrice != null) quote.avg = toNum(c.avgPrice, quote.avg)
+ if (c.status) quote.status = c.status
}
if (data.update) {
- if (data.update.text) next.updatedAt = data.update.text
- if (data.update.stockStatus) next.status = data.update.stockStatus
- else if (data.update.tradeStatusCN) next.status = data.update.tradeStatusCN
+ if (data.update.text) quote.updatedAt = data.update.text
+ if (data.update.stockStatus) quote.status = data.update.stockStatus
+ else if (data.update.tradeStatusCN) quote.status = data.update.tradeStatusCN
}
if (data.pankouinfos?.length) {
@@ -120,20 +124,20 @@ export function applyWsSnapshot(quote: QuoteData, data: BaiduWsSnapshotData): Qu
const num = (ename: string, fallback: number) =>
by[ename] ? toNum(by[ename]!.originValue ?? by[ename]!.value, fallback) : fallback
- next.open = num('open', next.open)
- next.high = num('high', next.high)
- next.low = num('low', next.low)
- next.prevClose = num('preClose', next.prevClose)
- next.volume = num('volume', next.volume)
- next.openInterest = num('holdingAmount', next.openInterest)
- next.amplitude = num('amplitudeRatio', next.amplitude)
- next.settlement = num('settlement', next.settlement)
- next.prevSettlement = num('prevSettlement', next.prevSettlement)
- next.outerVol = num('outside', next.outerVol)
- next.innerVol = num('inside', next.innerVol)
- if (by.avgPrice) next.avg = num('avgPrice', next.avg)
+ quote.open = num('open', quote.open)
+ quote.high = num('high', quote.high)
+ quote.low = num('low', quote.low)
+ quote.prevClose = num('preClose', quote.prevClose)
+ quote.volume = num('volume', quote.volume)
+ quote.openInterest = num('holdingAmount', quote.openInterest)
+ quote.amplitude = num('amplitudeRatio', quote.amplitude)
+ quote.settlement = num('settlement', quote.settlement)
+ quote.prevSettlement = num('prevSettlement', quote.prevSettlement)
+ quote.outerVol = num('outside', quote.outerVol)
+ quote.innerVol = num('inside', quote.innerVol)
+ if (by.avgPrice) quote.avg = num('avgPrice', quote.avg)
if (by.amount) {
- next.amount = parseAmountYi(by.amount.value, by.amount.originValue)
+ quote.amount = parseAmountYi(by.amount.value, by.amount.originValue)
}
if (by.amountDelta) {
const raw = by.amountDelta.originValue
@@ -141,7 +145,7 @@ export function applyWsSnapshot(quote: QuoteData, data: BaiduWsSnapshotData): Qu
const unavailable =
(raw == null || !Number.isFinite(raw)) && (!display || display === '--')
if (!unavailable) {
- next.amountDelta = parseAmountDelta(display, raw)
+ quote.amountDelta = parseAmountDelta(display, raw)
}
}
}
@@ -166,20 +170,18 @@ export function applyWsSnapshot(quote: QuoteData, data: BaiduWsSnapshotData): Qu
const bidVol = bids.reduce((s, b) => s + b.volume, 0)
const askVol = asks.reduce((s, a) => s + a.volume, 0)
const total = bidVol + askVol
- next.orderBook = { asks, bids }
- next.buyRatio = total > 0 ? Math.round((bidVol / total) * 100) : 50
- next.sellRatio = 100 - next.buyRatio
+ quote.orderBook = { asks, bids }
+ quote.buyRatio = total > 0 ? Math.round((bidVol / total) * 100) : 50
+ quote.sellRatio = 100 - quote.buyRatio
}
if (data.point) {
- next.intraday = upsertIntraday(
- next.intraday,
+ quote.intraday = upsertIntraday(
+ quote.intraday,
data.point.price,
data.point.avgPrice,
data.point.time,
data.point.totalVolume,
)
}
-
- return next
}
diff --git a/src/components/quote/ChartPanel.vue b/src/components/quote/ChartPanel.vue
index 439ecbc..463f33c 100644
--- a/src/components/quote/ChartPanel.vue
+++ b/src/components/quote/ChartPanel.vue
@@ -6,12 +6,12 @@
-
+
diff --git a/src/composables/useAiAdvice.ts b/src/composables/useAiAdvice.ts
index fe50436..aa6f343 100644
--- a/src/composables/useAiAdvice.ts
+++ b/src/composables/useAiAdvice.ts
@@ -108,8 +108,10 @@ export function useAiAdvice() {
snapshot: quota.getAnalysisSnapshot(),
}
- console.log('[AI] 提交给 DeepSeek 的关键词', payload.keywords)
- console.log('[AI] 提交给 DeepSeek 的数据快照', payload.snapshot)
+ // 勿 console.log 完整 snapshot:DevTools 会长期持有大对象导致内存暴涨
+ if (import.meta.env.DEV) {
+ console.log('[AI] 分析', payload.snapshot.contract.code, payload.keywords.slice(0, 80))
+ }
const advice = await fetchDeepSeekAdvice(apiKey, payload)
if (epoch !== analysisEpoch) return
diff --git a/src/stores/quota.ts b/src/stores/quota.ts
index 93413bd..39319af 100644
--- a/src/stores/quota.ts
+++ b/src/stores/quota.ts
@@ -137,10 +137,11 @@ export const useQuotaStore = defineStore('quota', () => {
const code = activeContract().code
if (msg.data.code && msg.data.code !== code) return
const product = msg.data.product
+ // Mutate in place — replacing quote.value forces every chart/UI to rebuild.
if (product === 'tick') {
- quote.value = applyWsTick(quote.value, msg.data as BaiduWsTickData)
+ applyWsTick(quote.value, msg.data as BaiduWsTickData)
} else if (product === 'snapshot') {
- quote.value = applyWsSnapshot(quote.value, msg.data as BaiduWsSnapshotData)
+ applyWsSnapshot(quote.value, msg.data as BaiduWsSnapshotData)
}
}
@@ -181,10 +182,11 @@ export const useQuotaStore = defineStore('quota', () => {
startSessionWatch()
}
- /** (Re)connect only while page wants WS and within trading session. */
+ /** Connect only while page wants WS and within trading session; skip if already running. */
function connectWs() {
if (!wsDesired || !isWsTradingSession()) return
wsInSession.value = true
+ if (quoteWs?.isRunning()) return
quoteWs?.disconnect()
quoteWs = new BaiduQuoteWs({
onMessage: handleWsMessage,