Files
Chan/web/static/js/app/chart_tv_finalize.js
T
jackyu66gitandCursor dbb6202325 feat(ECR-008): 拆分主站 chart_tv.js 为多模块薄门面
行为冻结物理拆分;保留 initTradingView/dispose 对外 API;无打包器。node --check 全绿。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 15:09:48 +08:00

229 lines
10 KiB
JavaScript

/* chart_tv_finalize.js — time sync / bindSync / view restore / tooltip */
function chartTvFinalize(ctx) {
var symbol = ctx.symbol;
var timeframe = ctx.timeframe;
var symbolConfig = ctx.symbolConfig;
var useSubSubPeriod = ctx.useSubSubPeriod;
var useElementPeriod = ctx.useElementPeriod;
var klinePeriodLabel = ctx.klinePeriodLabel;
var candles = ctx.candles;
var klineDataSource = ctx.klineDataSource;
var container = ctx.container;
var showMacd = ctx.showMacd;
var showOriginalKline = ctx.showOriginalKline;
var mainChartContainer = ctx.mainChartContainer;
var volumeChartContainer = ctx.volumeChartContainer;
var atrChartContainer = ctx.atrChartContainer;
var macdChartContainer = ctx.macdChartContainer;
var chanMacdChartContainer = ctx.chanMacdChartContainer;
var mainChart = ctx.mainChart;
var volumeChart = ctx.volumeChart;
var atrChart = ctx.atrChart;
var macdChart = ctx.macdChart;
var chanMacdChart = ctx.chanMacdChart;
var createChartOptions = ctx.createChartOptions;
// 同步所有图表的时间轴配置
const syncTimeScaleSettings = () => {
// 获取主图表的时间轴设置
const mainTimeScale = mainChart.timeScale();
const baseOptions = {
timeVisible: true,
secondsVisible: false,
borderColor: '#ddd',
barSpacing: symbolConfig.type === 'a_stock' ? 6 : 10,
rightOffset: 12,
lockVisibleTimeRangeOnResize: true,
// 关键:确保所有图表边缘行为完全一致
fixLeftEdge: false,
fixRightEdge: false,
// 确保时间刻度行为一致
ticksVisible: true,
minimumHeight: 0,
};
console.log('🔧 同步时间轴设置:', baseOptions);
// 应用相同的设置到所有图表
mainChart.timeScale().applyOptions(baseOptions);
volumeChart.timeScale().applyOptions(baseOptions);
atrChart.timeScale().applyOptions(baseOptions);
if (showMacd && macdChart) {
macdChart.timeScale().applyOptions(baseOptions);
}
};
// 首先同步时间轴设置
syncTimeScaleSettings();
// 仅在没有待恢复视图时,设置默认可见范围
const totalBars = candles ? candles.length : 0;
const visibleBarsCount = 200;
const hasPendingRestoreView = !!window._pendingRestoreView;
if (!hasPendingRestoreView) {
// 显示最近 200 根K线而非全部挤压(避免K线过多时重叠)
if (totalBars > visibleBarsCount) {
const rangeFrom = totalBars - visibleBarsCount;
const rangeTo = totalBars + 12;
mainChart.timeScale().setVisibleLogicalRange({ from: rangeFrom, to: rangeTo });
} else {
mainChart.timeScale().fitContent();
}
}
// 立即同步其他图表到主图表的范围
setTimeout(() => {
const logRange = mainChart.timeScale().getVisibleLogicalRange();
if (logRange) {
console.log('🔧 同步可见范围:', logRange);
volumeChart.timeScale().setVisibleLogicalRange(logRange);
atrChart.timeScale().setVisibleLogicalRange(logRange);
if (showMacd && macdChart) {
macdChart.timeScale().setVisibleLogicalRange(logRange);
}
if (showMacd && chanMacdChart) {
chanMacdChart.timeScale().setVisibleLogicalRange(logRange);
}
console.log('🔧 时间轴同步完成');
}
}, 50);
// 保存图表对象
tvWidget.mainChart = mainChart;
tvWidget.volumeChart = volumeChart;
tvWidget.atrChart = atrChart;
tvWidget.macdChart = macdChart;
tvWidget.chanMacdChart = chanMacdChart;
tvWidget.state.isInitialized = true;
// 注册窗口卸载时释放资源,避免GPU内存泄漏
window.onbeforeunload = function() {
try {
if (tvWidget && tvWidget.state && tvWidget.state.isInitialized) {
if (tvWidget.mainChart && typeof tvWidget.mainChart.remove === 'function') tvWidget.mainChart.remove();
if (tvWidget.volumeChart && typeof tvWidget.volumeChart.remove === 'function') tvWidget.volumeChart.remove();
if (tvWidget.macdChart && typeof tvWidget.macdChart.remove === 'function') tvWidget.macdChart.remove();
if (tvWidget.chanMacdChart && typeof tvWidget.chanMacdChart.remove === 'function') tvWidget.chanMacdChart.remove();
if (tvWidget.atrChart && typeof tvWidget.atrChart.remove === 'function') tvWidget.atrChart.remove();
}
} catch (e) {}
};
// 初始化默认均线/布林带配置(仅在首次初始化时)
if (!hasInitializedDefaultMAs && movingAverages.length === 0) {
console.log('初始化默认均线与布林带指标');
if (typeof maIdCounter !== 'number' || !Number.isFinite(maIdCounter)) {
maIdCounter = 0;
}
if (typeof bbIdCounter !== 'number' || !Number.isFinite(bbIdCounter)) {
bbIdCounter = 0;
}
const defaultMAs = [
{ type: 'EMA', length: 13, color: '#800080', name: 'EMA13', visible: true }, // 紫色
{ type: 'EMA', length: 26, color: '#FF8C00', name: 'EMA26', visible: true }, // 橙色
{ type: 'EMA', length: 52, color: '#000000', name: 'EMA52', visible: false }, // 黑色
{ type: 'EMA', length: 104, color: '#1E90FF', name: 'EMA104', visible: false }, // 蓝色
{ type: 'EMA', length: 156, color: '#F700FF', name: 'EMA156', visible: false } // 粉色
];
defaultMAs.forEach(ma => {
const config = {
id: ++maIdCounter,
type: ma.type,
length: ma.length,
source: 'close',
smoothType: 'none',
smoothLength: 3,
lineWidth: 1, // 1px线宽
lineStyle: 0, // 实线
color: ma.color,
visible: ma.visible
};
movingAverages.push(config);
console.log(`添加默认${ma.name}:`, ma.color);
});
if (bollingerBands.length === 0) {
const defaultBB = {
id: ++bbIdCounter,
type: 'Bollinger Bands',
length: 20,
upperMultiplier: 2,
lowerMultiplier: 2,
source: 'close',
lineWidth: 1,
lineStyle: 0,
upperColor: '#ff6b6b',
middleColor: '#ffffff',
lowerColor: '#ff6b6b',
visible: false
};
bollingerBands.push(defaultBB);
console.log('添加默认布林带: BB(20, 2, 2)');
}
console.log('默认指标配置完成,当前均线数量', movingAverages.length, '布林带数量', bollingerBands.length);
hasInitializedDefaultMAs = true;
}
// 添加均线到图表
addMovingAveragesToChart(candles);
// 添加布林带到图表
addBollingerBandsToChart(candles);
// 更新技术指标面板显示
updateIndicatorPanel();
// 绑定同步事件
bindSyncEvents(mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, mainChart, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
// 最终确保所有图表时间轴对齐(同时恢复刷新前保存的缩放/位置)
setTimeout(() => {
const allCharts = [mainChart, volumeChart, atrChart];
if (showMacd && macdChart) allCharts.push(macdChart);
if (showMacd && chanMacdChart) allCharts.push(chanMacdChart);
// 检查是否有待恢复的视图(缩放 + 位置)
const pending = window._pendingRestoreView;
window._pendingRestoreView = null;
if (pending) {
// 恢复刷新前的缩放和位置(优先可见范围/逻辑范围,最后回退到滚动位置)
console.log('📌 恢复图表视图:', JSON.stringify(pending));
restoreChartViewState(allCharts, pending);
} else {
// 无保存视图,正常同步主图到子图
const visibleRange = mainChart.timeScale().getVisibleRange();
if (visibleRange) {
console.log('🔧 最终同步可见范围:', visibleRange);
[volumeChart, atrChart].concat(
showMacd && macdChart ? [macdChart] : [],
showMacd && chanMacdChart ? [chanMacdChart] : []
).forEach(c => {
try { c.timeScale().setVisibleRange(visibleRange); } catch(e) {}
});
}
}
console.log('🔧 最终时间轴对齐完成');
}, 150);
// 只有在时间输入框都为空时才设置图表默认时间范围
if (!$('#start_time').val() && !$('#end_time').val()) {
setDefaultTimeRange();
}
// 添加买卖点提示
// 初始化 tooltip 与 U 显示状态
window.showUOnMain = $('#toggleUOnMain').is(':checked');
window.showUOnElement = $('#toggleUOnElement').is(':checked');
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
// 更新EMA52显示
if (currentData) {
updateEMA52Display(currentData);
}
}