瀏覽代碼

增加动液面曲线绘制组件,动液面曲线集成

chenwen 1 年之前
父節點
當前提交
0514f6d9ba

+ 21 - 0
src/api/wellLiquid.js

@@ -0,0 +1,21 @@
+import request from '../utils/request';
+
+const api={}
+
+api.getLiquid = (dataId)=>{
+	return request({
+	    url: '/liquid/get',
+		method: 'post',
+	    data:  {dataId}
+	});
+}
+
+api.saveLiquid = (liquid)=>{
+	return request({
+	    url: '/liquid/save',
+		method: 'post',
+	    data: liquid
+	});
+}
+
+export  default api

+ 1 - 0
src/components/crudtable/CrudTable.vue

@@ -172,6 +172,7 @@
 		justify-content:space-between;
 		justify-content:space-between;
 		border:1px solid #e3f1f8;
 		border:1px solid #e3f1f8;
 		border-bottom:0px;
 		border-bottom:0px;
+		align-items: center;
 	}
 	}
 	
 	
 	.tab-toolbar .is-loading{
 	.tab-toolbar .is-loading{

+ 162 - 0
src/components/liquidCurve/LiquidCurve.vue

@@ -0,0 +1,162 @@
+<template>
+	<div class="zr-app">
+		<div  :id="id" class="zr-container"></div>
+		<div class="context-menu" @mouseover="menuOverHandle" @mouseout="menuOutHandle" :style="{left:(ctxMenuConfig?.x+'px'),top:(ctxMenuConfig?.y+'px')}" v-show="ctxMenuConfig?.show">
+			<div class="menu-item" @click="setMark('wellHead')">设为井口位置</div>
+			<div class="menu-item" @click="setMark('liquid')">设为液面位置</div>
+			<div class="menu-item" @click="setMark('sound')" v-show="refComputeMode=='sound_mark'">设为音标位置</div>
+			<div class="menu-item" @click="setMark('hoopStart')" v-show="refComputeMode=='hoop'">设为起始接箍位置</div>
+			<div class="menu-item" @click="setMark('hoopEnd')" v-show="refComputeMode=='hoop'">设为截止接箍位置</div>
+		</div>
+	</div>
+</template>
+
+<script setup>
+	import {onMounted,onUnmounted,ref,watch} from 'vue'
+	
+	import {LiquidCurve} from './lib/liquidCurve'
+	
+	const  refComputeMode=ref(props.computeMode)
+	
+	const props=defineProps({
+		id:{
+			type:String,
+			default:'zrender_liquid_curve'
+		},
+		computeMode:{
+			type:String
+		},
+		data:{
+			type:Object
+		}
+	})
+	
+	const ctxMenuConfig=ref(null)
+	
+	let liquidCurve=null
+	let zrbox=null
+	onMounted(()=>{
+		liquidCurve=new LiquidCurve(props.id,ctxMenuConfig)
+		//liquidCurve.build()
+		//liquidCurve.setData(props.data)
+		//zrbox=document.querySelector('.zr-app').getBoundingClientRect()
+	})
+	
+	const setData=(dataObj)=>{
+		liquidCurve.setData(dataObj)
+	}
+	
+	const setInitData=(dataObj)=>{
+		liquidCurve.setInitData(dataObj)
+		refComputeMode.value=dataObj.computeMode
+	}
+	
+	const getMarkPos=()=>{
+		return liquidCurve.getMarkPos()
+	}
+	
+	const setMark=(markType)=>{
+		ctxMenuConfig.value.show=false
+		liquidCurve.moveMarkLine(markType)
+	}
+	
+	const refreshDepthRule=(liquidDepth)=>{
+		liquidCurve.drawDepthRule(liquidDepth)
+	}
+	
+	const initResize=()=>{
+		liquidCurve.resize()
+		liquidCurve.build()
+		//liquidCurve.setData(props.data)
+		zrbox=document.querySelector('.zr-app').getBoundingClientRect()
+	}
+	
+	const reset=()=>{
+		liquidCurve.reset()
+	}
+	
+	let menuShowTimer=null
+	
+	const menuOverHandle=()=>{
+		if(menuShowTimer){
+			clearTimeout(menuShowTimer)
+		}
+		ctxMenuConfig.value.show=true
+	}
+	
+	const menuOutHandle=()=>{
+		menuShowTimer=setTimeout(()=>{
+			ctxMenuConfig.value.show=false
+		},300)
+	}
+	
+	onUnmounted(()=>{
+		if(liquidCurve){
+			console.log('liquid curve dispose')
+			liquidCurve.dispose()
+		}
+	})
+	
+	watch(ctxMenuConfig,(newCfg,oldcfg)=>{
+		
+		if(newCfg.show){
+			newCfg.x-=5
+			newCfg.y-=5
+			if((newCfg.x+160)>zrbox.width){
+				newCfg.x-=160-10
+			}
+			
+		}
+	})
+	
+	defineExpose({
+		setData,
+		setInitData,
+		getMarkPos,
+		initResize,
+		reset,
+		refreshDepthRule
+	})
+</script>
+
+<style scoped>
+	.zr-app{
+		position: relative;
+		width: 100%;
+		min-width:1000px;
+		height: 320px;
+	}
+	.zr-container {
+	  width:100%;
+	  height:100%;
+	  border: 1px solid #e5e5e5;
+	  box-sizing: border-box;
+	 
+	}
+	.context-menu{
+		position: absolute;
+		width:150px;
+		padding:5px;
+		top:100px;
+		left:100px;
+		background-color: rgb(255,255,255,1);
+		border-radius: 2px;
+		border: 1px solid #e8e8e8;
+		box-shadow: 2px 2px 8px  #e4e4e4;
+		font-size: 12px;
+		text-align: left;
+	}
+	.context-menu .menu-item{
+		display: flex;
+		flex-flow: row nowrap;
+		justify-content: space-between;
+		font-size:14px;
+		padding:5px;
+		cursor:pointer;
+	}
+	
+	.context-menu .menu-item:hover{
+		background-color:#55aaff;
+		color:#fff;
+	}
+</style>

+ 105 - 0
src/components/liquidCurve/lib/drawUtils.js

@@ -0,0 +1,105 @@
+import * as zrender from 'zrender'
+
+export default {
+	drawLineSimple(x1,y1,x2,y2){
+		return this.drawLine(x1,y1,x2,y2,{})
+	},
+	drawLine(px1,py1,px2,py2,{stroke='#000',lineWidth=1}){
+		let line=new zrender.Line({
+			shape:{
+				x1:px1,
+				y1:py1,
+				x2:px2,
+				y2:py2
+			},
+			style:{
+				stroke,
+				lineWidth
+			}
+		})
+		
+		return line
+		
+	},
+	drawText(txt,px,py,align,size){
+		let txtObj=new zrender.Text({
+			rectHover:true,
+			style:{
+				text:txt,
+				x:px,
+				y:py,
+				fontSize:size||12
+			}
+		})
+		
+		let prect=txtObj.getBoundingRect()
+		
+		if(!align){
+			align='left|top'
+		}
+		let [hAlign,vAlign]=align.split('\|')
+		
+		switch(hAlign){
+			case 'center':
+				txtObj.attr('style',{x:txtObj.style.x-prect.width/2})
+				break
+			case 'right':
+				txtObj.attr('style',{x:txtObj.style.x-prect.width})
+				break
+		}
+		switch(vAlign){
+			case 'middle':
+				txtObj.attr('style',{y:txtObj.style.y-prect.height/2})
+				break
+			case 'bottom':
+				txtObj.attr('style',{y:txtObj.style.y-prect.height})
+				break
+		}
+		
+		return txtObj
+		
+	},
+	drawPolyline(pts,{stroke='#000',lineWidth=1}){
+		let line=new zrender.Polyline({
+			shape:{
+				points:pts
+			},
+			style:{
+				stroke,
+				lineWidth
+			}
+		})
+		
+		return line
+	},
+	drawPt(cx,cy,r,{fill='#000'}){
+		let pt=new zrender.Circle({
+			shape:{
+				cx,
+				cy,
+				r
+			},
+			style:{
+				fill
+			}
+		})
+		return pt
+	},
+	drawRect(x,y,width,height,{fill='transparent'}){
+		let rect = new zrender.Rect({
+			cursor:'default',
+			shape:{
+				x,
+				y,
+				width,
+				height
+			},
+			style: {
+			  fill, 
+			  lineWidth: 1 
+			}
+		})
+		
+		return rect
+	}
+}

+ 366 - 0
src/components/liquidCurve/lib/liquidCurve.js

@@ -0,0 +1,366 @@
+import * as echarts from 'echarts'
+import * as zrender from 'zrender'
+import drawUtils from './drawUtils'
+import utils from './utils'
+
+function LiquidCurve(containerId,refCtxMenu){
+	this.containerId=containerId
+	this.refCtxMenu=refCtxMenu
+	this.zr=null
+	this.container=null
+	this.mask=null
+	this.tipEl=null //提示文本显示对象
+	this.origin=null
+	this.origin2=null
+	this.axisEnd=null
+	this.xAxisGap=30  //中间轴与下部顶边间隙
+	this.xRatio=null
+	this.yRatio=null
+	
+	this.wellHeadPos=1
+	this.hoopSerial={}
+	this.liquidSerial={}
+	
+	this.indicateLine=null
+    this.wellHeadMark=null  //井口标记线
+	this.liquidMark=null  //液面标记线
+	this.soundMark=null  //音标标记线
+	this.hoopStartMark=null  //起始接箍标记线
+	this.hoopEndMark=null  //截止接箍标记线
+	this.ruleGroup=null //标尺容器
+	this.init(containerId)
+}
+
+LiquidCurve.prototype={
+	init(containerId){
+		this.zr = zrender.init(document.getElementById(containerId))
+		this.container =  new zrender.Group()
+		this.zr.add(this.container)
+		this.zr.on('contextmenu',(event)=>{
+			//console.log(event.event)
+			event.event.preventDefault()
+			this.refCtxMenu.value={x:event.offsetX,y:event.offsetY,show:true}
+		})
+		
+		this.zr.on('click',(event)=>{
+			event.event.preventDefault()
+			this.refCtxMenu.value={show:false}
+			//console.log(this.wellHeadMark)
+			//this.wellHeadMark.setPosition([event.event.offsetX,0])
+			//this.wellHeadMark.dirty()
+		})
+		
+		
+	},
+	addChild(displayObj){
+		this.container.add(displayObj)
+	},
+	build(){
+		
+		let glbH=this.zr.getHeight(),glbW=this.zr.getWidth(),midH=(glbH-this.xAxisGap)/2
+		
+		this.origin={
+			x:0,
+			y:midH
+		}
+		this.axisEnd={
+			x:glbW,
+			y:midH
+		}
+		this.origin2={
+			x:0,
+			y:glbH
+		}
+		
+		//标尺容器
+		this.ruleGroup=new zrender.Group()
+		this.container.add(this.ruleGroup)
+		
+		//指示线
+		this.indicateLine=drawUtils.drawLine(10,0,10,glbH,{stroke:'#ff0000'})
+		this.container.add(this.indicateLine)
+		this.indicateLine.attr({
+			zlevel:8,
+			style:{
+				lineDash:[5,3]
+			}
+		})
+		//标记线
+		this.wellHeadMark=this.drawMarkLine(glbH,'井口','#ff0000')
+		this.liquidMark=this.drawMarkLine(glbH,'液面','#00a800')
+		this.soundMark=this.drawMarkLine(glbH,'音标','#00aaff')
+		this.hoopStartMark=this.drawMarkLine(glbH,'起始接箍','#c6c600')
+		this.hoopEndMark=this.drawMarkLine(glbH,'截止接箍','#b99f1e')
+		
+		//提示文本
+		this.tipEl=drawUtils.drawText('',glbW/2,glbH/2,'center|middle')
+		this.tipEl.attr({zlevel:101,style:{fill:'#436cff'}})
+		this.tipEl.hide()
+		this.container.add(this.tipEl)
+		
+		//遮罩层
+		this.mask=new zrender.Rect({
+			cursor:'default',
+			zlevel:100,
+			shape:{
+				x:0,
+				y:0,
+				width:glbW,
+				height:glbH
+			},
+			style: {
+			  fill: 'rgb(0,0,0,.2)', // 填充颜色,默认#000
+			  lineWidth: 0 // 线宽, 默认1
+			}
+		})
+		
+		this.mask.hide()
+		this.container.add(this.mask)
+		
+		//图形区(主要为了能响应鼠标移动事件)
+		let plot = new zrender.Rect({
+			cursor:'default',
+			zlevel:10,
+			shape:{
+				x:0,
+				y:0,
+				width:glbW,
+				height:glbH
+			},
+			style: {
+			  fill: 'transparent', // 填充颜色,默认#000
+			  stroke: '#999', // 描边颜色,默认null
+			  lineWidth: 1 // 线宽, 默认1
+			}
+		})
+		
+		this.container.add(plot)
+		
+		plot.on('mousemove',(event)=>{
+			this.indicateHandle({x:event.offsetX,y:event.offsetX})
+		})
+		
+		/*
+		plot.on("mouseout",(event)=>{
+			//this.parent.sendNotice({show:false})
+		})*/
+		
+		this.drawCoordinate()
+		
+		
+	},
+	
+	drawCoordinate(){
+		this.addChild(drawUtils.drawLine(this.origin.x,this.origin.y,this.axisEnd.x,this.axisEnd.y,{stroke:'#0070a8'}))
+		this.addChild(drawUtils.drawLine(this.origin.x,this.origin.y+this.xAxisGap,this.axisEnd.x,this.axisEnd.y+this.xAxisGap,{stroke:'#999'}))
+	},
+	
+	setData({hoopDatas,liquidDatas,initData}){
+		
+		//将数据的index作为x值,目前单个曲线有5000点,画布width太小会导致数据点堆积
+		let xRange=Math.max(hoopDatas.length,liquidDatas.length)
+		let xAxisLen=this.axisEnd.x-this.origin.x
+		this.xRatio=xAxisLen/xRange
+		
+		let yRange=300  //指定值
+		this.yRatio=this.origin.y/yRange
+		
+		this.hoopSerial.datas=hoopDatas
+		this.liquidSerial.datas=liquidDatas
+		if(!this.hoopSerial.curve){
+			this.hoopSerial.curve=this.drawCurve(hoopDatas,"#009900",this.origin)
+		}
+		else{
+			this.refreshCurve(this.hoopSerial.curve,hoopDatas,this.origin)
+		}
+		if(!this.liquidSerial.curve){
+			this.liquidSerial.curve=this.drawCurve(liquidDatas,"#009900",this.origin2)
+		}
+		else{
+			this.refreshCurve(this.liquidSerial.curve,liquidDatas,this.origin2)
+		}
+		
+		this.setInitData(initData)
+		
+	},
+	
+	//初始化标线位置
+	setInitData(initData){
+		if(!initData || !initData.computeMode){
+			return
+		}
+		if(initData.computeMode=='hoop'){
+			this.setMarkLinePos('hoopStart',initData.hoopStartPos||2)
+			this.setMarkLinePos('hoopEnd',initData.hoopEndPos||2)
+		}
+		else if(initData.computeMode=='sound_mark'){
+			this.setMarkLinePos('sound',initData.soundMarkPos||2)
+		}
+		this.setMarkLinePos('wellHead',initData.wellHeadPos||1)
+		this.setMarkLinePos('liquid',initData.liquidSufacePos||2)
+		
+		if(initData.liquidDepth){
+			this.drawDepthRule(initData.liquidDepth)
+		}
+		
+	},
+	
+	drawCurve(datas,color,origin){  //datas=[[x,y],...[xn,yn]]    动液面数据只有y,x可以自生成
+		let pts=[],pt=null
+		for(let i=0,len=datas.length;i<len;i++){
+			pt=this.getDrawPoint([i,datas[i]],origin)
+			pts.push([pt.x,pt.y])
+		}
+		let curve=drawUtils.drawPolyline(pts,{stroke:color,lineWidth:1})
+		this.addChild(curve)
+		return curve
+	},
+	
+	//刷新曲线-更新曲线对象的数据
+	refreshCurve(curve,datas,origin){
+		let pts=[],pt=null
+		for(let i=0,len=datas.length;i<len;i++){
+			pt=this.getDrawPoint([i,datas[i]],origin)
+			pts.push([pt.x,pt.y])
+		}
+		curve.attr({
+			shape:{
+				points:pts
+			}
+		})
+		curve.show()
+	},
+	
+	//绘制深度标尺
+	drawDepthRule(liquidDepth){
+		this.ruleGroup.removeAll()
+		let whidx=this.wellHeadMark.crtIdx,liqidx=this.liquidMark.crtIdx
+		let points=this.hoopSerial.curve.shape.points
+		let depthpx=points[liqidx][0]-points[whidx][0]  //液面深度对应的像素长
+		let xRatio=liquidDepth/depthpx
+		
+		let rulepx=points[points.length-1][0]-points[whidx][0] //要绘制标尺像素长(基于井口位置)
+		let maxDepth=rulepx*xRatio
+		let {pieceProj,pieceCount}=utils.calcAxis(maxDepth,rulepx)  //utils.calcAxis返回的piecePx 是修正过的,不符合xRatio,要另算
+		let piecePx=pieceProj/xRatio
+		let xpos=points[whidx][0],lab=0
+		for(let i=0;i<=pieceCount;i++){
+			
+			this.ruleGroup.add(drawUtils.drawLineSimple(xpos,this.origin.y,xpos,this.origin.y+5))
+			this.ruleGroup.add(drawUtils.drawText(lab+'',xpos,this.origin.y+6,'center'))
+			lab+=pieceProj
+			xpos+=piecePx
+		}
+	},
+	
+	//绘制标记线:井口、液面、起始接箍、截止接箍、音标
+	drawMarkLine(lineH,name,color){
+		let mark=new zrender.Group()
+		this.container.add(mark)
+		let line=drawUtils.drawLine(0,0,0,lineH,{stroke:color})
+		
+		mark.add(line)
+		let mkname=drawUtils.drawText(name,0,1,'center')
+		mkname.attr({
+			style:{
+				fill:'#fff',
+				x:mkname.style.x-4.5,
+				padding:[4,4,2,4],
+				backgroundColor:color
+			}
+		})
+		
+		mark.add(mkname)
+		mark.hide()
+		return mark
+		
+	},
+	
+	moveMarkLine(markKey){
+		
+	   this.setMarkLinePos(markKey,this.indicateLine.crtIdx)
+	},
+	
+	setMarkLinePos(markKey,idx){
+		let markline=this[markKey+'Mark']
+		let pos=this.hoopSerial.curve.shape.points[idx]
+		markline.setPosition([pos[0],0])
+		markline.dirty()
+		markline.show()
+		markline.crtIdx=idx
+	},
+	
+	indicateHandle({x,y}){
+		if(!this.hoopSerial.curve){
+			console.log('there is no curve exists')
+			return
+		}
+		let idx=utils.getNearestPtIdxByX(this.hoopSerial.curve.shape.points,x)
+		let pos=this.hoopSerial.curve.shape.points[idx]
+		this.indicateLine.attr({
+			shape:{
+				x1:pos[0],
+				x2:pos[0]
+			},
+		})
+		this.indicateLine.crtIdx=idx
+	},
+	
+	reset(){
+		//清除标量线
+		['hoopStart','hoopEnd','sound','wellHead','liquid'].forEach(key=>{
+			this[key+'Mark'].hide()
+		})
+		
+		if(this.hoopSerial.curve){
+			this.hoopSerial.curve.hide()
+		}
+		if(this.liquidSerial.curve){
+			this.liquidSerial.curve.hide()
+		}
+		//清除标尺
+		this.ruleGroup.removeAll() 
+		
+	},
+	
+	getMarkPos(){
+		return {
+			wellHeadPos:this.wellHeadMark.crtIdx,
+			liquidSufacePos:this.liquidMark.crtIdx,
+			soundMarkPos:this.soundMark.crtIdx,
+			hoopStartPos:this.hoopStartMark.crtIdx,
+			hoopEndPos:this.hoopEndMark.crtIdx,
+			datalen:this.hoopSerial.datas.length
+			}
+	},
+	
+	getDrawPoint(projary,origin){
+		let xmin=0,ymin=0
+		return {x:origin.x+(projary[0]-xmin)*this.xRatio,y:origin.y-(projary[1]-ymin)*this.yRatio}
+	},
+	
+	dispose(){
+		if(this.zr){
+			this.zr.dispose()
+		}
+	},
+	resize(){
+		this.zr.resize({
+			width:'auto',
+			height:'auto'
+		})
+	},
+	showTip(msg,showMask){
+		this.tipEl.attr({style:{text:msg}})
+		this.tipEl.show()
+		if(showMask){
+			this.mask.show()
+		}
+	},
+	hideTip(){
+		this.tipEl.hide()
+		this.mask.hide()
+	}
+}
+
+export {LiquidCurve}

+ 81 - 0
src/components/liquidCurve/lib/utils.js

@@ -0,0 +1,81 @@
+
+export default {
+	calcAxis(range,axisLength){
+		let piecePx=20 //
+		let pieceCount=Math.floor(axisLength/piecePx)
+		let pieceProj=range/pieceCount
+		pieceProj=Math.ceil(pieceProj/50)*50
+		
+		pieceCount=Math.ceil(range/pieceProj)
+		piecePx=axisLength/pieceCount
+		return {piecePx,pieceProj,pieceCount}
+	},
+	//查找最接近的点,基于y坐标(曲线数据是按y递增的)
+	getNearestPtIdx(points,pty){
+		let lf=0,rt=points.length-1
+		let mid=0
+		while(lf<rt){
+			mid=Math.floor((lf+rt)/2)
+			if(mid==lf){
+				break
+			}
+			if(mid==rt){
+				break
+			}
+			if(points[mid][1] === pty){
+				return mid
+			}
+			if(points[mid][1] > pty){
+				rt=mid
+			}
+			else if(points[mid][1] < pty){
+				lf=mid
+			}
+		}
+		
+		return Math.abs(points[lf][1]-pty) < Math.abs(points[rt][1]-pty)?lf:rt
+		
+	},
+	
+	//查找最接近的点,基于x坐标(曲线数据是按x递增的)
+	getNearestPtIdxByX(points,ptx){
+		let lf=0,rt=points.length-1
+		let mid=0
+		while(lf<rt){
+			mid=Math.floor((lf+rt)/2)
+			if(mid==lf){
+				break
+			}
+			if(mid==rt){
+				break
+			}
+			if(points[mid][0] === ptx){
+				return mid
+			}
+			if(points[mid][0] > ptx){
+				rt=mid
+			}
+			else if(points[mid][0] < ptx){
+				lf=mid
+			}
+		}
+		
+		return Math.abs(points[lf][0]-ptx) < Math.abs(points[rt][0]-ptx)?lf:rt
+		
+	},
+	
+	
+	
+	getCeilPiece(ary,val){
+		for(let i=0,len=ary.length;i<len;i++){
+			if(ary[i]>=val){
+				return ary[i]
+			}
+		}
+		return ary[ary.length-1]
+		
+	}
+	
+	
+	
+}

+ 137 - 16
src/pages/single/Liquid.vue

@@ -11,7 +11,9 @@
 				:autoLoad="false"
 				:autoLoad="false"
 				>
 				>
 				<template #toolMid>
 				<template #toolMid>
+					<div style="width:180px;">井名:{{queryForm.wellName}}</div>
 					<div style="width:300px">
 					<div style="width:300px">
+						
 						<el-date-picker
 						<el-date-picker
 						        v-model="queryForm.dataTime"
 						        v-model="queryForm.dataTime"
 						        type="datetimerange"
 						        type="datetimerange"
@@ -27,7 +29,7 @@
 					<el-table-column type="index" :index="indexGenerate" label="序号" width="60" align="center" fixed/>
 					<el-table-column type="index" :index="indexGenerate" label="序号" width="60" align="center" fixed/>
 					<el-table-column prop="wellName" label="井名" width="150" fixed/>
 					<el-table-column prop="wellName" label="井名" width="150" fixed/>
 					<el-table-column prop="liquidDepth" label="液面深度(m)" width="120" align="center"/>
 					<el-table-column prop="liquidDepth" label="液面深度(m)" width="120" align="center"/>
-					<el-table-column prop="computeMode" label="计算方法" width="90" align="center"/>
+					<el-table-column prop="computeModeName" label="计算方法" width="90" align="center"/>
 					<el-table-column prop="testTime" label="测试时间" width="160" align="center"/>
 					<el-table-column prop="testTime" label="测试时间" width="160" align="center"/>
 					<el-table-column prop="liquidDepthDev" label="液面深度(m设备)" width="160" align="center"/>
 					<el-table-column prop="liquidDepthDev" label="液面深度(m设备)" width="160" align="center"/>
 					<el-table-column prop="soundSpeedDev" label="音速(m/s设备)" width="120" align="center"/>
 					<el-table-column prop="soundSpeedDev" label="音速(m/s设备)" width="120" align="center"/>
@@ -35,7 +37,7 @@
 					<el-table-column prop="oper" label="操作" width="120" fixed="right">
 					<el-table-column prop="oper" label="操作" width="120" fixed="right">
 						<template #default="scope">
 						<template #default="scope">
 							<div class="tool-column">
 							<div class="tool-column">
-								<el-button type="primary" icon="edit" size="small">手动计算</el-button>
+								<el-button type="primary" icon="edit" size="small" @click="loadLiquid(scope.row.dataId)">手动计算</el-button>
 							</div>
 							</div>
 						    
 						    
 						</template>				
 						</template>				
@@ -46,7 +48,7 @@
 			<div class="curve-toolbar">
 			<div class="curve-toolbar">
 				<el-form :inline="true" :model="toolForm" class="query-form-inline"  label-width="auto" style="width:100%;display:flex;">
 				<el-form :inline="true" :model="toolForm" class="query-form-inline"  label-width="auto" style="width:100%;display:flex;">
 				<el-form-item label="">
 				<el-form-item label="">
-						<el-select v-model="toolForm.computeMode"  placeholder="计算方式" style="width:100px">
+						<el-select v-model="toolForm.computeMode"  placeholder="计算方式" style="width:100px" @change="computeModeChange">
 							<el-option label="接箍法" value="hoop"/>
 							<el-option label="接箍法" value="hoop"/>
 							<el-option label="音速法" value="sound_speed"/>
 							<el-option label="音速法" value="sound_speed"/>
 							<el-option label="音标法" value="sound_mark"/>
 							<el-option label="音标法" value="sound_mark"/>
@@ -68,27 +70,31 @@
 						
 						
 					</el-form-item>
 					</el-form-item>
 					<el-form-item label="计算深度">
 					<el-form-item label="计算深度">
-					  <el-input-number v-model="toolForm.liquidDepth"  :controls="false" placeholder="深度" :min="1" class="tool-input" readonly/><label class="tool-input-unit">(m)</label>
+					  <el-input v-model="toolForm.liquidDepth"  :controls="false" placeholder="深度"  class="tool-input" readonly/><label class="tool-input-unit">(m)</label>
 					</el-form-item>
 					</el-form-item>
-					<el-button type="primary">开始计算</el-button>
-					<el-button type="success">保存</el-button>
+					<el-button type="primary" @click="calculateDepth">开始计算</el-button>
+					<el-button type="success" @click="saveLiquid" :loading="isSaving">保存</el-button>
 				</el-form>
 				</el-form>
 			</div>
 			</div>
-			<div class="liquid-curve">
-				<template v-show="1==1">
-					ddd
-				</template>
+			<div style="width:100%;overflow: auto;">
+				<div class="liquid-curve">
+					<LiquidCurve ref="liquidCurve"/>
+				</div>
 			</div>
 			</div>
+			
 		</div>
 		</div>
 	</div>
 	</div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-	import {reactive,ref,watch,onMounted, nextTick} from 'vue'
+	import {reactive,ref,watch,onMounted, toRaw} from 'vue'
 	import { storeToRefs } from 'pinia'
 	import { storeToRefs } from 'pinia'
 	import { useHomeStore } from '../../store/home.js'
 	import { useHomeStore } from '../../store/home.js'
 	import {ElMessage} from 'element-plus'
 	import {ElMessage} from 'element-plus'
 	import CrudTable from '../../components/crudtable/CrudTable.vue'
 	import CrudTable from '../../components/crudtable/CrudTable.vue'
+	import LiquidCurve from '../../components/liquidCurve/LiquidCurve.vue'
+	import wellLiquidAPI from '../../api/wellLiquid.js'
+	import utils from '../../utils/utils.js'
 	
 	
 	const isQuerying=ref(false)
 	const isQuerying=ref(false)
 	
 	
@@ -101,11 +107,12 @@
 	})
 	})
 	
 	
 	const toolForm=reactive({
 	const toolForm=reactive({
+		dataId:null,
 		computeMode:null,
 		computeMode:null,
 		wellHeadPos:null,
 		wellHeadPos:null,
 		liquidSufacePos:null,
 		liquidSufacePos:null,
-		startHoopPos:null,
-		endHoopPos:null,
+		hoopStartPos:null,
+		hoopEndPos:null,
 		hoopCount:null,
 		hoopCount:null,
 		avgLengthPipe:null,
 		avgLengthPipe:null,
 		soundMarkPos:null,
 		soundMarkPos:null,
@@ -130,6 +137,118 @@
 		
 		
 	}
 	}
 	
 	
+	
+	
+	
+	const liquidCurve=ref(null)
+	
+	const isSaving=ref(false)
+	
+	const loadLiquid=(dataId)=>{
+		liquidCurve.value.reset()
+		wellLiquidAPI.getLiquid(dataId).then(resp=>{
+			if(resp.code!=0){
+				ElMessage.error(resp.msg||'获取动液面数据失败')
+				return
+			}
+			//console.log(resp.data)
+			utils.copyAttr(resp.data,toolForm)
+			let datas={hoopDatas:resp.data.hoopDatas.split(','),liquidDatas:resp.data.liquidDatas.split(',')}
+			let {computeMode,liquidDepth,wellHeadPos,liquidSufacePos,soundMarkPos,hoopStartPos,hoopEndPos}=resp.data
+			datas.initData={computeMode,liquidDepth,wellHeadPos,liquidSufacePos,soundMarkPos,hoopStartPos,hoopEndPos}
+			liquidCurve.value.setData(datas)
+		}).catch(err=>{
+			console.log('get liquid data occur:',err)
+			ElMessage.error('获取动液面数据出错')
+		})
+	}
+	
+	//动液面深度计算
+	const calculateDepth=()=>{
+		let calMode=toolForm.computeMode
+		let marks=liquidCurve.value.getMarkPos()
+		let offset=marks.liquidSufacePos-marks.wellHeadPos
+		let depth=null
+		if(offset<=0){
+			ElMessage.error('请正确设置井口、液面位置')
+			return
+		}
+		if(calMode=="sound_speed"){
+			if(!toolForm.soundSpeed || toolForm.soundSpeed<=0){
+				ElMessage.error('请正确设置音速')
+				return
+			}
+			depth=toolForm.soundSpeed*offset*(toolForm.soundInterval||0.0045)*0.5
+			
+		}
+		else if(calMode=="sound_mark"){
+			let smarkOffset=marks.soundMarkPos-marks.wellHeadPos
+			if(smarkOffset<=0 || marks.soundMarkPos>marks.liquidSufacePos){
+				ElMessage.error('请正确设置音标位置')
+				return
+			}
+			if(!toolForm.soundMarkDepth || toolForm.soundMarkDepth<=0){
+				ElMessage.error('请正确设置音标深度')
+				return
+			}
+			depth=(offset/smarkOffset)*toolForm.soundMarkDepth
+		}
+		
+		else if(calMode=="hoop"){
+			let hoopOffset=Math.abs(marks.hoopEndPos-marks.hoopStartPos)
+			if(marks.hoopEndPos<marks.wellHeadPos || marks.hoopEndPos>marks.liquidSufacePos || marks.hoopStartPos<marks.wellHeadPos || marks.hoopStartPos>marks.liquidSufacePos){
+				ElMessage.error('请正确设置接箍位置')
+				return
+			}
+			if(!toolForm.avgLengthPipe || toolForm.avgLengthPipe<=0){
+				ElMessage.error('请正确设置平均管长')
+				return
+			}
+			if(!toolForm.hoopCount || toolForm.hoopCount<=0){
+				ElMessage.error('请正确设置接箍数')
+				return
+			}
+			depth=toolForm.avgLengthPipe*toolForm.hoopCount
+			depth=(offset/hoopOffset)*depth
+		}
+		
+		
+		if(depth && depth>0){
+			toolForm.liquidDepth=depth.toFixed(2)
+			liquidCurve.value.refreshDepthRule(parseFloat(toolForm.liquidDepth))
+		}
+		
+	}
+	
+	const computeModeChange=(val)=>{
+		toolForm.liquidDepth=null
+		liquidCurve.value.setInitData(toolForm)
+	}
+	
+	const saveLiquid=()=>{
+		isSaving.value=true
+		let posData=liquidCurve.value.getMarkPos()
+		let formData=toRaw(toolForm)
+		Object.assign(formData,posData)
+		wellLiquidAPI.saveLiquid(formData).then(resp=>{
+			isSaving.value=false
+			if(resp.code!=0){
+				ElMessage.error(resp.msg || '数据保存失败')
+			}
+			ElMessage.success('操作成功')
+			tabQuery()
+		}).catch(err=>{
+			isSaving.value=false
+			console.log(err)
+		})
+	}
+	
+	onMounted(()=>{
+		setTimeout(()=>{
+			liquidCurve.value.initResize()
+		},10)
+	})
+	
 	const store = useHomeStore()
 	const store = useHomeStore()
 	const {currentTreeNode} = storeToRefs(store)
 	const {currentTreeNode} = storeToRefs(store)
 	watch(currentTreeNode, (newNode, oldNode) => {
 	watch(currentTreeNode, (newNode, oldNode) => {
@@ -147,7 +266,7 @@
 <style scoped>
 <style scoped>
 	.liquid-page{
 	.liquid-page{
 		width:100%;
 		width:100%;
-		height: 100%;
+		/* height: 100%; */
 		box-sizing: border-box;
 		box-sizing: border-box;
 		display: flex;
 		display: flex;
 		flex-flow:column nowrap;
 		flex-flow:column nowrap;
@@ -176,10 +295,12 @@
 		flex-flow:column nowrap;
 		flex-flow:column nowrap;
 	}
 	}
 	.liquid-tab{
 	.liquid-tab{
-		flex:1;
+		height: calc(100vh - 485px);
 	}
 	}
 	.liquid-curve{
 	.liquid-curve{
-		height:300px;
+		height:320px;
+		width:5000px;
+		overflow: auto;
 	}
 	}
 	
 	
 	.tool-input{
 	.tool-input{

+ 6 - 0
src/utils/utils.js

@@ -55,6 +55,12 @@ export default {
 		})
 		})
 		
 		
 		return {topNodes,mapping}
 		return {topNodes,mapping}
+	},
+	
+	copyAttr(srcObj,targetObj){
+		for(let attr in targetObj){
+			targetObj[attr]=srcObj[attr]
+		}
 	}
 	}