@@ -958,7 +952,7 @@
// 添加分型类型复选框变更事件
$('#showKlcFxType').change(function() {
- updateChartDisplay();
+ refreshChartOnly();
});
// 添加K线周期切换事件监听器
@@ -979,103 +973,7 @@
return;
}
- console.log(`当前选择的元素时间周期: ${elementTimeframe}`);
-
- // 设置API请求参数,只获取元素数据(笔、线段、中枢)
- const params = {
- symbol: $('#symbol').val(),
- timeframe: mainTimeframe,
- element_timeframe: elementTimeframe,
- timezone: $('#timezone').val(),
- elements_only: true // 只获取元素数据
- };
-
- // 获取并正确处理时间范围
- const startTimeVal = $('#start_time').val();
- const endTimeVal = $('#end_time').val();
-
- // 如果有设置开始和结束时间,添加到参数
- if (startTimeVal) {
- try {
- params.start_time = new Date(startTimeVal).getTime();
- console.log(`设置起始时间: ${startTimeVal} => ${params.start_time}`);
- } catch (e) {
- console.error('无效的开始时间格式:', startTimeVal, e);
- }
- }
-
- if (endTimeVal) {
- try {
- params.end_time = new Date(endTimeVal).getTime();
- console.log(`设置结束时间: ${endTimeVal} => ${params.end_time}`);
- } catch (e) {
- console.error('无效的结束时间格式:', endTimeVal, e);
- }
- }
-
- // 显示加载指示器
- $('#loadingIndicator').show();
- console.log('发送API请求获取元素数据:', params);
-
- // 调用API获取数据
- $.getJSON('/api/analyze', params, function(data) {
- // 隐藏加载指示器
- $('#loadingIndicator').hide();
-
- // 检查是否有错误
- if (data.error) {
- alert('错误: ' + data.error);
- console.error('API返回错误:', data.error);
- return;
- }
-
- console.log('API返回数据:', data);
-
- // 使用现有K线数据,添加新的元素数据
- if (currentData) {
- // 保存主周期的K线和MACD数据
- const originalKlineData = currentData.original_kline_data || currentData.kline_data;
- const originalMacd = currentData.original_macd || currentData.macd;
-
- // 首次保存原始数据
- if (!currentData.original_kline_data) {
- currentData.original_kline_data = originalKlineData;
- }
- if (!currentData.original_macd) {
- currentData.original_macd = originalMacd;
- }
-
- // 更新当前数据对象,但保留原始K线和指标数据
- currentData.element_timeframe = data.element_timeframe;
- currentData.element_bi_list = data.element_bi_list;
- currentData.element_seg_list = data.element_seg_list;
- currentData.element_zs_list = data.element_zs_list;
-
- // 确保K线和MACD数据始终使用主周期数据
- currentData.kline_data = originalKlineData;
- currentData.macd = originalMacd;
-
- console.log(`更新元素数据成功,元素周期: ${data.element_timeframe}`);
- console.log(`笔: ${data.element_bi_list ? data.element_bi_list.length : 0}条`);
- console.log(`线段: ${data.element_seg_list ? data.element_seg_list.length : 0}条`);
- console.log(`中枢: ${data.element_zs_list ? data.element_zs_list.length : 0}条`);
-
- // 只重绘分形元素图表(笔、线段、中枢)
- redrawFractalElements();
-
- // 更新分形元素的表格数据
- updateFractalTables();
- } else {
- console.error('当前没有主周期数据,无法更新元素数据');
- alert('请先获取主周期数据');
- }
- }).fail(function(jqxhr, textStatus, error) {
- // 隐藏加载指示器
- $('#loadingIndicator').hide();
- console.error('API请求失败:', textStatus, error);
- console.error('错误详情:', jqxhr.responseText);
- alert('请求失败: ' + textStatus + ', ' + error);
- });
+ console.log(`当前选择的元素时间周期: ${elementTimeframe},需要点击分析按钮来应用更改`);
});
// 比较两个时间周期的大小
@@ -1151,8 +1049,8 @@
console.log('- 显示线段:', $('#showMainSeg').is(':checked'));
console.log('- 显示中枢:', $('#showMainZs').is(':checked'));
console.log('- 显示未完成中枢:', $('#showMainUncompletedZs').is(':checked'));
- console.log('- 显示MACD:', $('#showMacd').is(':checked'));
- console.log('- 显示成交量:', $('#showVolume').is(':checked'));
+ console.log('- 显示MACD:', true);
+ console.log('- 显示成交量:', false);
console.log('- 显示分型类型:', $('#showKlcFxType').is(':checked'));
console.log('- 显示小周期分型:', $('#showElementKlcFxType').is(':checked'));
console.log('- 显示买卖点:', $('#showTradePoints').is(':checked'));
@@ -1420,7 +1318,7 @@
container.style.height = '100%';
// 是否显示MACD
- const showMacd = $('#showMacd').is(':checked');
+ const showMacd = true;
const showOriginalKline = $('#showOriginalKline').is(':checked');
// 创建主图容器
@@ -1442,24 +1340,24 @@
// 如果需要显示MACD,创建MACD容器
let macdChartContainer = null;
if (showMacd) {
- // 设置各图表高度
- mainChartContainer.style.height = '60%';
- volumeChartContainer.style.top = '60%';
- volumeChartContainer.style.height = '20%';
+ // 设置各图表高度 - 为三个图表分配合理比例
+ mainChartContainer.style.height = '50%'; // 主图占50%
+ volumeChartContainer.style.top = '50%';
+ volumeChartContainer.style.height = '25%'; // 成交量图占25%
macdChartContainer = document.createElement('div');
macdChartContainer.style.width = '100%';
- macdChartContainer.style.height = '20%';
+ macdChartContainer.style.height = '25%'; // MACD图占25%
macdChartContainer.style.position = 'absolute';
- macdChartContainer.style.top = '80%';
+ macdChartContainer.style.top = '75%'; // 从75%位置开始
macdChartContainer.style.left = '0';
macdChartContainer.style.right = '0';
macdChartContainer.style.borderTop = '1px solid #e0e0e0';
} else {
- // 不显示MACD时的高度
- mainChartContainer.style.height = '75%';
- volumeChartContainer.style.top = '75%';
- volumeChartContainer.style.height = '25%';
+ // 不显示MACD时的高度 - 主图和成交量图平分
+ mainChartContainer.style.height = '70%'; // 主图占70%
+ volumeChartContainer.style.top = '70%';
+ volumeChartContainer.style.height = '30%'; // 成交量图占30%
}
container.appendChild(mainChartContainer);
@@ -1470,10 +1368,22 @@
let syncInProgress = false;
// 创建统一的图表选项
- const createChartOptions = (showTimeScale = true) => {
+ const createChartOptions = (showTimeScale = true, chartType = 'main') => {
+ // 根据图表类型确定高度
+ let chartHeight;
+ if (chartType === 'main') {
+ chartHeight = mainChartContainer.clientHeight;
+ } else if (chartType === 'volume') {
+ chartHeight = volumeChartContainer.clientHeight;
+ } else if (chartType === 'macd') {
+ chartHeight = macdChartContainer ? macdChartContainer.clientHeight : 0;
+ } else {
+ chartHeight = mainChartContainer.clientHeight;
+ }
+
const baseOptions = {
width: mainChartContainer.clientWidth,
- height: mainChartContainer.clientHeight,
+ height: chartHeight,
layout: {
background: { color: '#ffffff' },
textColor: '#333',
@@ -1526,6 +1436,16 @@
},
rightPriceScale: {
borderColor: '#ddd',
+ scaleMargins: {
+ top: 0.1,
+ bottom: 0.1,
+ },
+ // 为标签留出更多空间,防止遮挡
+ minimumWidth: 80,
+ },
+ // 添加左边距配置
+ leftPriceScale: {
+ visible: false,
},
// 添加本地化选项,确保所有时间显示都使用选定的时区
localization: {
@@ -1567,6 +1487,10 @@
visible: showTimeScale,
borderColor: '#ddd',
barSpacing: symbolConfig.type === 'a_stock' ? 6 : 10,
+ // 移除可能影响拖动的固定边缘设置
+ // fixLeftEdge: true,
+ // fixRightEdge: true,
+ lockVisibleTimeRangeOnResize: true,
tickMarkFormatter: (time) => {
const selectedTimezone = symbolConfig.type === 'a_stock' ? 'Asia/Shanghai' : $('#timezone').val();
try {
@@ -1595,15 +1519,15 @@
};
// 创建主图表
- const mainChart = LightweightCharts.createChart(mainChartContainer, createChartOptions(true));
+ const mainChart = LightweightCharts.createChart(mainChartContainer, createChartOptions(true, 'main'));
// 创建成交量图表 - 只显示底部的时间轴
- const volumeChart = LightweightCharts.createChart(volumeChartContainer, createChartOptions(false));
+ const volumeChart = LightweightCharts.createChart(volumeChartContainer, createChartOptions(false, 'volume'));
// 创建MACD图表(如果需要)
let macdChart = null;
if (showMacd) {
- macdChart = LightweightCharts.createChart(macdChartContainer, createChartOptions(false));
+ macdChart = LightweightCharts.createChart(macdChartContainer, createChartOptions(false, 'macd'));
}
// 创建蜡烛图系列并设置数据
@@ -1639,19 +1563,15 @@
// 转换成交量数据 - 始终使用主K线周期数据
let volumes = [];
- if (useElementPeriod && currentData.element_kline_data && Array.isArray(currentData.element_kline_data)) {
- volumes = currentData.element_kline_data.map(kline => {
- // 创建日期对象并获取时间戳,不手动调整时区
- const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
- return {
- time: timestamp,
- value: parseFloat(kline.volume),
- color: parseFloat(kline.close) >= parseFloat(kline.open) ? 'rgba(220, 53, 69, 0.5)' : 'rgba(40, 167, 69, 0.5)',
- };
- });
- } else if (currentData.kline_data && Array.isArray(currentData.kline_data)) {
- volumes = currentData.kline_data.map(kline => {
- // 创建日期对象并获取时间戳,不手动调整时区
+ // 使用与K线和MACD相同的数据源选择逻辑
+ const volumeDataSource = useElementPeriod ? currentData.element_kline_data : currentData.kline_data;
+
+ console.log('成交量数据源选择:', useElementPeriod ? '次周期' : '主周期');
+ console.log('成交量数据长度:', volumeDataSource.length);
+
+ if (volumeDataSource && Array.isArray(volumeDataSource)) {
+ volumes = volumeDataSource.map(kline => {
+ // 使用与K线和MACD完全相同的时间戳计算方式
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
return {
time: timestamp,
@@ -1659,6 +1579,8 @@
color: parseFloat(kline.close) >= parseFloat(kline.open) ? 'rgba(220, 53, 69, 0.5)' : 'rgba(40, 167, 69, 0.5)',
};
});
+
+ console.log('处理后的成交量数据点数:', volumes.length);
}
// 添加成交量图表
@@ -1679,7 +1601,8 @@
color: '#2962FF',
lineWidth: 1,
title: 'MACD',
- lastValueVisible: true,
+ lastValueVisible: false, // 禁用最后值标签,防止遮挡
+ priceLineVisible: false, // 禁用价格线
});
// 创建信号线
@@ -1687,7 +1610,8 @@
color: '#FF6B6B',
lineWidth: 1,
title: 'Signal',
- lastValueVisible: true,
+ lastValueVisible: false, // 禁用最后值标签,防止遮挡
+ priceLineVisible: false, // 禁用价格线
});
// 创建直方图
@@ -1700,28 +1624,39 @@
},
});
- // 提取MACD数据
+ // 提取MACD数据 - 使用和K线数据相同的时间处理逻辑
const macdData = [];
const signalData = [];
const histogramData = [];
- for (let i = 0; i < currentData.kline_data.length; i++) {
- const kline = currentData.kline_data[i];
+ // 使用与K线数据相同的数据源来确保时间对齐
+ const klineDataSource = useElementPeriod ? currentData.element_kline_data : currentData.kline_data;
+ const macdDataSource = useElementPeriod ?
+ (currentData.element_macd || currentData.macd) : // 如果有次周期MACD数据则使用,否则使用主周期
+ currentData.macd; // 主周期使用主周期MACD数据
+
+ console.log('MACD数据源选择:', useElementPeriod ? '次周期' : '主周期');
+ console.log('K线数据长度:', klineDataSource.length);
+ console.log('MACD数据:', macdDataSource);
+
+ for (let i = 0; i < klineDataSource.length; i++) {
+ const kline = klineDataSource[i];
+ // 使用与K线完全相同的时间戳计算方式
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
- if (currentData.macd && currentData.macd.macd && currentData.macd.macd[i] !== undefined) {
+ if (macdDataSource && macdDataSource.macd && macdDataSource.macd[i] !== undefined) {
macdData.push({
time: timestamp,
- value: currentData.macd.macd[i]
+ value: macdDataSource.macd[i]
});
signalData.push({
time: timestamp,
- value: currentData.macd.signal[i]
+ value: macdDataSource.signal[i]
});
// 设置直方图颜色
- const histValue = currentData.macd.histogram[i];
+ const histValue = macdDataSource.histogram[i];
histogramData.push({
time: timestamp,
value: histValue,
@@ -1730,6 +1665,8 @@
}
}
+ console.log('处理后的MACD数据点数:', macdData.length);
+
macdLineSeries.setData(macdData);
signalLineSeries.setData(signalData);
histogramSeries.setData(histogramData);
@@ -1743,56 +1680,148 @@
// 同步图表的时间范围
function syncCharts(sourceChart, sourceContainer) {
- // 防止无限循环
- if (syncInProgress) return;
- syncInProgress = true;
-
- try {
- if (sourceChart) {
- const logicalRange = sourceChart.timeScale().getVisibleLogicalRange();
-
- if (logicalRange) {
- if (sourceChart !== mainChart) {
- mainChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
-
- if (sourceChart !== volumeChart) {
- volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
-
- if (showMacd && macdChart && sourceChart !== macdChart) {
- macdChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
- }
- }
- } catch (e) {
- console.error('同步图表出错:', e);
+ // 防止无限循环 - 使用更精确的检查
+ if (syncInProgress) {
+ console.log('🔄 同步正在进行中,跳过此次同步');
+ return;
}
- setTimeout(() => { syncInProgress = false; }, 50);
+ syncInProgress = true;
+ console.log('🚀 开始同步图表,来源:',
+ sourceChart === mainChart ? '主图' :
+ sourceChart === volumeChart ? '成交量图' : 'MACD图');
+
+ try {
+ if (sourceChart && sourceChart.timeScale) {
+ const logicalRange = sourceChart.timeScale().getVisibleLogicalRange();
+
+ if (logicalRange && logicalRange.from !== undefined && logicalRange.to !== undefined) {
+ console.log('📊 同步时间范围:', logicalRange);
+
+ // 同步主图
+ if (sourceChart !== mainChart && mainChart && mainChart.timeScale) {
+ try {
+ mainChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ 主图同步完成');
+ } catch (e) {
+ console.error('❌ 主图同步失败:', e);
+ }
+ }
+
+ // 同步成交量图
+ if (sourceChart !== volumeChart && volumeChart && volumeChart.timeScale) {
+ try {
+ volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ 成交量图同步完成');
+ } catch (e) {
+ console.error('❌ 成交量图同步失败:', e);
+ }
+ }
+
+ // 同步MACD图
+ if (showMacd && macdChart && sourceChart !== macdChart && macdChart.timeScale) {
+ try {
+ macdChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ MACD图同步完成');
+ } catch (e) {
+ console.error('❌ MACD图同步失败:', e);
+ }
+ }
+
+ // 保存当前的可见范围到全局状态
+ if (tvWidget && tvWidget.state) {
+ tvWidget.state.logicalRange = logicalRange;
+ }
+ } else {
+ console.warn('⚠️ 无效的逻辑范围:', logicalRange);
+ }
+ } else {
+ console.warn('⚠️ 无效的源图表或时间刻度');
+ }
+ } catch (e) {
+ console.error('💥 同步图表出错:', e);
+ }
+
+ // 立即重置同步标志,提高响应速度
+ setTimeout(() => {
+ syncInProgress = false;
+ console.log('🔓 同步标志已重置');
+ }, 1);
}
+ // 用于跟踪所有图表的拖动状态
+ let localDragStates = {
+ main: false,
+ volume: false,
+ macd: false
+ };
+
+ // 全局鼠标抬起事件(只添加一次)
+ document.addEventListener('mouseup', () => {
+ // 重置所有拖动状态
+ Object.keys(localDragStates).forEach(key => {
+ if (localDragStates[key]) {
+ console.log(`全局鼠标抬起,重置${key}图表拖动状态`);
+ localDragStates[key] = false;
+ }
+ });
+ });
+
// 为每个图表添加事件监听
const addChartSyncEvents = (chartContainer, chart) => {
- // 鼠标滚轮事件
- chartContainer.addEventListener('wheel', () => {
- syncCharts(chart, chartContainer);
- });
+ console.log('为图表添加同步事件监听:', chart === mainChart ? '主图' : chart === volumeChart ? '成交量图' : 'MACD图');
- // 拖动事件
- let isDragging = false;
- chartContainer.addEventListener('mousedown', () => { isDragging = true; });
- chartContainer.addEventListener('mousemove', () => {
- if (isDragging) {
+ // 确定当前图表类型
+ const chartType = chart === mainChart ? 'main' : chart === volumeChart ? 'volume' : 'macd';
+
+ // 使用LightweightCharts内置的时间范围变化事件(这是最可靠的方法)
+ chart.timeScale().subscribeVisibleTimeRangeChange(() => {
+ // 使用图表特定的同步标志防止递归
+ if (!syncInProgress) {
+ console.log('✅ 检测到时间范围变化,触发同步:', chartType, '当前范围:', chart.timeScale().getVisibleLogicalRange());
syncCharts(chart, chartContainer);
+ } else {
+ console.log('⏸️ 同步进行中,跳过时间范围变化事件:', chartType);
}
});
- chartContainer.addEventListener('mouseup', () => { isDragging = false; });
- chartContainer.addEventListener('mouseleave', () => { isDragging = false; });
- // 触摸事件(移动设备)
- chartContainer.addEventListener('touchmove', () => {
- syncCharts(chart, chartContainer);
+ // 备用的DOM事件监听(用于调试和额外保障)
+ let isScrolling = false;
+
+ // 鼠标按下事件
+ chartContainer.addEventListener('mousedown', (e) => {
+ localDragStates[chartType] = true;
+ console.log('鼠标按下开始拖动:', chartType);
+ });
+
+ // 鼠标抬起事件
+ chartContainer.addEventListener('mouseup', (e) => {
+ if (localDragStates[chartType]) {
+ localDragStates[chartType] = false;
+ console.log('鼠标抬起,结束拖动:', chartType);
+ }
+ });
+
+ // 鼠标离开事件
+ chartContainer.addEventListener('mouseleave', (e) => {
+ if (localDragStates[chartType]) {
+ localDragStates[chartType] = false;
+ console.log('鼠标离开容器,结束拖动:', chartType);
+ }
+ });
+
+ // 滚轮缩放事件(保持原有逻辑)
+ chartContainer.addEventListener('wheel', (e) => {
+ if (!isScrolling) {
+ isScrolling = true;
+ console.log('滚轮缩放:', chartType);
+ setTimeout(() => {
+ if (!syncInProgress) {
+ syncCharts(chart, chartContainer);
+ }
+ isScrolling = false;
+ }, 50);
+ }
});
};
@@ -1803,11 +1832,6 @@
addChartSyncEvents(macdChartContainer, macdChart);
}
- // 全局鼠标抬起事件
- document.addEventListener('mouseup', () => {
- isDragging = false;
- });
-
// 窗口大小变化时重绘图表
window.addEventListener('resize', () => {
// 调整主图大小
@@ -1823,20 +1847,21 @@
});
// 调整MACD图大小
- if (showMacd && macdChart) {
+ if (showMacd && macdChart && macdChartContainer) {
macdChart.applyOptions({
width: macdChartContainer.clientWidth,
height: macdChartContainer.clientHeight
});
}
- // 重新同步
- setTimeout(() => syncCharts(mainChart, null), 100);
+ // 重新同步 - 使用主图作为同步源
+ setTimeout(() => {
+ if (mainChart) {
+ syncCharts(mainChart, mainChartContainer);
+ }
+ }, 200);
});
- // ------------------------------
- // 分形元素(笔、线段、中枢)绘制
- // ------------------------------
// 显示笔的绘制 - 分别处理主周期和次周期
if ($('#showMainBi').is(':checked') || $('#showElementBi').is(':checked')) {
console.log('绘制笔 - 已启用');
@@ -2817,6 +2842,74 @@
// 添加鼠标悬停事件显示提示
mainChart.subscribeCrosshairMove(param => {
+ // 十字线同步到其他图表 - 通过DOM元素绘制垂直线实现虚线延长效果
+ if (param.time && param.point && volumeChart) {
+ try {
+ // 清除之前的十字线标记
+ const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
+ existingVolumeLines.forEach(line => line.remove());
+ const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
+ existingMacdLines.forEach(line => line.remove());
+
+ // 获取时间对应的坐标位置
+ const mainTimeCoordinate = mainChart.timeScale().timeToCoordinate(param.time);
+ if (mainTimeCoordinate !== null) {
+ // 获取主图容器的位置
+ const mainChartRect = mainChartContainer.getBoundingClientRect();
+
+ // 在交易量图上绘制垂直线
+ const volumeTimeCoordinate = volumeChart.timeScale().timeToCoordinate(param.time);
+ if (volumeTimeCoordinate !== null) {
+ const volumeChartRect = volumeChartContainer.getBoundingClientRect();
+ const volumeLine = document.createElement('div');
+ volumeLine.className = 'volume-crosshair-line';
+ volumeLine.style.position = 'absolute';
+ volumeLine.style.left = (volumeChartRect.left + volumeTimeCoordinate) + 'px';
+ volumeLine.style.top = volumeChartRect.top + 'px';
+ volumeLine.style.width = '1px';
+ volumeLine.style.height = volumeChartRect.height + 'px';
+ volumeLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
+ volumeLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)';
+ volumeLine.style.pointerEvents = 'none';
+ volumeLine.style.zIndex = '1000';
+ document.body.appendChild(volumeLine);
+ }
+
+ // 如果有MACD图,也在MACD图上绘制垂直线
+ if (showMacd && macdChart && macdChartContainer) {
+ const macdTimeCoordinate = macdChart.timeScale().timeToCoordinate(param.time);
+ if (macdTimeCoordinate !== null) {
+ const macdChartRect = macdChartContainer.getBoundingClientRect();
+ const macdLine = document.createElement('div');
+ macdLine.className = 'macd-crosshair-line';
+ macdLine.style.position = 'absolute';
+ macdLine.style.left = (macdChartRect.left + macdTimeCoordinate) + 'px';
+ macdLine.style.top = macdChartRect.top + 'px';
+ macdLine.style.width = '1px';
+ macdLine.style.height = macdChartRect.height + 'px';
+ macdLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
+ macdLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)';
+ macdLine.style.pointerEvents = 'none';
+ macdLine.style.zIndex = '1000';
+ document.body.appendChild(macdLine);
+ }
+ }
+ }
+ } catch (e) {
+ console.debug('十字线同步出错:', e);
+ }
+ } else {
+ // 当十字线离开时,清除垂直线
+ try {
+ const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
+ existingVolumeLines.forEach(line => line.remove());
+ const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
+ existingMacdLines.forEach(line => line.remove());
+ } catch (e) {
+ console.debug('清除十字线时出错:', e);
+ }
+ }
+
if (param.time && param.point) {
const timeStr = param.time;
const markers = [...buyMarkers, ...sellMarkers].filter(m => m.time === timeStr);
@@ -2940,7 +3033,7 @@
fx_strength_level: fx.fx_strength_level,
is_strong_fx: fx.is_strong_fx
});
- const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
+ let displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
if (fx.fx_strength < 1.4) {
displayText = ''
}
@@ -3030,13 +3123,12 @@
// 强分型使用更亮的颜色
strengthColor = fx.is_bottom ? '#FF0000' : '#00CED1';
}
-
+ let displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
// 构建小周期分型显示文本
if (fx.fx_strength < 1){
displayText = ''
}
- const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
-
+
// 小周期分型标记配置 - 根据分型类型使用正确的箭头形状
const markerConfig = {
time: timestamp,
@@ -3136,7 +3228,7 @@
}
// 添加买卖点提示
- setupTooltip(mainChart);
+ setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, macdChartContainer, volumeChart, macdChart, showMacd);
// 显示买卖点
if ($('#showTradePoints').is(':checked')) {
@@ -3245,7 +3337,7 @@
}
// 更新MACD数据
- if ($('#showMacd').is(':checked') && currentData.macd && currentData.kline_data && Array.isArray(currentData.kline_data) && tvWidget.series.macdLineSeries) {
+ if (true && currentData.macd && currentData.kline_data && Array.isArray(currentData.kline_data) && tvWidget.series.macdLineSeries) {
// 提取MACD数据
const macdData = [];
const signalData = [];
@@ -3309,58 +3401,139 @@
// 防止同步过程中的无限循环
let syncInProgress = false;
+ // 用于跟踪所有图表的拖动状态 - 在函数内部定义以确保作用域正确
+ let localDragStates = {
+ main: false,
+ volume: false,
+ macd: false
+ };
+
// 同步图表的时间范围
function syncCharts(sourceChart, sourceContainer) {
- // 防止无限循环
- if (syncInProgress) return;
- syncInProgress = true;
-
- try {
- if (sourceChart) {
- const logicalRange = sourceChart.timeScale().getVisibleLogicalRange();
-
- if (logicalRange) {
- if (sourceChart !== mainChart) {
- mainChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
-
- if (sourceChart !== volumeChart) {
- volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
-
- if (showMacd && macdChart && sourceChart !== macdChart) {
- macdChart.timeScale().setVisibleLogicalRange(logicalRange);
- }
- }
- }
- } catch (e) {
- console.error('同步图表出错:', e);
+ // 防止无限循环 - 使用更精确的检查
+ if (syncInProgress) {
+ console.log('🔄 同步正在进行中,跳过此次同步');
+ return;
}
- setTimeout(() => { syncInProgress = false; }, 50);
+ syncInProgress = true;
+ console.log('🚀 开始同步图表,来源:',
+ sourceChart === mainChart ? '主图' :
+ sourceChart === volumeChart ? '成交量图' : 'MACD图');
+
+ try {
+ if (sourceChart && sourceChart.timeScale) {
+ const logicalRange = sourceChart.timeScale().getVisibleLogicalRange();
+
+ if (logicalRange && logicalRange.from !== undefined && logicalRange.to !== undefined) {
+ console.log('📊 同步时间范围:', logicalRange);
+
+ // 同步主图
+ if (sourceChart !== mainChart && mainChart && mainChart.timeScale) {
+ try {
+ mainChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ 主图同步完成');
+ } catch (e) {
+ console.error('❌ 主图同步失败:', e);
+ }
+ }
+
+ // 同步成交量图
+ if (sourceChart !== volumeChart && volumeChart && volumeChart.timeScale) {
+ try {
+ volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ 成交量图同步完成');
+ } catch (e) {
+ console.error('❌ 成交量图同步失败:', e);
+ }
+ }
+
+ // 同步MACD图
+ if (showMacd && macdChart && sourceChart !== macdChart && macdChart.timeScale) {
+ try {
+ macdChart.timeScale().setVisibleLogicalRange(logicalRange);
+ console.log('✅ MACD图同步完成');
+ } catch (e) {
+ console.error('❌ MACD图同步失败:', e);
+ }
+ }
+
+ // 保存当前的可见范围到全局状态
+ if (tvWidget && tvWidget.state) {
+ tvWidget.state.logicalRange = logicalRange;
+ }
+ } else {
+ console.warn('⚠️ 无效的逻辑范围:', logicalRange);
+ }
+ } else {
+ console.warn('⚠️ 无效的源图表或时间刻度');
+ }
+ } catch (e) {
+ console.error('💥 同步图表出错:', e);
+ }
+
+ // 立即重置同步标志,提高响应速度
+ setTimeout(() => {
+ syncInProgress = false;
+ console.log('🔓 同步标志已重置');
+ }, 1);
}
// 为每个图表添加事件监听
const addChartSyncEvents = (chartContainer, chart) => {
- // 鼠标滚轮事件
- chartContainer.addEventListener('wheel', () => {
- syncCharts(chart, chartContainer);
- });
+ console.log('为图表添加同步事件监听:', chart === mainChart ? '主图' : chart === volumeChart ? '成交量图' : 'MACD图');
- // 拖动事件
- let isDragging = false;
- chartContainer.addEventListener('mousedown', () => { isDragging = true; });
- chartContainer.addEventListener('mousemove', () => {
- if (isDragging) {
+ // 确定当前图表类型
+ const chartType = chart === mainChart ? 'main' : chart === volumeChart ? 'volume' : 'macd';
+
+ // 使用LightweightCharts内置的时间范围变化事件(这是最可靠的方法)
+ chart.timeScale().subscribeVisibleTimeRangeChange(() => {
+ // 使用图表特定的同步标志防止递归
+ if (!syncInProgress) {
+ console.log('✅ 检测到时间范围变化,触发同步:', chartType, '当前范围:', chart.timeScale().getVisibleLogicalRange());
syncCharts(chart, chartContainer);
+ } else {
+ console.log('⏸️ 同步进行中,跳过时间范围变化事件:', chartType);
}
});
- chartContainer.addEventListener('mouseup', () => { isDragging = false; });
- chartContainer.addEventListener('mouseleave', () => { isDragging = false; });
- // 触摸事件(移动设备)
- chartContainer.addEventListener('touchmove', () => {
- syncCharts(chart, chartContainer);
+ // 备用的DOM事件监听(用于调试和额外保障)
+ let isScrolling = false;
+
+ // 鼠标按下事件
+ chartContainer.addEventListener('mousedown', (e) => {
+ localDragStates[chartType] = true;
+ console.log('鼠标按下开始拖动:', chartType);
+ });
+
+ // 鼠标抬起事件
+ chartContainer.addEventListener('mouseup', (e) => {
+ if (localDragStates[chartType]) {
+ localDragStates[chartType] = false;
+ console.log('鼠标抬起,结束拖动:', chartType);
+ }
+ });
+
+ // 鼠标离开事件
+ chartContainer.addEventListener('mouseleave', (e) => {
+ if (localDragStates[chartType]) {
+ localDragStates[chartType] = false;
+ console.log('鼠标离开容器,结束拖动:', chartType);
+ }
+ });
+
+ // 滚轮缩放事件(保持原有逻辑)
+ chartContainer.addEventListener('wheel', (e) => {
+ if (!isScrolling) {
+ isScrolling = true;
+ console.log('滚轮缩放:', chartType);
+ setTimeout(() => {
+ if (!syncInProgress) {
+ syncCharts(chart, chartContainer);
+ }
+ isScrolling = false;
+ }, 50);
+ }
});
};
@@ -3375,11 +3548,6 @@
addChartSyncEvents(macdChartContainer, macdChart);
}
- // 全局鼠标抬起事件
- document.addEventListener('mouseup', () => {
- isDragging = false;
- });
-
// 窗口大小变化时重绘图表
window.addEventListener('resize', () => {
// 调整主图大小
@@ -3406,12 +3574,16 @@
});
}
- // 重新同步
- setTimeout(() => syncCharts(mainChart, null), 100);
+ // 重新同步 - 使用主图作为同步源
+ setTimeout(() => {
+ if (mainChart) {
+ syncCharts(mainChart, mainChartContainer);
+ }
+ }, 200);
});
}
- function setupTooltip(mainChart, buyMarkers = [], sellMarkers = []) {
+ function setupTooltip(mainChart, buyMarkers = [], sellMarkers = [], mainChartContainer, volumeChartContainer, macdChartContainer, volumeChart, macdChart, showMacd) {
// 调试变量
window.debugMode = true;
@@ -3437,6 +3609,74 @@
// 添加鼠标悬停事件显示提示
if (mainChart) {
mainChart.subscribeCrosshairMove(param => {
+ // 十字线同步到其他图表 - 通过DOM元素绘制垂直线实现虚线延长效果
+ if (param.time && param.point && volumeChart) {
+ try {
+ // 清除之前的十字线标记
+ const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
+ existingVolumeLines.forEach(line => line.remove());
+ const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
+ existingMacdLines.forEach(line => line.remove());
+
+ // 获取时间对应的坐标位置
+ const mainTimeCoordinate = mainChart.timeScale().timeToCoordinate(param.time);
+ if (mainTimeCoordinate !== null) {
+ // 获取主图容器的位置
+ const mainChartRect = mainChartContainer.getBoundingClientRect();
+
+ // 在交易量图上绘制垂直线
+ const volumeTimeCoordinate = volumeChart.timeScale().timeToCoordinate(param.time);
+ if (volumeTimeCoordinate !== null) {
+ const volumeChartRect = volumeChartContainer.getBoundingClientRect();
+ const volumeLine = document.createElement('div');
+ volumeLine.className = 'volume-crosshair-line';
+ volumeLine.style.position = 'absolute';
+ volumeLine.style.left = (volumeChartRect.left + volumeTimeCoordinate) + 'px';
+ volumeLine.style.top = volumeChartRect.top + 'px';
+ volumeLine.style.width = '1px';
+ volumeLine.style.height = volumeChartRect.height + 'px';
+ volumeLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
+ volumeLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)';
+ volumeLine.style.pointerEvents = 'none';
+ volumeLine.style.zIndex = '1000';
+ document.body.appendChild(volumeLine);
+ }
+
+ // 如果有MACD图,也在MACD图上绘制垂直线
+ if (showMacd && macdChart && macdChartContainer) {
+ const macdTimeCoordinate = macdChart.timeScale().timeToCoordinate(param.time);
+ if (macdTimeCoordinate !== null) {
+ const macdChartRect = macdChartContainer.getBoundingClientRect();
+ const macdLine = document.createElement('div');
+ macdLine.className = 'macd-crosshair-line';
+ macdLine.style.position = 'absolute';
+ macdLine.style.left = (macdChartRect.left + macdTimeCoordinate) + 'px';
+ macdLine.style.top = macdChartRect.top + 'px';
+ macdLine.style.width = '1px';
+ macdLine.style.height = macdChartRect.height + 'px';
+ macdLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)';
+ macdLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)';
+ macdLine.style.pointerEvents = 'none';
+ macdLine.style.zIndex = '1000';
+ document.body.appendChild(macdLine);
+ }
+ }
+ }
+ } catch (e) {
+ console.debug('十字线同步出错:', e);
+ }
+ } else {
+ // 当十字线离开时,清除垂直线
+ try {
+ const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
+ existingVolumeLines.forEach(line => line.remove());
+ const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
+ existingMacdLines.forEach(line => line.remove());
+ } catch (e) {
+ console.debug('清除十字线时出错:', e);
+ }
+ }
+
if (param.time && param.point) {
const timeStr = param.time;
const markers = [...buyMarkers, ...sellMarkers].filter(m => m.time === timeStr);
@@ -4143,10 +4383,10 @@
// 初始化股票筛选表格
initStockFilterTable();
- // 突出显示股票筛选tab
+ // 突出显示股票筛选tab (已禁用)
setTimeout(function() {
const stockFilterTab = $('#stock-filter-tab');
- if (stockFilterTab.length > 0) {
+ if (false && stockFilterTab.length > 0) {
console.log('股票筛选tab已找到,开始突出显示');
// 添加闪烁效果来吸引注意
@@ -4160,7 +4400,7 @@
// 添加提示信息
const alertDiv = $(`
- 新功能! 股票筛选功能已添加,请查看绿色的"股票筛选"标签页。
+
`);
@@ -4170,10 +4410,8 @@
setTimeout(() => {
alertDiv.alert('close');
}, 10000);
-
- } else {
- console.error('未找到股票筛选tab');
}
+ // 已禁用突出显示功能,无需错误日志
}, 2000);
});
});
@@ -4349,7 +4587,7 @@
const logicalRange = mainChart.timeScale().getVisibleLogicalRange();
// 获取当前图表设置
- const showMacd = $('#showMacd').is(':checked');
+ const showMacd = true;
const showOriginalKline = $('#showOriginalKline').is(':checked');
const showBi = $('#showMainBi').is(':checked');
const showSeg = $('#showMainSeg').is(':checked');
@@ -4534,11 +4772,6 @@
}
}
- // 绑定MACD显示开关
- $('#showMacd').change(function() {
- updateChart();
- });
-
// 绑定主周期MACD背离显示开关
$('#showMainMacdDiv').change(function() {
refreshChartOnly();
@@ -4551,7 +4784,7 @@
// 绑定分型类型显示开关
$('#showKlcFxType').change(function() {
- updateChart();
+ refreshChartOnly();
});
// 绑定小周期分型显示开关
@@ -4576,8 +4809,8 @@
'showMainSeg': $('#showMainSeg').is(':checked'),
'showMainZs': $('#showMainZs').is(':checked'),
'showMainUncompletedZs': $('#showMainUncompletedZs').is(':checked'),
- 'showVolume': $('#showVolume').is(':checked'),
- 'showMacd': $('#showMacd').is(':checked'),
+ 'showVolume': false,
+ 'showMacd': true,
'showKlcFxType': $('#showKlcFxType').is(':checked'),
'showElementKlcFxType': $('#showElementKlcFxType').is(':checked'),
'showTradePoints': $('#showTradePoints').is(':checked'),
@@ -5520,12 +5753,15 @@
// 初始化股票筛选表格
initStockFilterTable();
- // 突出显示股票筛选tab
+ // 突出显示股票筛选tab (已禁用)
setTimeout(function() {
const stockFilterTab = $('#stock-filter-tab');
- if (stockFilterTab.length > 0) {
+ if (false && stockFilterTab.length > 0) {
console.log('股票筛选tab已找到,开始突出显示');
+ // 添加闪烁效果来吸引注意
+ stockFilterTab.addClass('animate__animated animate__pulse');
+
// 滚动到tab区域
$('html, body').animate({
scrollTop: $('.data-container').offset().top - 100
@@ -5534,7 +5770,7 @@
// 添加提示信息
const alertDiv = $(`
- 新功能! 股票筛选功能已添加,请查看绿色的"股票筛选"标签页。
+
`);
@@ -5544,10 +5780,8 @@
setTimeout(() => {
alertDiv.alert('close');
}, 10000);
-
- } else {
- console.error('未找到股票筛选tab');
}
+ // 已禁用突出显示功能,无需错误日志
}, 2000);
});