You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1463 lines
36 KiB
1463 lines
36 KiB
<template>
|
|
<div ref="qxnlzhqEchartsRef" class="qxnlzhqEcharts"></div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, onBeforeUnmount, toRef, reactive } from 'vue'
|
|
import { useLanguage } from '@/utils/languageService'
|
|
import { ElMessage } from 'element-plus'
|
|
import * as echarts from 'echarts'
|
|
// import { text } from 'stream/consumers'
|
|
// import { start } from 'repl'
|
|
const { translate, t } = useLanguage()
|
|
defineExpose({ initQXNLZHEcharts })
|
|
|
|
let qxnlzhqEchartsRef = ref(null)
|
|
let qxnlzhqEchartsInstance = null
|
|
|
|
let regions = reactive([])
|
|
let markLineRegions = reactive([])
|
|
const dataMax = ref(null)
|
|
// 设置区域名称 位置
|
|
function getNameTop(min, max, regionMin, regionMax, regionMiidle) {
|
|
max = Math.min(max, regionMax)
|
|
min = Math.max(min, regionMin)
|
|
|
|
// console.log(
|
|
// 'min',
|
|
// min,
|
|
// 'max',
|
|
// max,
|
|
// 'regionMin',
|
|
// regionMin,
|
|
// 'regionMax',
|
|
// regionMax,
|
|
// 'regionMiidle',
|
|
// regionMiidle
|
|
// )
|
|
|
|
// 获取整个图表的高度
|
|
const chartHeight = qxnlzhqEchartsInstance.getHeight()
|
|
const topHeight = 40
|
|
const bottomHeight = 60
|
|
const dataZoomHeight = 20
|
|
const noHeight = topHeight + bottomHeight + dataZoomHeight
|
|
// console.log('%', ((max - Number(regionMiidle)) / (max - min)) * (chartHeight - noHeight))
|
|
// console.log(chartHeight)
|
|
// 60: 为x轴占的高度,20: 向上偏移量让文字向上移动
|
|
return ((max - Number(regionMiidle)) / (max - min)) * (chartHeight - noHeight)
|
|
// return 2.84
|
|
}
|
|
// 设置区域最大值 位置
|
|
function getNumberTop(min, max, regionMax) {
|
|
// 获取整个图表的高度
|
|
const chartHeight = qxnlzhqEchartsInstance.getHeight()
|
|
// 60: 为x轴占的高度
|
|
return ((max - Number(regionMax)) / (max - min)) * (chartHeight - 60)
|
|
// return 10
|
|
}
|
|
|
|
// 生成图形标注(核心逻辑)
|
|
const generateGraphics = (min, max) => {
|
|
// console.log('regions', regions)
|
|
let regionMin
|
|
let regionMax
|
|
for (let i = 0; i < regions.length; i++) {
|
|
if (i == 0) {
|
|
regionMin = Number(regions[i].min)
|
|
regionMax = Number(regions[i].max)
|
|
} else {
|
|
regionMin = Math.min(regionMin, Number(regions[i].min))
|
|
regionMax = Math.max(regionMax, Number(regions[i].max))
|
|
}
|
|
// console.log('regionMin', regionMin, 'regionMax', regionMax)
|
|
}
|
|
|
|
return regions.flatMap((region) => {
|
|
// console.log(region)
|
|
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, region.min) + Math.min(max, region.max)) / 2
|
|
// 检查区域是否完全可见
|
|
const isFullyVisible = region.min >= min && region.max <= max
|
|
// 检查区域是否部分可见
|
|
const isPartiallyVisible = region.min < max && region.max > min && !isFullyVisible
|
|
|
|
// 计算区域在图表中的实际像素高度
|
|
const chartHeight = qxnlzhqEchartsInstance ? qxnlzhqEchartsInstance.getHeight() : 400
|
|
const visibleRegionMin = Math.max(region.min, min)
|
|
const visibleRegionMax = Math.min(region.max, max)
|
|
const regionPixelHeight =
|
|
((visibleRegionMax - visibleRegionMin) / (max - min)) * (chartHeight - 60)
|
|
|
|
// 设置最小高度阈值,区域太小时不显示名称
|
|
const minHeightThreshold = 5 // 像素
|
|
const shouldShowName = regionPixelHeight >= minHeightThreshold
|
|
|
|
const graphics = []
|
|
// 区域名称(中间位置)- 只有在区域足够大时才显示
|
|
if ((isFullyVisible || isPartiallyVisible) && shouldShowName) {
|
|
graphics.push({
|
|
type: 'text',
|
|
left: region.left,
|
|
right: region.right,
|
|
top:
|
|
window.innerWidth > 769
|
|
? 40 - 6 + getNameTop(min, max, regionMin, regionMax, safeY)
|
|
: 40 - 3 + getNameTop(min, max, regionMin, regionMax, safeY),
|
|
// top: 40,
|
|
style: {
|
|
text: region.name,
|
|
fill: region.fontColor,
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
fontWeight: 'bold'
|
|
},
|
|
z: 2
|
|
})
|
|
}
|
|
// y轴数值(顶部位置)
|
|
// if (isFullyVisible) {
|
|
// // 检测是否为手机端
|
|
// const isMobile = window.matchMedia('(max-width: 768px)').matches
|
|
// graphics.push({
|
|
// type: 'text',
|
|
// left: '5%', // 向右调整位置
|
|
// top: getNumberTop(min, max, region.max),
|
|
// // left: isMobile ? '15%' : '5%', // 手机端调整位置,其他设备保持原位置
|
|
|
|
// // top: getNumberTop(min, max, region.max) + (isMobile ? 0 : 5), // 手机端向下偏移5像素
|
|
// // top: 100,
|
|
// style: {
|
|
// text: region.max.toString(),
|
|
// // fill: isMobile ? '#8f8f98' : region.NumberColor, // 手机端使用灰色,其他设备保持原色
|
|
// fontSize: 12,
|
|
// fill: region.NumberColor
|
|
// // fontWeight: isMobile ? 'bold' : 'normal', // 手机端加粗
|
|
// // textBorderColor: isMobile ? 'rgba(0,0,0,0.5)' : 'transparent',
|
|
// // textBorderWidth: isMobile ? 1 : 0
|
|
// },
|
|
// z: 3
|
|
// })
|
|
// }
|
|
return graphics
|
|
})
|
|
}
|
|
|
|
const klineData = ref()
|
|
const qxnlzhqData = ref()
|
|
|
|
// const fetchData = async () => {
|
|
// const qxnlzhqStore = localStorage.getItem('qxnlzhq')
|
|
// const klineStore = localStorage.getItem('kline20Data')
|
|
|
|
// // 检查是否有缓存数据
|
|
// if (qxnlzhqStore && klineStore) {
|
|
// const qxnlzhqParsed = JSON.parse(qxnlzhqStore)
|
|
// const klineParsed = JSON.parse(klineStore)
|
|
|
|
// // 深拷贝数据
|
|
// qxnlzhqData.value = JSON.parse(JSON.stringify(qxnlzhqParsed))
|
|
// klineData.value = JSON.parse(JSON.stringify(klineParsed))
|
|
// initQXNLZHEcharts(klineData.value, qxnlzhqData.value)
|
|
// } else {
|
|
// ElMessage.error(response.data.msg || '请求失败')
|
|
// }
|
|
// }
|
|
|
|
function initQXNLZHEcharts(kline, qxnlzhqData) {
|
|
// console.log('kline', kline)
|
|
|
|
const textEcahrts = t.value // 创建多语言实例
|
|
|
|
// 测试数据 !!! 删掉
|
|
// qxnlzhqData.topxh = ['2025/07/22', '2025/07/22']
|
|
// qxnlzhqData.lowxh = ['2025/07/02', '2025/07/02']
|
|
// qxnlzhqData.qixh = ['2025/07/08', '2025/07/08']
|
|
|
|
if (!qxnlzhqEchartsRef.value) {
|
|
console.log('DOM 元素未准备好,无法初始化 ECharts')
|
|
return
|
|
}
|
|
if (qxnlzhqEchartsInstance) {
|
|
qxnlzhqEchartsInstance.dispose()
|
|
}
|
|
// 数据
|
|
let mixData = []
|
|
if (!Array.isArray(kline)) {
|
|
console.log('不是')
|
|
}
|
|
kline.forEach((element) => {
|
|
let date = element[0]
|
|
let value = [element[1], element[2], element[3], element[4]]
|
|
mixData.push({
|
|
date,
|
|
value
|
|
})
|
|
})
|
|
|
|
// 动态区域配置
|
|
// dd到zc 低吸区------情绪冰点区 ; zc到ht 关注区------认知潜伏区; ht到qs 回调区------多空消化区 ; qs到tp 拉升区------共识加速区;
|
|
// tp到js 突破区------情绪临界区 ; js到yl 警示区-------杠杆失衡区 ; yl到gg 风险区-------情绪熔断区;
|
|
regions = [
|
|
{
|
|
min: qxnlzhqData.dd,
|
|
max: qxnlzhqData.zc,
|
|
name: '情绪冰点区',
|
|
color: '#F5D6FF',
|
|
fontColor: '#A7691C',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
NumberColor: 'white',
|
|
left: null,
|
|
right: '7%'
|
|
},
|
|
{
|
|
min: qxnlzhqData.zc,
|
|
max: qxnlzhqData.ht,
|
|
name: '认知潜伏区',
|
|
color: '#FFF6C4',
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: null,
|
|
right: '7%'
|
|
},
|
|
{
|
|
min: qxnlzhqData.ht,
|
|
max: qxnlzhqData.qs,
|
|
name: '多空消化区',
|
|
color: {
|
|
type: 'linear',
|
|
x: 0,
|
|
y: 0,
|
|
x2: 1,
|
|
y2: 0,
|
|
colorStops: [
|
|
{ offset: 0, color: '#D7FF9B' },
|
|
{ offset: 1, color: '#CEFF85' }
|
|
]
|
|
},
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: null,
|
|
right: '7%'
|
|
},
|
|
{
|
|
min: qxnlzhqData.qs,
|
|
max: qxnlzhqData.tp,
|
|
name: '共识加速区',
|
|
color: '#FFDC8F',
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: null,
|
|
right: '7%'
|
|
},
|
|
{
|
|
min: qxnlzhqData.tp,
|
|
max: qxnlzhqData.js,
|
|
name: '情绪临界区',
|
|
color: '#FFC0AA',
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: '32%',
|
|
right: null
|
|
}
|
|
]
|
|
// gg yl为-1 不绘制部分图表
|
|
if (Number(qxnlzhqData.yl) != -1) {
|
|
regions.push({
|
|
min: qxnlzhqData.js,
|
|
max: qxnlzhqData.yl,
|
|
name: '杠杆失衡区',
|
|
color: {
|
|
type: 'linear',
|
|
x: 0,
|
|
y: 0,
|
|
x2: 1,
|
|
y2: 0,
|
|
colorStops: [
|
|
{ offset: 0, color: '#FEA474' },
|
|
{ offset: 1, color: '#FFAAF6' }
|
|
]
|
|
},
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: '32%',
|
|
right: null
|
|
})
|
|
}
|
|
if (Number(qxnlzhqData.gg) != -1) {
|
|
regions.push({
|
|
min: qxnlzhqData.yl,
|
|
max: qxnlzhqData.gg,
|
|
name: '情绪熔断区',
|
|
color: {
|
|
type: 'linear',
|
|
x: 0,
|
|
y: 0,
|
|
x2: 1,
|
|
y2: 0,
|
|
colorStops: [
|
|
{ offset: 0, color: '#F66475' },
|
|
{ offset: 1, color: '#FFB98E' }
|
|
]
|
|
},
|
|
fontColor: '#A7691C',
|
|
NumberColor: 'white',
|
|
left: '32%',
|
|
right: null
|
|
})
|
|
}
|
|
|
|
// 计算动态的y轴范围
|
|
const priceValues = kline.flatMap((item) => [item[1], item[2], item[3], item[4]])
|
|
const dataMin = Math.min(...priceValues)
|
|
const dataMax = Math.max(...priceValues)
|
|
// 计算止盈止损价格
|
|
const stopProfitPrice = Number(qxnlzhqData.cc) * 1.05 // 止盈价
|
|
const stopLossPrice = Number(qxnlzhqData.cc) * 0.97 // 止损价
|
|
// 确定起始和结束位置
|
|
const startIndex = Math.max(0, mixData.length - 17)
|
|
// 创建完整数据数组
|
|
const takeProfitData = new Array(mixData.length).fill(null)
|
|
const stopLossData = new Array(mixData.length).fill(null)
|
|
// 填充显示区域的数据
|
|
for (var i = startIndex; i < mixData.length; i++) {
|
|
takeProfitData[i] = stopProfitPrice
|
|
stopLossData[i] = stopLossPrice
|
|
}
|
|
|
|
// topxh、lowxh、qixh 对应k线染色
|
|
// 创建中间区域数据
|
|
const topMiddleRangeData = []
|
|
const topMiddleRangeData1 = []
|
|
const lowMiddleRangeData = []
|
|
const lowMiddleRangeData1 = []
|
|
const markPointData = []
|
|
const qixhData = ref([])
|
|
const topData = ref([])
|
|
const lowData = ref([])
|
|
const maxKlineData = {
|
|
data: {
|
|
value: [0, 0, 0, 0]
|
|
}
|
|
}
|
|
const lastKlineData = {
|
|
data: {
|
|
value: [0, 0, 0, 0]
|
|
}
|
|
}
|
|
|
|
let markLineMax = Math.max(
|
|
Math.ceil(dataMax * 1.02),
|
|
qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02),
|
|
stopProfitPrice * 1.02
|
|
)
|
|
|
|
markLineRegions = regions.filter((region) => {
|
|
return region.max < markLineMax
|
|
})
|
|
|
|
console.log('markLineMax', markLineMax, 'markLineRegions', markLineRegions)
|
|
|
|
mixData.forEach((item, index) => {
|
|
const [open, close, low, high] = item.value
|
|
const rangeHeight = high - low
|
|
const noneItem = { date: item.date, value: [null, null, null, null] }
|
|
// const middleThirdStart = low + rangeHeight * (1/3);
|
|
// const middleThirdEnd = low + rangeHeight * (2/3);
|
|
|
|
let color = null
|
|
|
|
// 获取最高价的K线数据和最后一根K线数据
|
|
if (maxKlineData == null || maxKlineData.data.value[3] < high) {
|
|
maxKlineData.data = item
|
|
maxKlineData.index = index
|
|
}
|
|
lastKlineData.data = item
|
|
lastKlineData.index = index
|
|
|
|
let isTopxh = false
|
|
let isLowxh = false
|
|
|
|
if (qxnlzhqData.topxh.includes(item.date)) {
|
|
topData.value.push({
|
|
date: item.date,
|
|
value: [item.value[0], item.value[1], item.value[0], item.value[1]]
|
|
})
|
|
color = '#000000' // 黑色
|
|
isTopxh = true
|
|
} else {
|
|
topData.value.push(noneItem)
|
|
}
|
|
if (qxnlzhqData.lowxh.includes(item.date)) {
|
|
lowData.value.push({
|
|
date: item.date,
|
|
value: [item.value[0], item.value[1], item.value[0], item.value[1]]
|
|
})
|
|
color = '#001EFF' // 蓝色
|
|
isLowxh = true
|
|
} else {
|
|
lowData.value.push(noneItem)
|
|
}
|
|
if (qxnlzhqData.qixh.includes(item.date)) {
|
|
console.log('item', item)
|
|
qixhData.value.push({
|
|
date: item.date,
|
|
value: [item.value[0], item.value[1], item.value[0], item.value[1]]
|
|
})
|
|
} else {
|
|
qixhData.value.push(noneItem)
|
|
}
|
|
|
|
// 添加TOP中间区域数据
|
|
if (isTopxh) {
|
|
topMiddleRangeData.push({
|
|
value: [index, close > open ? close - open : open - close],
|
|
itemStyle: {
|
|
normal: {
|
|
color: color
|
|
}
|
|
}
|
|
})
|
|
topMiddleRangeData1.push({
|
|
value: [index, close > open ? open : close],
|
|
itemStyle: {
|
|
normal: {
|
|
color: 'transparent'
|
|
}
|
|
}
|
|
})
|
|
lowMiddleRangeData.push(null)
|
|
lowMiddleRangeData1.push(null)
|
|
} else if (isLowxh) {
|
|
lowMiddleRangeData.push({
|
|
value: [index, close > open ? close - open : open - close],
|
|
itemStyle: {
|
|
normal: {
|
|
color: '#001EFF'
|
|
}
|
|
}
|
|
})
|
|
lowMiddleRangeData1.push({
|
|
value: [index, close > open ? open : close],
|
|
itemStyle: {
|
|
normal: {
|
|
color: 'transparent'
|
|
}
|
|
}
|
|
})
|
|
console.log('lowMiddleRangeData', lowMiddleRangeData)
|
|
console.log('lowMiddleRangeData111111', lowMiddleRangeData1)
|
|
|
|
topMiddleRangeData.push(null)
|
|
topMiddleRangeData1.push(null)
|
|
} else {
|
|
topMiddleRangeData.push(null)
|
|
topMiddleRangeData1.push(null)
|
|
lowMiddleRangeData.push(null)
|
|
lowMiddleRangeData1.push(null)
|
|
}
|
|
|
|
// 添加文字标记数据
|
|
// if (qxnlzhqData.qixh.includes(item.date)) {
|
|
// markPointData.push({
|
|
// name: '起',
|
|
// coord: [index, high],
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: 'rgba(0,0,0,0)' // 标记点透明
|
|
// }
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'top',
|
|
// formatter: '起',
|
|
// textStyle: {
|
|
// color: '#249409',
|
|
// fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
// textBorderColor: '#FFFFFF',
|
|
// textBorderWidth: 2,
|
|
// fontWeight: 'bold'
|
|
// }
|
|
// }
|
|
// }
|
|
// })
|
|
// } else if (qxnlzhqData.lowxh.includes(item.date)) {
|
|
// markPointData.push({
|
|
// name: 'LOW',
|
|
// coord: [index, low],
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: 'rgba(0,0,0,0)' // 标记点透明
|
|
// }
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'bottom',
|
|
// formatter: 'LOW',
|
|
// textStyle: {
|
|
// color: '#001EFF',
|
|
// fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
// textBorderColor: '#FFFFFF',
|
|
// textBorderWidth: 2,
|
|
// fontWeight: 'bold'
|
|
// }
|
|
// }
|
|
// }
|
|
// })
|
|
// } else if (qxnlzhqData.topxh.includes(item.date)) {
|
|
// markPointData.push({
|
|
// name: 'TOP',
|
|
// coord: [index, high],
|
|
// itemStyle: {
|
|
// normal: {
|
|
// color: 'rgba(0,0,0,0)' // 标记点透明
|
|
// }
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'top',
|
|
// formatter: 'TOP',
|
|
// textStyle: {
|
|
// color: '#000',
|
|
// fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
// textBorderColor: '#FFFFFF',
|
|
// textBorderWidth: 2,
|
|
// fontWeight: 'bold'
|
|
// }
|
|
// }
|
|
// }
|
|
// })
|
|
// }
|
|
})
|
|
|
|
// console.log('maxKlineData', maxKlineData)
|
|
// console.log('lastKlineData', lastKlineData)
|
|
|
|
markPointData.push({
|
|
name: `${Number(maxKlineData.data.value[3]).toFixed(2)}`,
|
|
coord: [maxKlineData.index, maxKlineData.data.value[3]],
|
|
itemStyle: {
|
|
normal: {
|
|
color: 'rgba(0,0,0,0)' // 标记点透明
|
|
}
|
|
},
|
|
label: {
|
|
normal: {
|
|
show: true,
|
|
position: 'top',
|
|
formatter: `${Number(maxKlineData.data.value[3]).toFixed(2)}`,
|
|
textStyle: {
|
|
color: '#2171DD',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2,
|
|
fontWeight: 'bold'
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
markPointData.push({
|
|
name: `${Number(lastKlineData.data.value[3]).toFixed(2)}`,
|
|
coord: [lastKlineData.index, lastKlineData.data.value[2]],
|
|
itemStyle: {
|
|
normal: {
|
|
color: 'rgba(0,0,0,0)' // 标记点透明
|
|
}
|
|
},
|
|
label: {
|
|
normal: {
|
|
show: true,
|
|
position: 'bottom',
|
|
formatter: `${Number(lastKlineData.data.value[1]).toFixed(2)}`,
|
|
textStyle: {
|
|
color: '#3B8F08',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2,
|
|
fontWeight: 'bold'
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// 创建带有 backgroundSize 的图案
|
|
function createPatternWithSize(gradient, size) {
|
|
const canvas = document.createElement('canvas')
|
|
const ctx = canvas.getContext('2d')
|
|
|
|
// 解析 backgroundSize (例如: '60px 100%')
|
|
const [width, height] = size.split(' ')
|
|
canvas.width = parseInt(width) || 60
|
|
canvas.height = parseInt(height) || 60 // 固定高度或根据需要调整
|
|
|
|
// 创建渐变
|
|
const grad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
|
|
grad.addColorStop(0.24, 'rgba(0,0,0,0)')
|
|
grad.addColorStop(0.25, 'rgba(255,255,255,0.7)')
|
|
grad.addColorStop(0.26, 'rgba(0,0,0,0)')
|
|
grad.addColorStop(0.74, 'rgba(0,0,0,0)')
|
|
grad.addColorStop(0.75, 'rgba(255,255,255,0.7)')
|
|
grad.addColorStop(0.76, 'rgba(0,0,0,0)')
|
|
|
|
ctx.fillStyle = grad
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
return canvas
|
|
}
|
|
|
|
// 定义额外的区域
|
|
const addWhiteRegions = []
|
|
regions.forEach((item) => {
|
|
if (item.name == '情绪熔断区' || item.name == '情绪临界区' || item.name == '情绪冰点区') {
|
|
addWhiteRegions.push({
|
|
name: item.name,
|
|
min: item.min,
|
|
max: item.max,
|
|
backgroundSize: '10px 10px '
|
|
})
|
|
}
|
|
})
|
|
|
|
// 动态生成图例数据
|
|
const legendData = []
|
|
const legendSelected = {}
|
|
|
|
// 检查是否存在TOP数据
|
|
const hasTopData = qxnlzhqData.topxh && qxnlzhqData.topxh.length > 0
|
|
if (hasTopData) {
|
|
legendData.push({
|
|
name: 'TOP',
|
|
textStyle: {
|
|
color: '#000000',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2,
|
|
fontWeight: 'bold'
|
|
}
|
|
})
|
|
legendSelected.TOP = true
|
|
}
|
|
|
|
// 检查是否存在LOW数据
|
|
const hasLowData = qxnlzhqData.lowxh && qxnlzhqData.lowxh.length > 0
|
|
if (hasLowData) {
|
|
legendData.push({
|
|
name: 'LOW',
|
|
textStyle: {
|
|
color: '#001EFF',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2,
|
|
fontWeight: 'bold'
|
|
}
|
|
})
|
|
legendSelected.LOW = true
|
|
}
|
|
|
|
// 检查是否存在起数据
|
|
const hasQixhData = qxnlzhqData.qixh && qxnlzhqData.qixh.length > 0
|
|
if (hasQixhData) {
|
|
legendData.push({
|
|
name: '起',
|
|
textStyle: {
|
|
color: '#249409',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2,
|
|
fontWeight: 'bold'
|
|
}
|
|
})
|
|
legendSelected.起 = true
|
|
}
|
|
|
|
// 初始化图表
|
|
qxnlzhqEchartsInstance = echarts.init(qxnlzhqEchartsRef.value)
|
|
let option
|
|
// 设置图表配置
|
|
option = {
|
|
legend: {
|
|
data: legendData,
|
|
selected: legendSelected,
|
|
top: window.innerWidth > 768 ? '0%' : '1%',
|
|
textStyle: {
|
|
fontSize: window.matchMedia('(max-width: 767px)').matches ? 9 : 12
|
|
}
|
|
},
|
|
tooltip: {
|
|
show: true,
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'cross',
|
|
lineStyle: {
|
|
color: 'grey',
|
|
width: 1,
|
|
type: 'dashed'
|
|
},
|
|
label: {
|
|
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
color: '#fff',
|
|
borderColor: '#fff',
|
|
borderWidth: 1
|
|
}
|
|
},
|
|
backgroundColor: 'rgba(232, 232, 242, 0.87)',
|
|
borderColor: '#fff',
|
|
borderWidth: 1,
|
|
borderRadius: 8,
|
|
padding: 10,
|
|
textStyle: {
|
|
color: '#fff',
|
|
fontSize: 12
|
|
},
|
|
position: function (point, params, dom, rect, size) {
|
|
// 检测是否为手机端
|
|
const isMobile = window.matchMedia('(max-width: 768px)').matches
|
|
const tooltipWidth = size.contentSize[0]
|
|
const tooltipHeight = size.contentSize[1]
|
|
const chartWidth = size.viewSize[0]
|
|
const chartHeight = size.viewSize[1]
|
|
|
|
let x = point[0]
|
|
let y = point[1]
|
|
|
|
if (isMobile) {
|
|
// 手机端:确保tooltip完全在图表内显示
|
|
// 水平位置调整
|
|
if (x + tooltipWidth > chartWidth - 10) {
|
|
x = chartWidth - tooltipWidth - 10
|
|
}
|
|
if (x < 10) {
|
|
x = 10
|
|
}
|
|
|
|
// 垂直位置调整:确保不被遮挡,优先显示在下方
|
|
if (y - tooltipHeight - 20 < 0) {
|
|
// 如果上方空间不足,显示在下方
|
|
y = y + 20
|
|
if (y + tooltipHeight > chartHeight - 60) {
|
|
// 如果下方也不够,显示在中间偏上位置
|
|
y = Math.max(20, chartHeight - tooltipHeight - 60)
|
|
}
|
|
} else {
|
|
// 上方有足够空间,显示在上方
|
|
y = y - tooltipHeight - 20
|
|
}
|
|
} else {
|
|
// 桌面端:保持原有逻辑
|
|
if (x + tooltipWidth > chartWidth - 20) {
|
|
x = x - tooltipWidth - 20
|
|
} else {
|
|
x = x + 20
|
|
}
|
|
|
|
if (y - tooltipHeight < 20) {
|
|
y = y + 20
|
|
} else {
|
|
y = y - tooltipHeight - 20
|
|
}
|
|
}
|
|
|
|
return [x, y]
|
|
},
|
|
formatter: function (params) {
|
|
if (!params || params.length === 0) return ''
|
|
|
|
let result = `<div style="font-weight: bold; color: black; margin-bottom: 8px;">${params[0].name}</div>`
|
|
|
|
params.forEach((param, index) => {
|
|
console.log('参数索引:', index, '系列:', param)
|
|
let value = param.value
|
|
let color = param.color
|
|
|
|
if (param.seriesType === 'candlestick') {
|
|
const openN = textEcahrts.kai
|
|
const closeN = textEcahrts.shou
|
|
const lowN = textEcahrts.di
|
|
const highN = textEcahrts.gao
|
|
// const zhangdie = textEcahrts.zhangdie
|
|
|
|
let openPrice = value[1] // 开盘价
|
|
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'
|
|
) {
|
|
return ''
|
|
}
|
|
|
|
let priceChange
|
|
let changePercent
|
|
if (param.data[0] != 0) {
|
|
// console.log(
|
|
// 'preDayDate',
|
|
// kline[param.data[0] - 1][0],
|
|
// 'preDayClosePrice',
|
|
// kline[param.data[0] - 1][2]
|
|
// )
|
|
// console.log('paramDate', param.name, 'paramClosePrice', closePrice)
|
|
|
|
let preClosePrice = kline[param.data[0] - 1][2] //昨日收盘价;
|
|
priceChange = closePrice - preClosePrice
|
|
changePercent = ((priceChange / preClosePrice) * 100).toFixed(2)
|
|
}
|
|
let changeColor = priceChange >= 0 ? '#32B520' : '#D8001B'
|
|
result += `<div style="margin-bottom: 6px;">`
|
|
// result += `<div style="color: #fff; font-weight: bold;">${param.seriesName}</div>`
|
|
result += `<div style="color: black;">${openN}: ${openPrice.toFixed(2)}</div>`
|
|
result += `<div style="color: black;">${closeN}: ${closePrice.toFixed(2)}</div>`
|
|
result += `<div style="color: black;">${lowN}: ${lowPrice.toFixed(2)}</div>`
|
|
result += `<div style="color: black;">${highN}: ${highPrice.toFixed(2)}</div>`
|
|
if (param.data[0] != 0) {
|
|
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>`
|
|
} 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>`
|
|
}
|
|
})
|
|
|
|
return result
|
|
}
|
|
},
|
|
dataZoom: [
|
|
{
|
|
top: window.innerWidth <= 768 ? '86%' : '',
|
|
type: 'slider',
|
|
xAxisIndex: 0,
|
|
start: 0,
|
|
end: 100,
|
|
show: true,
|
|
bottom: 40,
|
|
height: 20,
|
|
borderColor: '#fff',
|
|
fillerColor: 'rgba(255, 255, 255, 0.2)',
|
|
handleStyle: {
|
|
color: '#fff',
|
|
borderColor: 'white'
|
|
},
|
|
textStyle: {
|
|
color: 'white'
|
|
}
|
|
},
|
|
{
|
|
type: 'inside',
|
|
xAxisIndex: 0,
|
|
start: 0,
|
|
end: 100,
|
|
zoomOnMouseWheel: true,
|
|
moveOnMouseMove: true,
|
|
moveOnMouseWheel: false
|
|
}
|
|
],
|
|
xAxis: {
|
|
type: 'category',
|
|
data: mixData.map((item) => item.date),
|
|
axisLabel: {
|
|
rotate: 0, // 取消倾斜角度
|
|
color: 'white',
|
|
interval: 'auto', // 自动计算显示间隔,只显示部分日期但覆盖所有范围
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
showMaxLabel: true
|
|
},
|
|
axisLine: {
|
|
// show: false,
|
|
onZero: false,
|
|
lineStyle: {
|
|
color: 'white' // x轴线颜色
|
|
}
|
|
},
|
|
axisTick: {
|
|
alignWithLabel: true
|
|
}
|
|
},
|
|
yAxis: {
|
|
scale: true,
|
|
axisLine: {
|
|
// show: false,
|
|
lineStyle: {
|
|
color: 'white' // x轴线颜色
|
|
// width: 1
|
|
}
|
|
},
|
|
splitLine: {
|
|
show: false
|
|
},
|
|
axisLabel: {
|
|
// 刻度标签
|
|
show: true,
|
|
color: 'white',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9
|
|
},
|
|
axisTick: {
|
|
// 刻度线
|
|
show: true,
|
|
color: 'white'
|
|
},
|
|
min:
|
|
qxnlzhqData.dd < stopLossPrice * 0.98
|
|
? Math.floor(qxnlzhqData.dd)
|
|
: Math.floor(stopLossPrice * 0.98),
|
|
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
|
|
? Math.floor(qxnlzhqData.dd)
|
|
: Math.floor(stopLossPrice * 0.98),
|
|
Math.max(
|
|
Math.ceil(dataMax * 1.02),
|
|
qxnlzhqData.yl > 0 ? qxnlzhqData.yl : Math.ceil(dataMax * 1.02),
|
|
stopProfitPrice * 1.02
|
|
)
|
|
),
|
|
series: [
|
|
{
|
|
type: 'candlestick',
|
|
data: mixData.map((item) => item.value),
|
|
z: 3,
|
|
clip: true,
|
|
markPoint: {
|
|
symbol: 'circle',
|
|
symbolSize: 10,
|
|
data: markPointData,
|
|
z: 5 // 确保标记显示在最上层
|
|
},
|
|
itemStyle: {
|
|
normal: {
|
|
// 阳线样式(收盘 > 开盘)
|
|
// color: '#14b143', // 开盘价 < 收盘价时为绿色
|
|
color: '#00AAFF',
|
|
color0: '#FF007F', // 开盘价 > 收盘价时为红色
|
|
borderColor: '#00AAFF', // 阳线边框色(绿)
|
|
borderColor0: '#FF007F' // 阴线边框色(红)
|
|
// borderWidth: 1.5
|
|
}
|
|
},
|
|
// 实现 分区域背景色
|
|
markArea: {
|
|
silent: true,
|
|
data: [
|
|
...regions.map((region) => [
|
|
{
|
|
x: '30%',
|
|
yAxis: region.min,
|
|
itemStyle: {
|
|
normal: {
|
|
color: region.color
|
|
// color:'#000'
|
|
}
|
|
}
|
|
},
|
|
{ x: '95%', yAxis: region.max }
|
|
]),
|
|
...addWhiteRegions.map((region) => [
|
|
{
|
|
x: '30%',
|
|
yAxis: region.min,
|
|
itemStyle: {
|
|
normal: {
|
|
color: {
|
|
image: createPatternWithSize(region.color, region.backgroundSize),
|
|
repeat: 'repeat'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ x: '95%', yAxis: region.max }
|
|
])
|
|
],
|
|
markPoint: {
|
|
symbol: 'circle',
|
|
symbolSize: 10,
|
|
data: markPointData,
|
|
z: 5 // 确保标记显示在最上层
|
|
}
|
|
},
|
|
// 添加markLine实现顶部边框
|
|
markLine: {
|
|
silent: true,
|
|
symbol: 'none',
|
|
data: [
|
|
...markLineRegions.map((region) => [
|
|
{
|
|
name: `${Number(region.max).toFixed(2)}`,
|
|
x: '30%',
|
|
yAxis: region.max, // 只在区域顶部画线
|
|
label: {
|
|
normal: {
|
|
// show: true,
|
|
position: 'start',
|
|
color:
|
|
region.name == '情绪冰点区' ||
|
|
region.name == '多空消化区' ||
|
|
region.name == '认知潜伏区'
|
|
? 'white'
|
|
: 'white',
|
|
fontSize: window.innerWidth > 769 ? 12 : 9,
|
|
}
|
|
},
|
|
lineStyle: {
|
|
normal: {
|
|
color: '#FFFFFF',
|
|
width: 2,
|
|
type: 'dashed'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
x: '95%',
|
|
yAxis: region.max
|
|
}
|
|
]),
|
|
[
|
|
{
|
|
name: `止盈${stopProfitPrice.toFixed(2)}`,
|
|
x: '60%',
|
|
yAxis: stopProfitPrice,
|
|
label: {
|
|
normal: {
|
|
position: 'start',
|
|
fontSize: window.innerWidth > 769 ? 13 : 9,
|
|
fontWeight: 'bold',
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2
|
|
}
|
|
},
|
|
lineStyle: {
|
|
normal: {
|
|
color: '#FF0000',
|
|
width: 2,
|
|
type: 'solid'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
x: '95%',
|
|
yAxis: stopProfitPrice
|
|
}
|
|
],
|
|
[
|
|
{
|
|
name: `止损${stopLossPrice.toFixed(2)}`,
|
|
x: '60%',
|
|
yAxis: stopLossPrice,
|
|
label: {
|
|
normal: {
|
|
position: 'start',
|
|
fontSize: window.innerWidth > 769 ? 13 : 9,
|
|
fontWeight: 'bold',
|
|
textBorderColor: '#FFFFFF',
|
|
textBorderWidth: 2
|
|
}
|
|
},
|
|
lineStyle: {
|
|
normal: {
|
|
color: '#001EFF',
|
|
width: 2,
|
|
type: 'solid'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
x: '95%',
|
|
yAxis: stopLossPrice
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
|
|
{
|
|
name: '起',
|
|
type: 'candlestick',
|
|
data: qixhData.value.map((item) => item.value),
|
|
itemStyle: {
|
|
normal: {
|
|
color: '#87FF6B',
|
|
color0: '#87FF6B',
|
|
borderColor: '#87FF6B',
|
|
borderColor0: '#87FF6B'
|
|
}
|
|
},
|
|
gridIndex: 0,
|
|
z: 4,
|
|
tooltip: {
|
|
show: false
|
|
}
|
|
},
|
|
{
|
|
name: 'TOP',
|
|
type: 'candlestick',
|
|
data: topData.value.map((item) => item.value),
|
|
itemStyle: {
|
|
normal: {
|
|
color: '#000',
|
|
color0: '#000',
|
|
borderColor: '#000',
|
|
borderColor0: '#000'
|
|
}
|
|
},
|
|
gridIndex: 0,
|
|
z: 4,
|
|
tooltip: {
|
|
show: false
|
|
}
|
|
},
|
|
{
|
|
name: 'LOW',
|
|
type: 'candlestick',
|
|
data: lowData.value.map((item) => item.value),
|
|
itemStyle: {
|
|
normal: {
|
|
color: '#001EFF',
|
|
color0: '#001EFF',
|
|
borderColor: '#001EFF',
|
|
borderColor0: '#001EFF'
|
|
}
|
|
},
|
|
gridIndex: 0,
|
|
z: 4,
|
|
tooltip: {
|
|
show: false
|
|
}
|
|
}
|
|
// {
|
|
// name: '止盈线',
|
|
// type: 'line',
|
|
// data: takeProfitData,
|
|
// symbol: 'none',
|
|
// lineStyle: {
|
|
// normal: {
|
|
// color: '#FF0000', // 蓝色
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// },
|
|
// markPoint: {
|
|
// symbol: 'circle',
|
|
// symbolSize: 1,
|
|
// data: [
|
|
// {
|
|
// coord: [mixData.map((item) => item.value).length, stopProfitPrice],
|
|
// itemStyle: {
|
|
// color: '#ff80ff',
|
|
// textBorderColor: '#ffffff'
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'start',
|
|
// formatter: `{text|止盈}`,
|
|
// rich: {
|
|
// text: {
|
|
// color: '#FF0000',
|
|
// fontSize: 14,
|
|
// fontWeight: 'bold',
|
|
// textBorderColor: '#ffffff'
|
|
// }
|
|
// }
|
|
// // offset: [-20, 0]
|
|
// }
|
|
// }
|
|
// }
|
|
// ]
|
|
// }
|
|
// },
|
|
// {
|
|
// name: '止损线',
|
|
// type: 'line',
|
|
// data: stopLossData,
|
|
// symbol: 'none',
|
|
// lineStyle: {
|
|
// normal: {
|
|
// color: '#001EFF',
|
|
// width: 2,
|
|
// type: 'solid'
|
|
// }
|
|
// },
|
|
// markPoint: {
|
|
// symbol: 'circle',
|
|
// symbolSize: 1,
|
|
// data: [
|
|
// {
|
|
// coord: [mixData.map((item) => item.value).length - 1, stopLossPrice],
|
|
// itemStyle: {
|
|
// color: '#080bfd'
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'start',
|
|
// formatter: `{text|止损}`,
|
|
// rich: {
|
|
// text: {
|
|
// color: '#001EFF',
|
|
// fontSize: 14,
|
|
// fontWeight: 'bold',
|
|
// textBorderColor: '#ffffff'
|
|
// }
|
|
// }
|
|
// // offset: [-20, 0]
|
|
// }
|
|
// }
|
|
// }
|
|
// ]
|
|
// }
|
|
// },
|
|
// {
|
|
// name: '最低价',
|
|
// type: 'line',
|
|
// symbol: 'none',
|
|
// lineStyle: {
|
|
// normal: {
|
|
// color: 'transparent',
|
|
// width: 0
|
|
// }
|
|
// },
|
|
// markPoint: {
|
|
// symbol: 'circle',
|
|
// symbolSize: 1,
|
|
// data: [
|
|
// {
|
|
// coord: [mixData.length - 1, mixData[mixData.length - 1].value[2]],
|
|
// itemStyle: {
|
|
// color: 'transparent'
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'top',
|
|
// formatter: `{text|${mixData[mixData.length - 1].value[2].toFixed(2)}}`,
|
|
// rich: {
|
|
// text: {
|
|
// color: '#001EFF',
|
|
// fontSize: 12,
|
|
// fontWeight: 'bold',
|
|
// textBorderColor: '#ffffff',
|
|
// textBorderWidth: 2
|
|
// }
|
|
// },
|
|
// offset: [0, 30]
|
|
// }
|
|
// }
|
|
// }
|
|
// ]
|
|
// }
|
|
// },
|
|
// {
|
|
// name: '最高价',
|
|
// type: 'line',
|
|
// symbol: 'none',
|
|
// lineStyle: {
|
|
// normal: {
|
|
// color: 'transparent',
|
|
// width: 0
|
|
// }
|
|
// },
|
|
// markPoint: {
|
|
// symbol: 'circle',
|
|
// symbolSize: 1,
|
|
// data: [
|
|
// {
|
|
// coord: [mixData.length - 1, mixData[mixData.length - 1].value[3]],
|
|
// itemStyle: {
|
|
// color: 'transparent'
|
|
// },
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: 'bottom',
|
|
// formatter: `{text|${mixData[mixData.length - 1].value[3].toFixed(2)}}`,
|
|
// rich: {
|
|
// text: {
|
|
// color: '#FF0000',
|
|
// fontSize: 12,
|
|
// fontWeight: 'bold',
|
|
// textBorderColor: '#ffffff',
|
|
// textBorderWidth: 2
|
|
// }
|
|
// },
|
|
// offset: [0, -20]
|
|
// }
|
|
// }
|
|
// }
|
|
// ]
|
|
// }
|
|
// }
|
|
],
|
|
// 修改grid配置,添加响应式设置
|
|
grid: {
|
|
// 根据屏幕宽度动态调整
|
|
left: window.innerWidth <= 768 ? '2%' : '5%', // 手机端增加左边距
|
|
right: window.innerWidth <= 768 ? '8%' : '8%', // 手机端增加右边距
|
|
// top: window.innerWidth <= 768 ? '2' : '10',
|
|
top: window.innerWidth <= 768 ? '40' : '40',
|
|
// top: '10',
|
|
bottom: '60',
|
|
containLabel: true, // 改为true确保标签完全显示
|
|
width: 'auto', // 改为auto让系统自动计算
|
|
height: 'auto',
|
|
overflow: 'hidden'
|
|
}
|
|
}
|
|
// 应用配置
|
|
qxnlzhqEchartsInstance.setOption(option)
|
|
|
|
// 防抖函数,避免频繁触发resize
|
|
const debounce = (func, wait) => {
|
|
let timeout
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout)
|
|
func(...args)
|
|
}
|
|
clearTimeout(timeout)
|
|
timeout = setTimeout(later, wait)
|
|
}
|
|
}
|
|
|
|
// 监听窗口大小变化,调整图表尺寸
|
|
const resizeHandler = debounce(() => {
|
|
if (qxnlzhqEchartsInstance && !qxnlzhqEchartsInstance.isDisposed()) {
|
|
try {
|
|
qxnlzhqEchartsInstance.resize()
|
|
console.log('情绪能量转化器图表已重新调整大小')
|
|
} catch (error) {
|
|
console.error('情绪能量转化器图表resize失败:', error)
|
|
}
|
|
}
|
|
}, 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(() => {
|
|
if (qxnlzhqEchartsInstance && !qxnlzhqEchartsInstance.isDisposed()) {
|
|
try {
|
|
qxnlzhqEchartsInstance.resize()
|
|
console.log('情绪能量转化器容器大小变化,图表已调整')
|
|
} catch (error) {
|
|
console.error('情绪能量转化器容器resize失败:', error)
|
|
}
|
|
}
|
|
}, 100)
|
|
)
|
|
|
|
containerObserver.observe(qxnlzhqEchartsRef.value)
|
|
window.emoEnergyConverterContainerObserver = containerObserver
|
|
}
|
|
}
|
|
|
|
// 监听数据变化或语言变化
|
|
// watch(
|
|
// () => t.value,
|
|
// (newLang) => {
|
|
// fetchData()
|
|
// },
|
|
// { immediate: true, deep: true }
|
|
// )
|
|
|
|
// onMounted(fetchData)
|
|
|
|
onBeforeUnmount(() => {
|
|
// 组件卸载时销毁图表
|
|
if (qxnlzhqEchartsInstance) {
|
|
qxnlzhqEchartsInstance.dispose()
|
|
qxnlzhqEchartsInstance = null
|
|
}
|
|
|
|
// 移除窗口resize监听器
|
|
if (window.emoEnergyConverterResizeHandler) {
|
|
window.removeEventListener('resize', window.emoEnergyConverterResizeHandler)
|
|
window.emoEnergyConverterResizeHandler = null
|
|
}
|
|
|
|
// 清理容器观察器
|
|
if (window.emoEnergyConverterContainerObserver) {
|
|
window.emoEnergyConverterContainerObserver.disconnect()
|
|
window.emoEnergyConverterContainerObserver = null
|
|
}
|
|
})
|
|
</script>
|
|
<style scoped>
|
|
.qxnlzhqEcharts {
|
|
width: 100%;
|
|
height: 542px;
|
|
margin: 0;
|
|
box-sizing: border-box;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 手机端适配样式 */
|
|
@media only screen and (max-width: 768px) {
|
|
.qxnlzhqEcharts {
|
|
width: 100%;
|
|
height: 300px;
|
|
/* 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>
|