diff --git a/web/templates/index.html b/web/templates/index.html index c3ce2bd..3387496 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -5923,6 +5923,12 @@ 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); @@ -6447,7 +6453,266 @@ }, 200); }); } + function setupTooltip(mainChart, buyMarkers = [], sellMarkers = [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd) { + // 调试变量 + window.debugMode = true; + // 初始化 U 显示状态(主/次周期分开控制) + const isShowUMain = $('#toggleUOnMain').is(':checked'); + const isShowUElement = $('#toggleUOnElement').is(':checked'); + window.showUOnMain = isShowUMain; + window.showUOnElement = isShowUElement; + if (!isShowUMain && !isShowUElement) { + // 隐藏时清空子图上的 U 标记 + if (tvWidget.series && tvWidget.series.chanMacdLineSeries) { + try { tvWidget.series.chanMacdLineSeries.setMarkers([]); } catch (e) {} + } + if (tvWidget.series && tvWidget.series.chanMacdSignalSeries) { + try { tvWidget.series.chanMacdSignalSeries.setMarkers([]); } catch (e) {} + } + } + + // 添加买卖点悬浮提示元素 + const tooltipElement = document.createElement('div'); + tooltipElement.className = 'point-tooltip'; + // document.body.appendChild(tooltipElement); + + // 添加自定义十字线信息显示 + const crosshairTooltip = document.createElement('div'); + crosshairTooltip.className = 'crosshair-tooltip'; + crosshairTooltip.style.position = 'absolute'; + crosshairTooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; + crosshairTooltip.style.color = 'white'; + crosshairTooltip.style.padding = '5px 10px'; + crosshairTooltip.style.borderRadius = '4px'; + crosshairTooltip.style.fontSize = '12px'; + crosshairTooltip.style.zIndex = '1000'; + crosshairTooltip.style.pointerEvents = 'none'; + crosshairTooltip.style.display = 'none'; + // document.body.appendChild(crosshairTooltip); + + // 添加鼠标悬停事件显示提示 + 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 existingAtrLines = document.querySelectorAll('.atr-crosshair-line'); + existingAtrLines.forEach(line => line.remove()); + const existingMacdLines = document.querySelectorAll('.macd-crosshair-line'); + existingMacdLines.forEach(line => line.remove()); + const existingChanMacdLines = document.querySelectorAll('.chanmacd-crosshair-line'); + existingChanMacdLines.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 = 'fixed'; // 改为fixed定位 + 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); + } + + // 在ATR图上绘制垂直线 + if (atrChart && atrChartContainer) { + const atrTimeCoordinate = atrChart.timeScale().timeToCoordinate(param.time); + if (atrTimeCoordinate !== null) { + const atrChartRect = atrChartContainer.getBoundingClientRect(); + const atrLine = document.createElement('div'); + atrLine.className = 'atr-crosshair-line'; + atrLine.style.position = 'fixed'; // 改为fixed定位 + atrLine.style.left = (atrChartRect.left + atrTimeCoordinate) + 'px'; + atrLine.style.top = atrChartRect.top + 'px'; + atrLine.style.width = '1px'; + atrLine.style.height = atrChartRect.height + 'px'; + atrLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)'; + atrLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)'; + atrLine.style.pointerEvents = 'none'; + atrLine.style.zIndex = '1000'; + document.body.appendChild(atrLine); + } + } + + // 如果有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 = 'fixed'; // 改为fixed定位 + 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); + } + } + + // 如果有ChanMACD图,也在ChanMACD图上绘制垂直线 + if (showMacd && chanMacdChart && chanMacdChartContainer) { + const chanMacdTimeCoordinate = chanMacdChart.timeScale().timeToCoordinate(param.time); + if (chanMacdTimeCoordinate !== null) { + const chanMacdChartRect = chanMacdChartContainer.getBoundingClientRect(); + console.log('ChanMACD图表位置(第二个位置):', { + left: chanMacdChartRect.left, + top: chanMacdChartRect.top, + width: chanMacdChartRect.width, + height: chanMacdChartRect.height, + timeCoordinate: chanMacdTimeCoordinate + }); + const chanMacdLine = document.createElement('div'); + chanMacdLine.className = 'chanmacd-crosshair-line'; + chanMacdLine.style.position = 'fixed'; + chanMacdLine.style.left = (chanMacdChartRect.left + chanMacdTimeCoordinate) + 'px'; + chanMacdLine.style.top = chanMacdChartRect.top + 'px'; + chanMacdLine.style.width = '1px'; + chanMacdLine.style.height = chanMacdChartRect.height + 'px'; + chanMacdLine.style.backgroundColor = 'rgba(128, 128, 128, 0.5)'; + chanMacdLine.style.borderLeft = '1px dashed rgba(128, 128, 128, 0.5)'; + chanMacdLine.style.pointerEvents = 'none'; + chanMacdLine.style.zIndex = '1000'; + document.body.appendChild(chanMacdLine); + console.log('ChanMACD垂直线已创建(第二个位置),位置:', chanMacdLine.style.left, chanMacdLine.style.top); + } else { + console.log('ChanMACD时间坐标为空(第二个位置)'); + } + } else { + console.log('ChanMACD图表条件不满足(第二个位置):', { + showMacd: showMacd, + hasChanMacdChart: !!chanMacdChart, + hasChanMacdChartContainer: !!chanMacdChartContainer + }); + } + } + } catch (e) { + console.debug('十字线同步出错:', e); + } + } else { + // 当十字线离开时,清除垂直线 + try { + const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line'); + existingVolumeLines.forEach(line => line.remove()); + const existingAtrLines = document.querySelectorAll('.atr-crosshair-line'); + existingAtrLines.forEach(line => line.remove()); + const existingMacdLines = document.querySelectorAll('.macd-crosshair-line'); + existingMacdLines.forEach(line => line.remove()); + const existingChanMacdLines = document.querySelectorAll('.chanmacd-crosshair-line'); + existingChanMacdLines.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); + + // 同时检查分型标记 + const fxMarkers = (window.fxMarkers || []).filter(m => m.time === timeStr); + const allMarkers = [...markers, ...fxMarkers]; + + // 显示时区调试信息 + if (window.debugMode) { + const timezone = $('#timezone').val(); + const formattedTime = formatTimeWithTimezone(timeStr * 1000, timezone); + + // 获取当前价格 - 通过param.seriesPrices获取 + let priceInfo = ''; + if (param.seriesPrices && param.seriesPrices.size > 0) { + // 依次从当前可能的主系列中获取价格 + if (tvWidget.series.candleSeries && param.seriesPrices.get(tvWidget.series.candleSeries)) { + const price = param.seriesPrices.get(tvWidget.series.candleSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.renkoSeries && param.seriesPrices.get(tvWidget.series.renkoSeries)) { + const price = param.seriesPrices.get(tvWidget.series.renkoSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.heikinSeries && param.seriesPrices.get(tvWidget.series.heikinSeries)) { + const price = param.seriesPrices.get(tvWidget.series.heikinSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.barSeries && param.seriesPrices.get(tvWidget.series.barSeries)) { + const price = param.seriesPrices.get(tvWidget.series.barSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.lineSeries && param.seriesPrices.get(tvWidget.series.lineSeries)) { + const price = param.seriesPrices.get(tvWidget.series.lineSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) { + const price = param.seriesPrices.get(tvWidget.series.areaSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) { + const price = param.seriesPrices.get(tvWidget.series.baselineSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } + // 如果没有蜡烛图系列价格,尝试从区域图系列获取 + else if (tvWidget.series.areaSeries && param.seriesPrices.get(tvWidget.series.areaSeries)) { + const price = param.seriesPrices.get(tvWidget.series.areaSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } + // 如果没有蜡烛图系列价格,尝试从基线图系列获取 + else if (tvWidget.series.baselineSeries && param.seriesPrices.get(tvWidget.series.baselineSeries)) { + const price = param.seriesPrices.get(tvWidget.series.baselineSeries); + priceInfo = `价格: ${price.toFixed(2)}`; + } + } + + // 仅记录最简短的调试信息 + console.debug(`十字线: ${timeStr} -> ${formattedTime} (${timezone})`); + + // 显示自定义时区工具提示,包含价格信息 + crosshairTooltip.innerHTML = `