<template>
<div class="TuBanChart">
<div :id="chartConfigs.chartId" :style="{'width':chartConfigs.width+'px','height': chartConfigs.height+'px','margin':'0 auto'}"></div>
</div>
</template>
<script>
import { nanoid } from "nanoid"
import {jsjlLegends as legends} from './legends.js'
import _ from "lodash"
export default {
props:{
chartConfigs:{
type: Object,
required: true,
default: ()=>({})
},
targetWell:{ // 需要高亮闪烁的目标井
type:String,
default:''
},
valueZoneDisable:{ // 是否开启价值区
type:Boolean,
default:true
},
isDefineValueZone:{ // 是否允许编辑价值区
type:Boolean,
default:true
},
isEditValueZone:{ // 是否允许删除价值区
type:Boolean,
default:true
},
},
watch:{
chartConfigs:{
handler(val){
if(val.chartData){
this.handleChartData(val.chartData)
}
},
deep:true
},
isDefineValueZone(val){
if(val)this.areaPoints = []
}
},
data(){
return {
option: {
grid:{
left:60,
bottom:30,
containLabel:true
},
legend:{
show:true,
bottom:0,
itemWidth: 20,
itemHeight: 20
},
xAxis: [],
yAxis: [],
tooltip:{
show:true,
formatter:v=>{
if(v.componentSubType.includes('scatter') || v.componentSubType.includes('effectScatter')){
if(!v.seriesName) return
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
const {xAxis,yAxis} = this.option
let point = myChart.convertToPixel({ seriesIndex: 0 }, v.data)
const originPoint = [xAxis[0].min,yAxis[0].min]
const originPoint1 = myChart.convertToPixel({ seriesIndex: 0 }, originPoint)
const w1 = point[0] - originPoint1[0]
const h1 = originPoint1[1] - point[1]
// Z轴预测
let Z = ''
const {min, max} = this.zAxis
// 当前点与X轴夹角
let angle = Math.round(Math.atan(h1 / w1) * 180 / Math.PI)
let lines = this.option.series.filter(it=>it.type === 'line')
let angles = [...lines.map(it=>it.angle), angle].sort((a,b) => a - b)
let index = angles.indexOf(angle)
if(index > 0 && index < angles.length - 1){// 点左右两侧均有Z轴
let leftLineAngle = angles[index + 1]
let rightLineAngle = angles[index - 1]
// 过当前点的水平线与左侧射线交点距Y轴的水平像素距离
let lx = h1/(Math.tan(leftLineAngle / 180 * Math.PI))
// 过当前点的水平线与右侧射线交点距Y轴的水平像素距离
let rx = h1/(Math.tan(rightLineAngle / 180 * Math.PI))
// 过当前点的水平线与左侧射线交点的像素横坐标
let lx_px = originPoint1[0] + lx
// 过当前点的水平线与右侧射线交点的横坐标
let rx_px = originPoint1[0] + rx
// 过当前点的水平线与左侧射线交点的逻辑横坐标
let lx_lj = myChart.convertFromPixel({ xAxisIndex: 0 }, lx_px)
// 过当前点的水平线与左侧射线交点的逻辑横坐标
let rx_lj = myChart.convertFromPixel({ xAxisIndex: 0 }, rx_px)
// 过当前点的水平线与左右侧射线交点逻辑距离
let B = rx_lj - lx_lj
// 当前点与右侧的射线水平逻辑距离
let C = rx_lj - v.data[0]
let leftLineIndex = lines.findIndex(it=>it.angle === leftLineAngle)
let leftLineTick = (max - min)/(lines.length + 2 - 1) * (leftLineIndex + 1)
let rightLineIndex = lines.findIndex(it=>it.angle === rightLineAngle)
let rightLineTick = (max - min)/(lines.length + 2 - 1) * (rightLineIndex + 1)
Z = ((leftLineTick - rightLineTick)/ B * C + rightLineTick).toFixed(4)
}else if(index === 0){// 点右侧无Z轴,以X轴作为右侧射线
let leftLineAngle = angles[index + 1]
// 过当前点的垂直线与左侧射线交点的垂直像素距离
let y1_l = w1 * (Math.tan(leftLineAngle / 180 * Math.PI))
// 过当前点的垂直线与左侧射线交点的垂直像素坐标
let y1_px = originPoint1[1] - y1_l
// 过当前点的垂直线与左侧射线交点的Y逻辑坐标
let y1_lj = myChart.convertFromPixel({ yAxisIndex: 0 }, y1_px)
// 当前点与右侧射线的垂直像素坐标
let y2_px = point[1]
// 过当前点的垂直线与左侧射线交点的Y逻辑坐标
let y2_lj = myChart.convertFromPixel({ yAxisIndex: 0 }, y2_px)
// 过当前点的垂直线与左右侧射线交点逻辑距离
let B = y1_lj - originPoint[1]
// 当前点与右侧射线的垂直逻辑距离
let C = y2_lj - originPoint[1]
let leftLineIndex = lines.findIndex(it=>it.angle === leftLineAngle)
let leftLineTick = (max - min)/(lines.length + 2 - 1) * (leftLineIndex + 1)
let rightLineTick = min
Z = ((leftLineTick - rightLineTick)/ B * C + rightLineTick).toFixed(4)
}else if(index === angles.length - 1){// 点左侧无Z轴,以Y轴作为左侧射线
let rightLineAngle = angles[index - 1]
// 过当前点的水平线与右侧射线交点距Y轴的水平像素距离
let rx = h1/(Math.tan(rightLineAngle / 180 * Math.PI))
// 过当前点的水平线与右侧射线交点的像素横坐标
let rx_px = originPoint1[0] + rx
// 过当前点的水平线与左侧射线交点的逻辑横坐标
let lx_lj = originPoint[0]
// 过当前点的水平线与右侧射线交点的逻辑横坐标
let rx_lj = myChart.convertFromPixel({ xAxisIndex: 0 }, rx_px)
let B = rx_lj - lx_lj
let C = rx_lj - v.data[0]
let leftLineTick = max
let rightLineIndex = lines.findIndex(it=>it.angle === rightLineAngle)
let rightLineTick = (max - min)/(lines.length + 2 - 1) * (rightLineIndex + 1)
Z = ((leftLineTick - rightLineTick)/ B * C + rightLineTick).toFixed(4)
}
const realData = `<span>${this.$t('jh')}:</span>${v.data[2].jh} <br/>
<span>x-${xAxis[0].name}:</span>${Number(v.data[0]).toFixed(4)} <br/>
<span>y-${yAxis[0].name}:</span>${Number(v.data[1]).toFixed(4)} <br/>
<span>${this.$t('syjl')}:</span>${v.data[2].syjl} <br/>`
let predictData = `${this.$t('ycsj')} <br/>`
// 像素坐标转逻辑坐标
// 上X轴预测,垂直取值;右Y轴预测,水平取值
xAxis.slice(1).forEach((item,index)=>{
const px = myChart.convertFromPixel({ xAxisIndex: index + 1 }, point[0]).toFixed(4)
const rx = v.data[2][item.name]
if(rx){
predictData += `<span>x-${item.name}:</span>${px}/${Number(rx).toFixed(4)} <br/>`
}else{
predictData += `<span>x-${item.name}:</span>${px} <br/>`
}
})
yAxis.slice(1).forEach((item,index)=>{
const py = myChart.convertFromPixel({ yAxisIndex: index + 1 }, point[1]).toFixed(4)
const ry = v.data[2][item.name]
if(ry){
predictData += `<span>y-${item.name}:</span>${py}/${Number(ry).toFixed(4)} <br/>`
}else{
predictData += `<span>y-${item.name}:</span>${py} <br/>`
}
})
const rz = v.data[2][this.zAxis.name]
if(rz){
predictData += `<span>z-${this.zAxis.name}:</span>${Z}/${Number(rz).toFixed(4)}`
}else{
predictData += `<span>z-${this.zAxis.name}:</span>${Z}`
}
return `<div class="chartTooltip">${realData}${predictData}</div>`
}
}
},
series: []
},
areaPoints:[],
zAxis:{}
}
},
mounted(){
this.initChart()
},
methods:{
initChart(){
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option)
// 左键单击
myChart.getZr().on('click', params=> {
if(this.valueZoneDisable ||!this.isDefineValueZone) return
// 获取点击事件里面的坐标轴的偏移值
const pointInPixel = [params.offsetX, params.offsetY]
if (myChart.containPixel({ seriesIndex: 0 }, pointInPixel)) {
// 像素坐标转逻辑坐标
let point = myChart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)
this.areaPoints.push(point)
this.drawPoints()
}
})
// 右键单击
myChart.getZr().on('contextmenu', params=> {
if(this.valueZoneDisable) return
if(this.isDefineValueZone){
if(this.areaPoints.length>2){
this.drawZone(this.areaPoints)
this.areaPoints = []
}
}else if(this.isEditValueZone){
const target = params.target
if(target?.type === 'polygon'){
const {index} = target.parent.__ecComponentInfo
this.deleteZone(index)
}
}
})
},
drawPoints(){
const {series} = this.option
const index = series.findIndex(it=>it.type==='scatter'&&it.symbolSize===5)
if(index>-1){
series[index].data=this.areaPoints
}else{
series.push({
type: 'scatter',
name:'',
color:'
symbolSize:5,
data:this.areaPoints
})
}
this.option.series = series
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option,true)
},
handleChartData(data){
const {axis,valueZone,axisData,axisType} = data
const xAxis = {
bottom:[],
top:[]
}
const yAxis = {
left:[],
right:[]
}
const zAxis = []
axis.forEach(item=>{
const {type,curveName,startValue,endValue,defaultValue,zaa} = item
const obj = {
name:curveName,
min:startValue,
max:endValue
}
if(type == 1 && defaultValue == 1){
xAxis.bottom.push(obj)
}else if(type == 2 && defaultValue == 1){
yAxis.left.push(obj)
}else if(type == 3){
xAxis.top.push(obj)
}else if(type == 4){
yAxis.right.push(obj)
}else if(type == 5){
zAxis.push({
...obj,
angles:zaa.map(it=>it.angle)
})
}
})
const option = _.cloneDeep(this.option)
const xAxisData = []
if(xAxis.bottom.length){
const {name,min,max} = xAxis.bottom[0]
xAxisData.push(
{name, type: axisType.xAxis, min:(axisType.xAxis === 'log' && min == 0)?.001:min, max,nameLocation:'start',nameGap:30 }
)
}
if(xAxis.top.length){
xAxis.top.forEach((it,index)=>{
const {name,min,max} = it
xAxisData.push(
{name, type: axisType.xAxis, min:(axisType.xAxis === 'log' && min == 0)?.001:min, max,nameLocation:'start',nameGap:30,position:"top",offset:35*index,axisLine:{ onZero:false } }
)
})
}
option.xAxis = xAxisData
const yAxisData = []
if(yAxis.left.length){
const {name,min,max} = yAxis.left[0]
yAxisData.push(
{name, type: axisType.yAxis, min:(axisType.yAxis === 'log' && min == 0)?.001:min, max,nameGap:20 }
)
}
if(yAxis.right.length){
yAxis.right.forEach((it,index)=>{
const {name,min,max} = it
yAxisData.push(
{name, type: axisType.yAxis, min:(axisType.yAxis === 'log' && min == 0)?.001:min, max,nameGap:20,position:"right",offset:35*index,axisLine:{ onZero:false } }
)
})
}
option.yAxis = yAxisData
const scatterData = []
const syjls = [...new Set(axisData.map(it=>it.syjl))].filter(it=>it!==undefined)
syjls.forEach(item=>{
const d1 = axisData.filter(it=>it.syjl === item && it[item] && it[xAxis.bottom[0].name] && it[yAxis.left[0].name])
if(d1.length){
scatterData.push({
type: 'scatter',
name: item,
symbol:d1[0][item]?.startsWith('
color:d1[0][item]?.startsWith('
symbolSize:20,
data:d1.map(i=>[
i[xAxis.bottom[0].name],
i[yAxis.left[0].name],
i
])
})
}
})
if(this.targetWell){
const well = axisData.find(it=>it.jh === this.targetWell)
if(well && scatterData.length){
const index1 = scatterData.findIndex(it=>it.name === well.syjl)
if(index1 > -1){
const index2 = scatterData[index1].data.findIndex(it=>it[2].jh === well.jh)
if(index2 > -1){
scatterData[index1].data.splice(index2,1)
}
}
scatterData.push({
type: 'effectScatter',
name: well.syjl,
symbol:well[well.syjl]?.startsWith('
color:well[well.syjl]?.startsWith('
symbolSize:20,
data:[
[well[xAxis.bottom[0].name],well[yAxis.left[0].name],well]
]
})
}
}
option.series = [...scatterData]
this.option = option
if(!this.valueZoneDisable && valueZone.filter(it=>it.status).length){
valueZone.forEach(item=>{
if(item.status){
const points = item.valueCoordinate.split(',').map(it=>it.split(' '))
this.drawZone(points,item)
}
})
}else{
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option,true)
}
this.$nextTick(()=>{
this.handleLines(zAxis[0])
})
},
handleLines(zAxis){
this.zAxis = zAxis
const myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
const {xAxis,yAxis} = this.option
const minX = xAxis[0].min
const maxX = xAxis[0].max
const minY = yAxis[0].min
const maxY = yAxis[0].max
const point1 = [
myChart.convertToPixel({ xAxisIndex: 0 }, minX),
myChart.convertToPixel({ yAxisIndex: 0 }, minY)
]
const point2 = [
myChart.convertToPixel({ xAxisIndex: 0 }, maxX),
myChart.convertToPixel({ yAxisIndex: 0 }, minY)
]
const point3 = [
myChart.convertToPixel({ xAxisIndex: 0 }, minX),
myChart.convertToPixel({ yAxisIndex: 0 }, maxY)
]
if(!point1 || !point2 || !point3){
return
}
const w_px = point2[0] - point1[0]
const h_px = Math.abs(point3[1] - point1[1])
const curA = Math.round(Math.atan(h_px / w_px) * 180 / Math.PI)
const lineData = []
const originPoint = [minX,minY]
const {name,min,max,angles} = zAxis
angles.sort((a,b)=>a-b).forEach((angle,index)=>{
let endPoint = []
if(angle <= curA){ // 根据X算Y
const h1 = Math.tan( angle/ 180 * Math.PI) * w_px
const y1 = point2[1] - h1
const y2 = myChart.convertFromPixel({ yAxisIndex: 0 }, y1)
endPoint = [maxX,y2]
}else{ // 根据Y算X
const w1 = h_px / Math.tan( angle/ 180 * Math.PI)
const x1 = point3[0] + w1
const x2 = myChart.convertFromPixel({ xAxisIndex: 0 }, x1)
endPoint = [x2,maxY]
}
lineData.push({
type: 'line',
color:'red',
symbol:v=>v[0]===originPoint[0]&&v[1]===originPoint[1]?'none':'circle',
symbolSize: 1,
lineStyle:{
width:1
},
label:{
show:true,
color:'red',
// Z轴名字(刻度):(Z轴右值-Z轴左值)/(Z轴数量+2-1)*n (n表示Z轴序列号)
formatter:`${name}=${((max - min)/(angles.length+2-1)*(index+1)).toFixed(0)}`
},
angle,
data:[originPoint,endPoint]
})
})
this.option.series.push(...lineData)
myChart.setOption(this.option,true)
},
drawZone(data,updateObj){
const echarts = this.$echarts
const renderItem = (params, api) => {
if (params.context.rendered) {
return
}
params.context.rendered = true
let points = []
for (let i = 0
points.push(api.coord(data[i]))
}
return {
type: 'polygon',
shape: {
points: echarts.graphic.clipPointsByRect(points, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
})
},
style: api.style({
fill: updateObj?.colorValue || '
stroke: '
})
}
}
const nid = updateObj?.valueName || this.$t('jzq') + nanoid()
this.option.series.push({
nid,
type: 'custom',
renderItem,
data
})
// 绘制价值区时删除历史打点
const valPointsIndex = this.option.series.findIndex(it=>it.type==='scatter'&&it.symbolSize===5)
if(valPointsIndex>-1){
this.option.series[valPointsIndex].data = []
}
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option,true)
if(!updateObj){
this.addTableData(nid)
}
},
deleteZone(index){
const {nid} = this.option.series[index]
this.option.series.splice(index,1)
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option,true)
this.$emit('deleteRow',nid)
},
addTableData(nid){
let color = '#ff00004d'
let wellCount = this.getPointNumInZone()
const obj ={
valueName:nid,
wellCount,
colorValue:color,
status:true,
xAxis:this.option.xAxis[0].name,
yAxis:this.option.yAxis[0].name,
points:[...this.areaPoints],
}
this.$emit('addRow',obj)
},
getPointNumInZone(){
const scatterSeries = this.option.series.filter(it=>it.type.includes('scatter')&&it.symbolSize!==5)
const points = []
scatterSeries.forEach(it=>{
points.push(...it.data)
})
let num = 0
points.forEach(it=>{
let isIn = this.pointInPolygon(it,this.areaPoints)
if(isIn) num++
})
return num
},
pointInPolygon(point, polygon) {
var x = point[0], y = point[1]
var inside = false
for (var i = 0, j = polygon.length - 1
var xi = polygon[i][0], yi = polygon[i][1]
var xj = polygon[j][0], yj = polygon[j][1]
var intersect = ((yi > y) != (yj > y)) &&
(x < (xj - xi) * (y - yi) / (yj - yi) + xi)
if (intersect) inside = !inside
}
return inside
},
deleteZoneByName(name){
const index = this.option.series.findIndex(it=>it.type==='custom' && it.nid === name)
if(index>-1){
this.option.series.splice(index,1)
let myChart = this.$echarts.init(document.getElementById(this.chartConfigs.chartId))
myChart.setOption(this.option,true)
}
},
changeColorByName(row){
const index = this.option.series.findIndex(it=>it.type==='custom' && it.nid === row.valueName)
if(index>-1){
this.option.series.splice(index,1)
this.drawZone(row.points,row)
}
},
changeStatusByName(row){
if(row.status){
this.drawZone(row.points,row)
}else{
this.deleteZoneByName(row.valueName)
}
}
}
}
</script>
<style scoped lang="scss">
.TuBanChart{
}
</style>
<style>
.chartTooltip span{
display: inline-block
width:80px
text-align: right
}
</style>