class TwdToWatData{ constructor(event,data){ this.event = event; this.data = data ; } static GetQueryString(name){ var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null){ return unescape(r[2]); } return null; } static feedbackDataToWat(data){ return window.parent.top.DxURLPage.action(TwdToWatData.GetQueryString('strwinid'),TwdToWatData.GetQueryString('strctrid'),data); } } class TemperatureList{ constructor(args){ this.width = args.width||1000; this.height = args.height||1000; this.leftPadding = args.leftPadding||150; this.topPadding = args.topPadding||150; this.rightPadding = args.rightPadding||10; this.bottomPadding = args.bottomPadding||20 this.showScale = args.showScale||false; this.halveNum= 0; this.tempListYdata=null; this.xScale = null; this.xAxis = null; this.yAxis = null; this.yScale = null; this.svg = d3.select("#scd") .append('g').attr("id", "g1"); this.widthActual = null; this.xDomain = 7; this.xStartIndex = 2; this.xEndIndex = 8; this.widthSm = null; this.isNumber = function(obj){ let vRegx = /[0-9]/; return vRegx.test(obj); } this.makeLine = function(svg,xSc,ySc,x1,y1,x2,y2){ svg.append("line") .attr("x1",xSc(x1+0.5)) .attr("y1",ySc(y1)) .attr("x2",xSc(x2+0.5)) .attr("y2",ySc(y2)) .attr("style","stroke:rgb(255,0,0);stroke-width:1") .classed("outerData",true); } this.datePonits=null; this.tips = this.svg.append ('g') .attr ('class', 'tips') .style('display','block') // this.tipsRect = this.tips.append ('path') // .attr ('class' , 'tips-border' ) // .attr ('d' , 'm5.99786,9.79993l0,0c0,-2.65063 2.97544,-4.79938 6.64583,-4.79938l3.02083,0l0,0l14.49999,0l27.18748,0c1.76259,0 3.45298,0.50565 4.69932,1.40571c1.24633,0.90006 1.94652,2.1208 1.94652,3.39368l0,11.99845l0,0l0,7.19907l0,0c0,2.65063 -2.97544,4.79938 -6.64583,4.79938l-27.18748,0l-18.94245,12.20371l4.44246,-12.20371l-3.02083,0c-3.67039,0 -6.64583,-2.14875 -6.64583,-4.79938l0,0l0,-7.19907l0,0l0,-11.99845z' ) // .attr('fill','none') // .attr("opacity","0.3") this.tipsRect = this.tips.append('rect') .attr('width','115px') .attr('height','75px') .attr('rx','5') .attr('fill','none') this.tipsText = this.tips.append ('text') .attr('class' ,'tips-text') .attr( 'x' , 6 ) .attr( 'y' , 25 ) .attr( 'fill',"white") .attr('font-size','14px') .text ( '' ) ; this.render(); } //入口 render(){ const _this = this d3.json('./TemperatureList.json?17').then(function(data){ _this.tempListYdata = data.content _this.date=data.date; data.content.forEach(function(dataSingle){ _this.halveNum += dataSingle.length }) //图表框架 _this.initXaxris() _this.initYaxris() _this.initDatePoint(); _this.initXGrid() _this.initYGrid() //计算整体网格宽度 _this.calculateTotalWidth() //特定网格 _this.initLine() //表头的线 _this.initTrLine() //表头的文字 _this.initTrText() //红色线的渲染 _this.redLineInit() _this.initTemperatureScale(); //渲染完毕后回调给框架,接受后台数据 let cbData = new TwdToWatData("load",""); TwdToWatData.feedbackDataToWat(JSON.stringify(cbData)); //渲染完毕后回调给框架,接受后台数据 }) } //渲染表格 initXaxris(){ this.xScale = d3.scaleLinear() //输入范围 .domain([0,this.xDomain+1]) //输出范围 .range([this.leftPadding,this.width-this.rightPadding]); this.xAxis = d3.axisTop().scale(this.xScale).ticks(this.xDomain).tickSize(0) if(!this.showScale){ this.xAxis.tickFormat("") } this.svg.append('g') .call(this.xAxis) .classed('axis_x', true) .attr('transform',`translate(0,${this.topPadding})`) } initYaxris(){ this.yScale = d3.scaleLinear() //输入范围 .domain([0,this.halveNum]) //输出范围 .range([this.topPadding,this.height-this.bottomPadding]); this.yAxis = d3.axisLeft() .scale(this.yScale) .ticks(this.halveNum) .tickSize(0) if(!this.showScale){ this.yAxis.tickFormat("") } this.svg.append('g') .call(this.yAxis) .classed('axis_y', true) .attr('transform',`translate(${this.leftPadding},0)`) } //渲染日期时间点,和备注 initDatePoint(){ let xSc = this.xScale; let ySc = this.yScale; let svg = this.svg; //初始化日期点 let datePoints =this.date; for(let i =1;i<8;i++){ let resDs = datePoints.map((d,index )=>[d>9?xSc(i+(index)/6):xSc(i+(index+0.3)/6),ySc(3.8),d]); svg.append('g') .selectAll("#date").data(resDs).enter() .append("text") .attr("x",d=>d[0]) .attr("y",d=>d[1]) .attr("style","font-size:12px;") .text(d=>d[2]); } svg.append('g') .append("text") .attr("x",xSc(0)) .attr("y",ySc(this.halveNum+1)) .attr("style","font-size:14px;") .text("腋:"); svg.append("use") .attr("href","#T") .attr("transform","translate("+xSc(0.4)+","+ySc(this.halveNum+1-0.4)+")") svg.append('g') .append("text") .attr("x",xSc(0.5)) .attr("y",ySc(this.halveNum+1)) .attr("style","font-size:14px;") .text("脉搏:"); svg.append("use") .attr("href","#P") .attr("transform","translate("+xSc(0.9)+","+ySc(this.halveNum+1-0.2)+")") svg.append('g') .append("text") .attr("x",xSc(1)) .attr("y",ySc(this.halveNum+1)) .attr("style","font-size:14px;") .text("心率:"); svg.append("use") .attr("href","#HR") .attr("transform","translate("+xSc(1.4)+","+ySc(this.halveNum+1-0.2)+")") } //渲染网格 initXGrid(){ d3.selectAll(".axis_x .tick") .append("line") .classed("grid-line", true) .attr('stroke', 'black') .attr('x1',0) .attr( 'y1',0) .attr( 'x2',0) .attr('y2',this.height-this.topPadding-this.bottomPadding) } initYGrid(){ d3.selectAll(".axis_y .tick") .append("line") .classed("grid-line", true) .attr('stroke', 'black') .attr('x1',0) .attr( 'y1',0) .attr( 'x2',(this.width-this.rightPadding-this.leftPadding)) .attr( 'y2',0) } //渲染特定网格 initLine(){ for(let i=this.xStartIndex;i<=this.xEndIndex;i++){ let startY = d3.select(`.axis_x .tick:nth-of-type(${i})`)._groups[0][0].transform.animVal[0].matrix.e for(let j=4;j<49;j++){ this.initYLine(startY,this.widthSm,j,j<46?'darkgray':'black',j<48?6:2) } } for(let j=4;j<46;j++){ this.initXLine(this.widthSm,j,'darkgray',1) } for(let j=5;j<46;j++){ d3.select(`.axis_y .tick:nth-of-type(${j})`).attr('opacity',0) } d3.select('.axis_y .tick:nth-of-type(47)').attr('opacity',0) d3.select('.axis_y .tick:nth-of-type(56)').attr('opacity',0) } initXLine(w,n){ let appendYEle = d3.selectAll('.axis_y') let xStart = d3.select(`.axis_y .tick:nth-of-type(${n})`)._groups[0][0].transform.animVal[0].matrix.f let xEnd = d3.select(`.axis_y .tick:nth-of-type(${(n+1)}`)._groups[0][0].transform.animVal[0].matrix.f appendYEle.append('g') .attr('transform',`translate(${w},${((xEnd-xStart)+xStart)})`) .append('line') .attr('fill','none') .attr('stroke',n===30?'red':((n===5||(n-5)%5===0)?'black':'darkgray')) .attr('x1',0) .attr('y1',0) .attr('x2',this.widthActual-w) .attr('y2',0) } initYLine(y,w,n,color,yNum){ let appendXEle = d3.selectAll('.axis_x') let x = d3.select(`.axis_y .tick:nth-of-type(${n})`)._groups[0][0].transform.animVal[0].matrix.f let xEnd = d3.select(`.axis_y .tick:nth-of-type(${(n+1)})`)._groups[0][0].transform.animVal[0].matrix.f for(let i=1;i=40;i=i-20){ this.appendText(`${i}`,(this.widthSm/2-this.widthSm/4+this.leftPadding),this.calculateHeight(j)) j=j+(i===180?4:5) } //体温 this.appendText('体 温',(this.widthSm/2+this.widthSm/4+this.leftPadding),this.calculateHeight(5)) for(let i=42;i>=35;i--){ this.appendText(`${i}`,(this.widthSm/2+this.widthSm/4+this.leftPadding),this.calculateHeight(k)) k=k+5 } } //计算特定网格里文字的y定位 calculateHeight(n){ return (this.height-this.topPadding-this.bottomPadding)/this.halveNum*n-5 } //渲染特定网格里文字方法的封装 appendText(name,transWidth,transHeight){ this.svg.select("#g1 .axis_x").append('g') .attr('transform', `translate(${transWidth},${transHeight})`) .append("text").text(`${name}`) .attr("style","font-size:12px;") .attr('fill','black') .attr('stroke','none') } //计算可视区域网格的宽度 calculateTotalWidth(){ let xStart = d3.select('.axis_x .tick:nth-of-type(1)')._groups[0][0].transform.animVal[0].matrix.e let xEnd = d3.select(`.axis_x .tick:nth-of-type(${(this.xDomain+2)}`)._groups[0][0].transform.animVal[0].matrix.e this.widthActual = xEnd - xStart; this.widthSm = this.widthActual/(this.xDomain+1) } //红色线的处理 redLineInit(){ for(let i=3;i<=this.xDomain+1;i++){ d3.select(`.axis_x .tick:nth-of-type(${i}) .grid-line`) .attr('stroke','red') } } /** * 初始化体温的坐标系 */ initTemperatureScale(){ //划线部分的X轴坐标 let begin = this.leftPadding + (1/(this.xDomain+1))*(this.width-this.rightPadding-this.leftPadding) this.dateScale = d3.scaleLinear() //输入范围 .domain([0,this.xDomain*6]) //输出范围 .range([begin,this.width-this.rightPadding]); this.dateXAxis = d3.axisTop().scale(this.dateScale).ticks(this.xDomain*6); let translateY = this.topPadding + (45/this.halveNum)*(this.height-this.bottomPadding-this.topPadding) this.svg.append('g') .call(this.dateXAxis) .classed('axis_x', true) .attr('transform',"translate(0,"+translateY+")") .attr("opacity",this.showScale?"1":"0") //体温对应的Y轴坐标系 let temYEnd = this.topPadding + (5/this.halveNum)*(this.height-this.bottomPadding-this.topPadding) this.temYScale = d3.scaleLinear() .domain([34,42]) .range([translateY,temYEnd]).clamp(true); this.temYAxis = d3.axisRight().scale(this.temYScale).ticks(42-34); this.svg.append('g') .call(this.temYAxis) .classed('axis_x', true) .attr('transform',"translate("+begin+",0)") .attr("opacity","0") //脉搏对应的Y轴坐标系 this.pulseYScale = d3.scaleLinear() .domain([20,180]) .range([translateY,temYEnd]).clamp(true); this.pulseYAxis = d3.axisRight().scale(this.pulseYScale).ticks(8); this.svg.append('g') .call(this.pulseYAxis) .classed('axis_x', true) .attr('transform',"translate("+begin+",0)") .attr("opacity","0") } /***对外接口begin 所有外部绘制的点都要做标记class="outerData"*/ /** * * @param {腋表温度} ds * @param {叉号有偏移量,是叉号的分支的一半长度} offset */ makeTempCrossLine(ds,offset){ let len = ds.length; var resDs = []; var physicalCool = []; for(let i =0;i [ xSc(d[0]+0.5), ySc(d[1]) ,d[1]] ); let svg = this.svg; let tips = this.tips let tipsText = this.tipsText; let tipsRect = this.tipsRect; let tems = svg.append("g") .selectAll("bcuse").data(resDs).enter() .append("use") .attr("href","#T") .attr("data-y",d =>d[2]) .attr("transform",d=>"translate("+(d[0]-offset)+","+(d[1]-offset)+") ") .attr("opacity",d =>d[1]==undefined ? 0 : 1) .classed("outerData",true) .attr('cursor','pointer') .on("mousemove",function(e){ // var pt = d3.pointer( e,tems.node() ); // console.log(pt,e.target.dataset.y); tipsText.text ("体温:"+e.target.dataset.y +"℃") ; tips.attr ('transform', 'translate(' + (e.pageX-10) + ',' + (e.pageY-50) + ')' ) tipsRect.attr("fill","rgb(111,111,111)") tips.style('display', 'block'); }) .on ('mouseout' , function () { tips.style('display', 'none'); }) var lnMaker = d3.line().defined((d,i) => d[1]==undefined ? false : true); svg.append("g").append("path") .attr("d",lnMaker(resDs)) .attr("fill","none") .attr("stroke","blue") .classed("outerData",true); //绘制物理降温 if(physicalCool.length>0){ physicalCool.forEach((d)=>{ //绘制虚线 let pcResDs = [[xSc(d[0]+0.5),ySc(d[1])],[xSc(d[0]+0.5),ySc(d[2])]]; svg.append("g").append("path") .attr("d",lnMaker(pcResDs)) .attr("fill","none") .attr("stroke-dasharray","5,5") .attr("stroke","red") .classed("outerData",true); //绘制红色圆圈 let rhcDs = [xSc(d[0]+0.5),ySc(d[2])]; svg.append("g") // .selectAll("bcuse").data(rhcDs).enter() .append("circle").attr("r",4) .attr("cx",rhcDs[0]).attr("cy",rhcDs[1]) .style("fill","none") .attr("stroke","red") .classed("outerData",true) }); } } /** * * @param {传入数据,42个 {"y":100,type:"P"}的数组} ds */ makePulseCrossLine(ds){ let len = ds.length; var resDs = [] for(let i =0;i [ xSc(d[0]+0.5), ySc(d[1]), d[2] ,d[1]] ); let svg = this.svg; let tips = this.tips let tipsText = this.tipsText; let tipsRect = this.tipsRect; let tems = svg.append("g") .selectAll("bcuse").data(resDs).enter() .append("use") .attr("href",d =>d[2]) .attr("data-y",d =>("#P"==d[2]?"P:":"HR:")+d[3]) .attr("transform",d=>"translate("+(d[0])+","+(d[1])+") ") .attr("opacity",d =>d[1]==undefined ? 0 : 1) .classed("outerData",true) .on("mousemove",function(e){ // var pt = d3.pointer( e,tems.node() ); // console.log(pt,e.target.dataset.y); tipsText.text (e.target.dataset.y) ; tips.attr ('transform', 'translate(' + (e.pageX-10) + ',' + (e.pageY-50) + ')' ) tipsRect.attr("fill","rgb(128,0,128)") tips.style('display', 'block'); }) .on ('mouseout' , function () { tips.style('display', 'none'); }) var lnMaker = d3.line().defined((d,i) => d[1]==undefined ? false : true);; svg.append("g").append("path") .attr("d",lnMaker(resDs)) .attr("fill","none") .attr("stroke","red") .classed("outerData",true) } //设置患者的基本信息 setText(x,y,label,style){ let xScal = this.xScale; let yScal = this.yScale; let svg = this.svg; svg.append("text") .attr("x",xScal(x)) .attr("y",yScal(y)) .classed("outerData",true) .classed(style,true) .text(label); } /** * * @param {数组} ds * @param {y轴的坐标} y * @param {文本样式} style */ setTextData(ds,y,style){ let xScal = this.xScale; let yScal = this.yScale; if(ds){ let resDs = ds.map((d,i) => [ d.length >= 14 ? xScal(i+1): xScal((i + 1.45) - 0.45 / 13 * d.length), yScal(y-0.1), d] ); let svg = this.svg; svg.append("g") .selectAll("#id").data(resDs).enter() .append("text") .attr("x",d=>d[0]) .attr("y",d=>d[1]) .classed("outerData",true) .classed(style,true) .text(d=>d[2]); } } /** * * @param {数组} ds * @param {y轴的坐标} y * @param {文本样式} style */ setTextDataToPs(ds,y,style){ let xScal = this.xScale; let yScal = this.yScale; if(ds){ let resDs = ds.map((d,i) => [ d.length >= 14 ? xScal(i+1): xScal((i + 1.45) - 0.45 / 13 * d.length), yScal(y-0.1), d] ); let svg = this.svg; svg.append("g") .selectAll("#id").data(resDs).enter() .append("text") .attr("x",d=>d[0]) .attr("y",d=>d[1]) .classed("outerData",true) .classed(style,true) .text(d=>d[2]); } } /** * * @param {[{"x":6,"value":"89","position":2},{"x":7,"value":"89","position":1}]} ds 呼吸记录 * @param {如果position =1,取y1} y1 * @param {如果position =2,取y2} y2 */ setRrecord(ds,y1,y2,style){ let xScal = this.xScale; let yScal = this.yScale; let resDs = ds.map((d,i) => [ xScal((d.x+6)/6), d.position==1?yScal(y1):yScal(y2), d.value] ); let svg = this.svg; svg.append("g") .selectAll("#id").data(resDs).enter() .append("text") .attr("x",d=>d[0]) .attr("y",d=>d[1]) .classed("outerData",true) .classed(style,true) .text(d=>d[2]); } setBPrecord(ds,y1,y2,style){ let svg = this.svg; let xScal = this.xScale; let yScal = this.yScale; var resDs = []; let DLT ="▦"; if(ds){ for(let i =0;id[0]) .attr("y",d=>d[1]) .classed("outerData",true) .classed(style,true) .text(d=>d[2]); } /** * * @param {*} ds * @param {*} y1 文字从y轴哪个位置开始绘制 * @param {*} y2 竖线从y轴哪个位置开始绘制 * @param {*} style */ setEvent(ds,y1,y2,style){ let xSc = this.dateScale; let ySc = this.temYScale; let svg = this.svg; if(ds){ let len = ds.length; for(let i =0;i this.isNumber(d1[2])?xSc(d1[0]+0.2):xSc(d1[0])) .attr("y",d1=>ySc(y1-(d1[1]+1)/5)) .classed("outerData",true) .classed(style,true) .attr("fill","red") .text(d1=>d1[2]); if(evtLen>0){ svg.append("line") .attr("x1",xSc(x+0.5)) .attr("y1",ySc(y2-(evLen)/5)) .attr("x2",xSc(x+0.5)) .attr("y2",ySc(y2-(evLen+2)/5)) .attr("style","stroke:rgb(255,0,0);stroke-width:1") .classed("outerData",true); } } } } /** * * @param {*} ds * 房颤 */ setPHrecord(ds){ let svg = this.svg; let xSc = this.dateScale; let ySc = this.pulseYScale; if(ds){ for(let i =0 ;i{ if(am.indexOf("+")>-1 || am.indexOf("-")>-1){//如果是阳性 let amiArr = am.split(""); for(let m =0;m0){ if(escape(amiArr[m-1]).indexOf( "%u" )<0){ // offX = 13.5; offX = 10.5; } } textSpan.append("tspan").text(ami) .attr("x",xScal(i+1)+m*(offX)) .attr("y",yScal(y-0.5)+(index==0?3:fs||14)) .attr("style",ami=="+"?"fill:red;":"fill:blue;") .classed(style,true) .classed("outerData",true) } }else{ textSpan.append("tspan").text(am) .attr("x",xScal(i+1)) .attr("dy",index==0?3:fs||14) .attr("style","fill:red;") .classed(style,true) .classed("outerData",true) } }); } } } } removeOuterData(){ let svg = this.svg; svg.selectAll(".outerData").remove(); } /**对外接口end */ }