<template>
<div class="resultsReview-chart" id="resultsReviewChart">
<div id="chart" class="chart-container" />
<div id="chartMarkBox" class="chartMarkBox">
</div>
</template>
<script>
import * as echarts from "echarts@5.1.2"
export default {
data() {
return {
batchMarker: []// 框选数据
}
},
mounted() {
this.initCharts()
window.onresize = () => {
this.chart.resize({ width: "auto", height: "auto" })
}
this.initTooltipClick()
},
methods: {
// 无数据添加标注方法
handleTooltipClick(event){
if (event.target.id === 'clickElement') {
const param1 = event.target.dataset.param1
const param2 = event.target.dataset.param2
if (this.props.showMarker) {
this.setState({
markerParam: {
ym: param1,
value: null,
name: param2,
content: null,
tagType: null,
},
})
const markerBox = document.getElementById('chartMarkBox')
markerBox.style.display = 'block'
markerBox.style.left = tooltipX + 'px'
markerBox.style.top = tooltipY + 'px'
} else {
message.warn('请先点击开始标注')
}
}
}
initTooltipClick() {
// 处理tooltip中添加的点击事件,还未点击时已触发事件
// 获取tooltip父元素并为其添加点击事件监听器
const tooltipContainerParent = document.querySelector('.chart-container')
if (tooltipContainerParent) {
tooltipContainerParent.parentElement.addEventListener('click', this.handleTooltipClick)
}
},
// 获取y轴最大最小值
genMinAndMax(yData){
const min = _.minBy(yData, (i) => {
if (i !== null) {
return Number(i)
} else {
return null
}
})
const max = _.maxBy(yData, (i) => {
if (i !== null) {
return Number(i)
} else {
return null
}
})
const minValue = min ? Number(min) : 0
const maxValue = max ? Number(max) : 0
let resultMin, resultMax
let step = maxValue - minValue
if (step > 0 && step < 2) {
const middle = (maxValue + minValue) / 2
resultMin = (Number((middle - 1.5).toFixed(2)) * 100) / 100
resultMax = (Number((middle + 1.5).toFixed(2)) * 100) / 100
} else {
if (step === 0) {
step = 1
}
resultMin = minValue === 0 ? 0 : (Number((minValue - (step / 0.6) * 0.2).toFixed(2)) * 100) / 100
resultMax = (Number(((step / 0.6) * 0.2 + maxValue).toFixed(2)) * 100) / 100
}
if (resultMin < 0 && minValue > 0) {
//如果没有小于0的数 将最小值设为0
resultMin = 0
}
// // //最小最大值相等时
// if (step === 0) {
// step = 1
// }
return [resultMin, resultMax]
},
// 获取y轴间隔,使过程线始终保持在中间位置
getInterval(num1, num2) {
const diff = Math.floor(num2 - num1)
let interval = 1
while (diff / interval > 10) {
interval *= 10
}
while (diff / interval < 1) {
interval /= 10
}
if (interval < 1) {
interval = 1
}
return interval
},
initOption() {
const seriesData = [-2.15, -2.14, -2.15, -2.15, -2.14, -2.15, -2.16, -2.15, -2.15, -2.15, -2.15, -2.15, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
const xAxisData = ['2023-03-30 00:00:00', '2023-03-30 04:00:00', '2023-03-30 08:00:00', '2023-03-30 12:00:00', '2023-03-30 16:00:00', '2023-03-30 20:00:00', '2023-03-31 00:00:00', '2023-03-31 04:00:00', '2023-03-31 08:00:00', '2023-03-31 12:00:00', '2023-03-31 16:00:00', '2023-03-31 20:00:00', '2023-04-01 00:00:00', '2023-04-01 04:00:00', '2023-04-01 08:00:00', '2023-04-01 12:00:00', '2023-04-01 16:00:00', '2023-04-01 20:00:00', '2023-04-02 00:00:00', '2023-04-02 04:00:00', '2023-04-02 08:00:00', '2023-04-02 12:00:00', '2023-04-02 16:00:00', '2023-04-02 20:00:00', '2023-04-03 00:00:00', '2023-04-03 04:00:00', '2023-04-03 08:00:00', '2023-04-03 12:00:00', '2023-04-03 16:00:00', '2023-04-03 20:00:00']
const [min, max] = this.genMinAndMax(seriesData)
const avg = (max - min) / 2 + min
const markerData = [
// 处理x轴数据不存在时显示标记
{
coord: [14, avg], // 第一个值代表数据在X轴的位置,第二个值根据最大最小值取中间值,当y值不存在时,标记无法显示,给一个默认值
name: '标注',
symbol: "circle',
symbolSize: 12,
value: '数据',
},
// x轴数据存在时可正常显示标记
{
xAxis: '2023-03-31 20:00:00',
yAxis: -2.15,
name: '标注',
symbol: "circle',
symbolSize: 12,
value: '数据2',
}
]
return {
grid: {
left: '5%',
right: '5%',
bottom: 40,
top: 60,
containLabel: true,
},
tooltip: {
trigger: 'axis',
triggerOn: 'mousemove|click',
enterable: true,
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow', // 默认为直线,可选为:'line' | 'shadow'
},
backgroundColor: 'rgba(255, 255, 255, 0.96)',
textStyle: {
color: '
fontSize: 12,
// lineHeight: 26,
},
// padding: [8, 10],
extraCssText: 'box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.12);opacity: 0.96;',
position: function(point, params, dom, rect, size) {
const [mouseX, mouseY] = point
let x = mouseX,
y = mouseY
const { offsetWidth: domWidth, offsetHeight: domHeight } = dom
const [viewWidth, viewHeight] = size.viewSize
if (mouseX + domWidth > viewWidth) {
x = x - domWidth
}
if (mouseY + domHeight > viewHeight) {
y = y - domHeight
}
tooltipX = x
tooltipY = y
return [x, y]
},
formatter: (params) => {
return `
<div class="self-tooltip-box">
<span class="y-data">${params[0].name}<span/><br/>
<div class="self-tooltip">
<span class="y-data">${params[0].seriesName}:</span><span class="y-name">${params[0].data || ''}</span><br/>
<div data-param1="${params[0].name}" data-param2="${params[0].seriesName}" id="clickElement" class="link">添加标注</div>
</div>
</div>
`
}
},
toolbox: {
show: true,
right: '3%',
// showTitle: false, // 隐藏默认文字,否则两者位置会重叠
feature: {
brush: {
type: ['rect', 'clear'],
title: {
rect: '框选',
clear: '清除框选',
},
},
},
},
brush: {
toolbox: ['rect', 'clear'],
brushStyle: {
borderWidth: 2,
color: 'rgba(0,0,0,0)',
borderColor: '#E80B0B',
},
xAxisIndex: 0,
throttleType: 'debounce',
throttleDelay: 1500,
},
xAxis: {
type: 'category',
data: xAxisData,
axisTick: {
alignWithLabel: true,
},
axisLabel: {
textStyle: {
color: 'rgba(191,191,191,1)',
},
},
axisLine: {
show: true,
onZero: false,
lineStyle: {
type: 'solid',
color: 'rgba(191,191,191,1)',
},
},
axisPointer: {
lineStyle: {
color: '#FF4D4F',
},
},
},
yAxis: {
type: 'value',
name: '水位(m)',
inverse: false,
nameLocation: 'end',
nameTextStyle: {
color: '#BFBFBF',
fontSize: 14,
lineHeight: 22,
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
},
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: true,
textStyle: {
color: 'rgba(191,191,191,1)',
},
formatter: function(value, index) {
let copyValue = value
if (typeof copyValue === 'number') {
copyValue = copyValue.toString()
if (copyValue.split('.')[1] && copyValue.split('.')[1].length > 3) {
return value.toFixed(3)
} else {
return value.toFixed(2)
}
} else {
return value
}
},
},
min: min,
max: max,
interval: getInterval(mins, maxs),
},
series: [
{
name: '水位(m)',
type: 'line',
data: seriesData,
itemStyle: {
color: '#1890ff',
width: 3,
},
symbolSize: 8,
large: true,
markPoint: {
// symbolSize: [24, 24],
symbolOffset: [0, '-100%'],
label: {
fontSize: 10,
offset: [0, 16],
color: '#595959',
},
data: markerData,
},
yAxisIndex: 0,
zlevel: 1,
selectedMode: 'multiple',
select: {
itemStyle: {
color: '#fa8c16',
},
},
},
],
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
minValueSpan: 3,
// realtime: true,
},
{
start: 0,
end: 100,
minValueSpan: 3,
handleIcon:
'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2,
},
bottom: 0,
// realtime: true,
},
],
}
}
initCharts() {
this.chart = echarts.init(document.getElementById("chart"), "dark")
const option = this.initOption()
this.chart.setOption(option)
// 点击添加标注
this.chart.on('click', params => {
// 处理点击事件
const domWidth = document.getElementById('resultsReviewChart').getBoundingClientRect()
.width
const markerBox = document.getElementById('chartMarkBox')
markerBox.style.display = 'block'
markerBox.style.left =
params.event.offsetX > domWidth - 327
? params.event.offsetX - 327 + 'px'
: params.event.offsetX + 25 + 'px'
markerBox.style.top = params.event.offsetY - 80 + 'px'
})
// 框选
this.chart.on('brushEnd', (params) => {
const xAxisArr = params.areas[0].coordRange[0]
const xAxisData = .slice(xAxisArr[0], xAxisArr[1])
this.batchMarker = option.xAxis.data
// const domWidth = document.getElementById('resultsReviewChart').getBoundingClientRect().width
const markerBox = document.getElementById('chartMarkBox')
markerBox.style.display = 'block'
markerBox.style.left = 527 + 'px'
markerBox.style.top = 200 + 'px'
})
// 清除框选
this.chart.on('brush', (params) => {
if (params.command === 'clear') {
// 执行清除选区后的操作
this.cleanEdit()
}
})
}
}
}
</script>
<style lang="scss" scoped>
.chart-container {
width: 100%
height: calc(100vh - 340px)
padding: 16px 24px
.self-tooltip-box{
width: 196px
font-size: 12px
padding: 8px 16px
font-weight: 400
.self-tooltip{
.y-data{
font-family: Microsoft YaHei UI
font-style: normal
font-weight: normal
color:
font-size: 12px
line-height: 22px
}
.y-name{
float: right
color:
font-size: 12px
line-height: 22px
}
.link{
font-size: 12px
line-height: 22px
margin-top: 8px
color:
cursor: pointer
}
}
}
}
.chartMarkBox{
width: 327px
// height: 200px
position: absolute
display: none
// min-height: 118px
background:
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2)
border-radius: 4px
padding: 16px 16px 0 16px
z-index: 99
}
</style>