|
|
@ -12,7 +12,7 @@ let qxnlzhqEchartsRef = ref(null); |
|
|
|
let qxnlzhqEchartsInstance = null; |
|
|
|
|
|
|
|
let regions = reactive([]); |
|
|
|
const dataMax=ref(null) |
|
|
|
const dataMax = ref(null) |
|
|
|
// 设置区域名称 位置 |
|
|
|
function getNameTop(min, max, regionMiidle) { |
|
|
|
// 获取整个图表的高度 |
|
|
@ -32,9 +32,9 @@ function getNumberTop(min, max, regionMax) { |
|
|
|
const generateGraphics = (min, max) => { |
|
|
|
let hasPartialVisible = false; // 标记是否已经遇到第一个部分可见的区域 |
|
|
|
return regions.flatMap((region) => { |
|
|
|
if(!region.min || !region.max) return []; |
|
|
|
if (!region.min || !region.max) return []; |
|
|
|
const middleY = (Number(region.min) + Number(region.max)) / 2; |
|
|
|
const safeY = Math.max(min, Math.min(middleY, max*0.99)); |
|
|
|
const safeY = Math.max(min, Math.min(middleY, max * 0.99)); |
|
|
|
// 检查区域是否完全可见 |
|
|
|
const isFullyVisible = region.min >= min && region.max <= max; |
|
|
|
// 检查区域是否部分可见 |
|
|
@ -266,12 +266,47 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
tooltip: { |
|
|
|
show: true, |
|
|
|
trigger: 'axis', |
|
|
|
confine: true, // 限制tooltip在图表区域内 |
|
|
|
position: function (point, params, dom, rect, size) { |
|
|
|
// 获取图表容器大小 |
|
|
|
const chartWidth = size.viewSize[0]; |
|
|
|
const chartHeight = size.viewSize[1]; |
|
|
|
const tooltipWidth = size.contentSize[0]; |
|
|
|
const tooltipHeight = size.contentSize[1]; |
|
|
|
|
|
|
|
// 检测是否为移动设备 |
|
|
|
const isMobile = window.innerWidth <= 768; |
|
|
|
|
|
|
|
if (isMobile) { |
|
|
|
// 移动端:固定在顶部中央 |
|
|
|
return { |
|
|
|
top: 10, |
|
|
|
left: Math.max(10, (chartWidth - tooltipWidth) / 2) |
|
|
|
}; |
|
|
|
} else { |
|
|
|
// 桌面端:智能定位 |
|
|
|
let x = point[0]; |
|
|
|
let y = point[1]; |
|
|
|
|
|
|
|
// 防止tooltip超出右边界 |
|
|
|
if (x + tooltipWidth > chartWidth) { |
|
|
|
x = chartWidth - tooltipWidth - 10; |
|
|
|
} |
|
|
|
|
|
|
|
// 防止tooltip超出下边界 |
|
|
|
if (y + tooltipHeight > chartHeight) { |
|
|
|
y = chartHeight - tooltipHeight - 10; |
|
|
|
} |
|
|
|
|
|
|
|
return [Math.max(10, x), Math.max(10, y)]; |
|
|
|
} |
|
|
|
}, |
|
|
|
axisPointer: { |
|
|
|
type: 'line', |
|
|
|
type: 'cross', |
|
|
|
lineStyle: { |
|
|
|
color: '#fff', |
|
|
|
color: 'grey', |
|
|
|
width: 1, |
|
|
|
type: 'solid' |
|
|
|
type: 'dashed' |
|
|
|
}, |
|
|
|
label: { |
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.8)', |
|
|
@ -283,15 +318,23 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
backgroundColor: '#646E71', |
|
|
|
borderColor: '#fff', |
|
|
|
borderWidth: 1, |
|
|
|
padding: 10, |
|
|
|
padding: [8, 12], |
|
|
|
textStyle: { |
|
|
|
color: '#fff', |
|
|
|
fontSize: 12 |
|
|
|
fontSize: window.innerWidth <= 768 ? 10 : 12 // 移动端使用更小字体 |
|
|
|
}, |
|
|
|
extraCssText: window.innerWidth <= 768 ? |
|
|
|
'max-width: 280px; word-wrap: break-word; white-space: normal; box-shadow: 0 2px 8px rgba(0,0,0,0.3);' : |
|
|
|
'max-width: 350px; word-wrap: break-word; white-space: normal; box-shadow: 0 2px 8px rgba(0,0,0,0.3);', |
|
|
|
formatter: function (params) { |
|
|
|
if (!params || params.length === 0) return '' |
|
|
|
|
|
|
|
let result = `<div style="font-weight: bold; color: #fff; margin-bottom: 8px;">${params[0].name}</div>` |
|
|
|
|
|
|
|
const isMobile = window.innerWidth <= 768; |
|
|
|
const fontSize = isMobile ? '10px' : '12px'; |
|
|
|
const lineHeight = isMobile ? '1.3' : '1.5'; |
|
|
|
const marginBottom = isMobile ? '4px' : '6px'; |
|
|
|
|
|
|
|
let result = `<div style="font-weight: bold; color: #fff; margin-bottom: ${marginBottom}; font-size: ${fontSize}; line-height: ${lineHeight};">${params[0].name}</div>` |
|
|
|
|
|
|
|
params.forEach(param => { |
|
|
|
let value = param.value |
|
|
@ -302,29 +345,40 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
let closePrice = value[2] // 收盘价 |
|
|
|
let lowPrice = value[3] // 最低价 |
|
|
|
let highPrice = value[4] // 最高价 |
|
|
|
|
|
|
|
|
|
|
|
// 检查数据有效性 |
|
|
|
if (typeof openPrice !== 'number' || typeof closePrice !== 'number' || |
|
|
|
typeof lowPrice !== 'number' || typeof highPrice !== 'number') { |
|
|
|
if (typeof openPrice !== 'number' || typeof closePrice !== 'number' || |
|
|
|
typeof lowPrice !== 'number' || typeof highPrice !== 'number') { |
|
|
|
return '' |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let priceChange = closePrice - openPrice |
|
|
|
let changePercent = ((priceChange / openPrice) * 100).toFixed(2) |
|
|
|
let changeColor = priceChange >= 0 ? '#14b143' : '#ef232a' |
|
|
|
|
|
|
|
result += `<div style="margin-bottom: 6px;">` |
|
|
|
// result += `<div style="color: #fff; font-weight: bold;">${param.seriesName}</div>` |
|
|
|
result += `<div style="color: #fff;">开盘: ${openPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">收盘: ${closePrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">最低: ${lowPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">最高: ${highPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: ${changeColor};">涨跌: ${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)} (${changePercent}%)</div>` |
|
|
|
result += `</div>` |
|
|
|
|
|
|
|
if (isMobile) { |
|
|
|
// 移动端简化显示 |
|
|
|
result += `<div style="margin-bottom: ${marginBottom}; font-size: ${fontSize}; line-height: ${lineHeight};">` |
|
|
|
result += `<div style="color: #fff; display: flex; justify-content: space-between;"><span>开盘价:</span><span>${openPrice.toFixed(2)}</span></div>` |
|
|
|
result += `<div style="color: #fff; display: flex; justify-content: space-between;"><span>收盘价:</span><span>${closePrice.toFixed(2)}</span></div>` |
|
|
|
result += `<div style="color: #fff; display: flex; justify-content: space-between;"><span>最低价:</span><span>${lowPrice.toFixed(2)}</span></div>` |
|
|
|
result += `<div style="color: #fff; display: flex; justify-content: space-between;"><span>最高价:</span><span>${highPrice.toFixed(2)}</span></div>` |
|
|
|
result += `<div style="color: ${changeColor}; display: flex; justify-content: space-between;"><span>涨跌:</span><span>${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)} (${changePercent}%)</span></div>` |
|
|
|
result += `</div>` |
|
|
|
} else { |
|
|
|
// 桌面端完整显示 |
|
|
|
result += `<div style="margin-bottom: ${marginBottom}; font-size: ${fontSize}; line-height: ${lineHeight};">` |
|
|
|
result += `<div style="color: #fff;">开盘价: ${openPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">收盘价: ${closePrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">最低价: ${lowPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #fff;">最高价: ${highPrice.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: ${changeColor};">涨跌: ${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)} (${changePercent}%)</div>` |
|
|
|
result += `</div>` |
|
|
|
} |
|
|
|
} else if (param.seriesName === '止盈线' && value !== null && value !== undefined && typeof value === 'number') { |
|
|
|
result += `<div style="color: #FF0000; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #FF0000; margin-bottom: 2px; font-size: ${fontSize}; line-height: ${lineHeight};">${isMobile ? '止盈' : param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
} else if (param.seriesName === '止损线' && value !== null && value !== undefined && typeof value === 'number') { |
|
|
|
result += `<div style="color: #001EFF; margin-bottom: 4px;">${param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
result += `<div style="color: #001EFF; margin-bottom: 2px; font-size: ${fontSize}; line-height: ${lineHeight};">${isMobile ? '止损' : param.seriesName}: ${value.toFixed(2)}</div>` |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
@ -369,37 +423,37 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
interval: 'auto' // 自动计算显示间隔,只显示部分日期但覆盖所有范围 |
|
|
|
}, |
|
|
|
axisLine: { |
|
|
|
// show: false, |
|
|
|
lineStyle: { |
|
|
|
color: 'white', // x轴线颜色 |
|
|
|
} |
|
|
|
}, |
|
|
|
// show: false, |
|
|
|
lineStyle: { |
|
|
|
color: 'white', // x轴线颜色 |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
yAxis: { |
|
|
|
scale: true, |
|
|
|
axisLine: { |
|
|
|
// show: false, |
|
|
|
lineStyle: { |
|
|
|
color: 'white', // x轴线颜色 |
|
|
|
width:3 |
|
|
|
} |
|
|
|
}, |
|
|
|
// show: false, |
|
|
|
lineStyle: { |
|
|
|
color: 'white', // y轴线颜色 |
|
|
|
width: 3 |
|
|
|
} |
|
|
|
}, |
|
|
|
splitLine: { |
|
|
|
show: false, |
|
|
|
}, |
|
|
|
axisLabel: { // 刻度标签 |
|
|
|
show: true, |
|
|
|
color:'white', |
|
|
|
color: 'white', |
|
|
|
}, |
|
|
|
axisTick: { // 刻度线 |
|
|
|
show: true, |
|
|
|
color:'white', |
|
|
|
color: 'white', |
|
|
|
}, |
|
|
|
min: |
|
|
|
qxnlzhqData.dd < stopLossPrice * 0.98 |
|
|
|
? Math.floor(qxnlzhqData.dd) |
|
|
|
: Math.floor(stopLossPrice * 0.98), |
|
|
|
max: Math.max(Math.ceil(dataMax * 1.02), (qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02)), stopProfitPrice * 1.02), |
|
|
|
max: Math.round(Math.max(Math.ceil(dataMax * 1.02), (qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02)), stopProfitPrice * 1.02)), |
|
|
|
}, |
|
|
|
// 自定义区域名称和区域范围值 位置 |
|
|
|
graphic: generateGraphics(qxnlzhqData.dd < stopLossPrice * 0.98 |
|
|
@ -571,18 +625,18 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
label: { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'top', |
|
|
|
formatter: `{text|${mixData[mixData.length - 1].value[2].toFixed(2)}}`, |
|
|
|
position: 'bottom', |
|
|
|
formatter: `{text| ${mixData[mixData.length - 1].value[2].toFixed(2)}}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#001EFF', |
|
|
|
fontSize: 12, |
|
|
|
fontWeight: 'bold', |
|
|
|
textBorderColor: '#ffffff', |
|
|
|
textBorderWidth: 2, |
|
|
|
textBorderWidth: 2, |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [0, 10] |
|
|
|
offset: [0, -30] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -611,18 +665,18 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
label: { |
|
|
|
normal: { |
|
|
|
show: true, |
|
|
|
position: 'bottom', |
|
|
|
formatter: `{text|${mixData[mixData.length - 1].value[3].toFixed(2)}}`, |
|
|
|
position: 'top', |
|
|
|
formatter: `{text| ${mixData[mixData.length - 1].value[3].toFixed(2)}}`, |
|
|
|
rich: { |
|
|
|
text: { |
|
|
|
color: '#FF0000', |
|
|
|
fontSize: 12, |
|
|
|
fontWeight: 'bold', |
|
|
|
textBorderColor: '#ffffff', |
|
|
|
textBorderWidth: 2, |
|
|
|
textBorderWidth: 2, |
|
|
|
} |
|
|
|
}, |
|
|
|
offset: [0, -10] |
|
|
|
offset: [0, 30] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -631,19 +685,19 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
} |
|
|
|
], |
|
|
|
grid: { |
|
|
|
left: "10%", |
|
|
|
left: "13%", |
|
|
|
right: "10", |
|
|
|
top: '10', |
|
|
|
bottom: "60", |
|
|
|
containLabel: false, |
|
|
|
width: '85%', |
|
|
|
width: '80%', |
|
|
|
height: 'auto', |
|
|
|
overflow: 'hidden' |
|
|
|
}, |
|
|
|
}; |
|
|
|
// 应用配置 |
|
|
|
qxnlzhqEchartsInstance.setOption(option); |
|
|
|
|
|
|
|
|
|
|
|
// 防抖函数,避免频繁触发resize |
|
|
|
const debounce = (func, wait) => { |
|
|
|
let timeout; |
|
|
@ -656,7 +710,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
timeout = setTimeout(later, wait); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 监听窗口大小变化,调整图表尺寸 |
|
|
|
const resizeHandler = debounce(() => { |
|
|
|
if (qxnlzhqEchartsInstance && !qxnlzhqEchartsInstance.isDisposed()) { |
|
|
@ -668,18 +722,18 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
} |
|
|
|
} |
|
|
|
}, 100); // 100ms防抖延迟 |
|
|
|
|
|
|
|
|
|
|
|
// 移除之前的监听器(如果存在) |
|
|
|
if (window.emoEnergyConverterResizeHandler) { |
|
|
|
window.removeEventListener('resize', window.emoEnergyConverterResizeHandler); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 添加新的监听器 |
|
|
|
window.addEventListener('resize', resizeHandler); |
|
|
|
|
|
|
|
|
|
|
|
// 存储resize处理器以便后续清理 |
|
|
|
window.emoEnergyConverterResizeHandler = resizeHandler; |
|
|
|
|
|
|
|
|
|
|
|
// 添加容器大小监听器 |
|
|
|
if (qxnlzhqEchartsRef.value && window.ResizeObserver) { |
|
|
|
const containerObserver = new ResizeObserver(debounce(() => { |
|
|
@ -692,7 +746,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { |
|
|
|
} |
|
|
|
} |
|
|
|
}, 100)); |
|
|
|
|
|
|
|
|
|
|
|
containerObserver.observe(qxnlzhqEchartsRef.value); |
|
|
|
window.emoEnergyConverterContainerObserver = containerObserver; |
|
|
|
} |
|
|
@ -704,13 +758,13 @@ onBeforeUnmount(() => { |
|
|
|
qxnlzhqEchartsInstance.dispose(); |
|
|
|
qxnlzhqEchartsInstance = null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 移除窗口resize监听器 |
|
|
|
if (window.emoEnergyConverterResizeHandler) { |
|
|
|
window.removeEventListener('resize', window.emoEnergyConverterResizeHandler); |
|
|
|
window.emoEnergyConverterResizeHandler = null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 清理容器观察器 |
|
|
|
if (window.emoEnergyConverterContainerObserver) { |
|
|
|
window.emoEnergyConverterContainerObserver.disconnect(); |
|
|
@ -732,8 +786,24 @@ onBeforeUnmount(() => { |
|
|
|
#qxnlzhqEcharts { |
|
|
|
width: 100%; |
|
|
|
height: 300px; |
|
|
|
margin: 0; |
|
|
|
/* margin: 0; */ |
|
|
|
} |
|
|
|
|
|
|
|
/* 移动端tooltip优化 */ |
|
|
|
:deep(.echarts-tooltip) { |
|
|
|
max-width: 280px !important; |
|
|
|
font-size: 10px !important; |
|
|
|
line-height: 1.3 !important; |
|
|
|
padding: 8px 10px !important; |
|
|
|
word-wrap: break-word !important; |
|
|
|
white-space: normal !important; |
|
|
|
box-sizing: border-box !important; |
|
|
|
} |
|
|
|
|
|
|
|
/* 确保tooltip不会超出屏幕 */ |
|
|
|
:deep(.echarts-tooltip-content) { |
|
|
|
max-width: 100% !important; |
|
|
|
overflow: hidden !important; |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |