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.
klhis/hcemr_new/d3/partogram/partogram.js

385 lines
14 KiB

/**
* 产程图包括上下两部分通过两个不同的svg来展示
*/
class PartoGram {
constructor(args){
//上半部分Svg
this.topSvg = args.topSvg;
//下半部分Svg
this.bottomSvg = args.bottomSvg;
//svg左边距
this.marginLeft = args.marginLeft;
//svg上边距
this.marginTop = args.marginTop;
//svg右边距
this.marginRight = args.marginRight||40;
//svg下边距
this.marginButtom = args.marginButtom||40;
//d3.domain定义域 X轴
this.Domain_X = args.Domain_X||[0,24];
//上部svg的左边y坐标轴定义域
this.domain_TYL = args.domain_TYL||[0, 10];
//上部svg的右边y坐标轴定义域
this.domain_TYR = args.domain_TYR||[-5, 5];
//下部svg的右边y坐标轴定义域
this.domain_BYL = args.domain_BYL||
['时间','胎心','血压','宫缩','处理','签名','评分','小结','备注',''];
this.domain_scale =[0,1,2,3,4,9,11,12,15,16,17];
this.yCutOut = this.yCutOut||11
//请求后台数据地址
this.url = args.url;
this._scaleTopSvgX = null;//x轴的坐标标尺
this._scaleTopSvgLeftY = null//topSvg左边的Y轴坐标尺
this._scaleTopSvgRightY = null//topSvg右边的Y轴坐标尺
this._scaleBottomSvgY = null;//bottomSvgY轴坐标尺
this._scaleBottomSvgX = null;//bottomSvgX轴坐标尺
this.run();
}
run(){
this.initTopSvgXaxris();
this.initTopSvgY1axris();
this.initTopSvgY2axris();
//下部svg
this.initBottomSvgAxris();
}
initTopSvgXaxris(){
//横坐标的最大长度
let topsvg = d3.select("#"+this.topSvg.id);
let xScaleMax = topsvg.attr("width")-this.marginRight
let xScale = d3.scaleLinear()
//输入范围
.domain(this.Domain_X)
//输出范围
.range([this.marginLeft, xScaleMax]);
this._scaleTopSvgX=xScale;//设置topSvgX轴坐标尺
//纵坐标网格线长度
let xAxisLength = (topsvg.attr("height")-this.marginButtom-this.marginTop)
let xAxis = d3.axisBottom(xScale)
.ticks(this.Domain_X[this.Domain_X.length-1]-this.Domain_X[0]).tickSize(-xAxisLength);
//横坐标在Y方向的偏移量
let yOffest= topsvg.attr("height")-this.marginButtom;
topsvg.append("g").call(xAxis)
.call(this.tasf,0,yOffest);
}
initTopSvgY1axris(){
let topsvg = d3.select("#"+this.topSvg.id);
var range_y = topsvg.attr("height") - this.marginButtom-this.marginTop;
let yScale1 = d3.scaleLinear()
.domain(this.domain_TYL)
.range([topsvg.attr("height") - this.marginButtom, this.marginTop]);
this._scaleTopSvgLeftY = yScale1;
let yAxisLength = (topsvg.attr("width")-this.marginLeft-this.marginRight)
let yAxis1 = d3.axisLeft().scale(yScale1).tickSize(-yAxisLength);
topsvg.append("g")
.call(yAxis1)
.call(this.tasR,90)
.call(this.tasf,this.marginLeft,0);
topsvg.append("g").call(this.textMkr,"宫颈扩张(厘米)红色",this.marginLeft-30,this.marginTop+(range_y/2),"label_lr");
topsvg.append("g").call(this.textMkr,"胎头下降(厘米)蓝色",topsvg.attr("width")-10,this.marginTop+(range_y/2),"label_lr");
}
initTopSvgY2axris(){
let topsvg = d3.select("#"+this.topSvg.id);
let yScale2 = d3.scaleLinear()
.domain(this.domain_TYR)
.range([topsvg.attr("height")- this.marginButtom, this.marginTop]);
this._scaleTopSvgRightY = yScale2;
let yAxis2 = d3.axisRight(yScale2).tickSize(0);
topsvg.append("g")
.call(yAxis2)
.call(this.tasf,topsvg.attr("width")-this.marginRight,0);
}
//初始化下边的svg的y坐标轴
initBottomSvgAxris(){
let bottomsvg = d3.select("#"+this.bottomSvg.id);
let yMax = this.domain_scale[this.domain_scale.length-1];
let ySourceDomainL = [0,yMax];
let yTargetDomain
= [0,bottomsvg.attr("height")-this.marginButtom];
let y2Scale = d3.scaleLinear()
.domain(ySourceDomainL)
.range(yTargetDomain);
this._scaleBottomSvgY = y2Scale;
let yAxisLength = (bottomsvg.attr("width")-this.marginLeft-this.marginRight)
let y2Axis = d3.axisLeft(y2Scale).tickSize(-yAxisLength).tickValues(this.domain_scale)
.tickFormat("");//自定义刻度;
bottomsvg.append("g")
.call(y2Axis)
.call(this.tasf,this.marginLeft,0)
//横坐标的最大长度
let xScaleMax = bottomsvg.attr("width")-this.marginRight;
let xScale = d3.scaleLinear()
//输入范围
.domain(this.Domain_X)
//输出范围
.range([this.marginLeft, xScaleMax]);
this._scaleBottomSvgX = xScale;
//纵坐标网格线长度
let xAxisLength = (bottomsvg.attr("height")-this.marginButtom)
let xAxis = d3.axisTop(xScale)
.ticks(this.Domain_X[this.Domain_X.length-1]-this.Domain_X[0]).tickSize(-xAxisLength*this.yCutOut/yMax).tickSizeOuter(-xAxisLength);
//横坐标在Y方向的偏移量
bottomsvg.append("g").call(xAxis);
//画最后一行的竖线
var x_range = this.Domain_X[this.Domain_X.length-1]
var df = Math.round(x_range/3);//
var ds =[[df,yMax-1],[df,yMax]];
this.lineMkr(xScale,y2Scale,bottomsvg,ds);
ds =[[df*2,yMax-1],[df*2,yMax]];
this.lineMkr(xScale,y2Scale,bottomsvg,ds);
var yMax_range = bottomsvg.attr("height")-this.marginButtom;
let minimum_sc = yMax_range/yMax
bottomsvg.append("g").call(this.textMkr,"时间",this.marginLeft-20,8+minimum_sc-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"胎心",this.marginLeft-20,8+minimum_sc*2-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"血压",this.marginLeft-20,8+minimum_sc*3-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"宫缩",this.marginLeft-20,8+minimum_sc*4-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"处",this.marginLeft-20,8+minimum_sc*5-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"理",this.marginLeft-20,8+minimum_sc*7-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"签",this.marginLeft-20,8+minimum_sc*10-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"名",this.marginLeft-20,8+minimum_sc*11-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"评分",this.marginLeft-20,8+minimum_sc*12-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"小结",this.marginLeft-20,8+minimum_sc*14-minimum_sc/2,"label_tb");
bottomsvg.append("g").call(this.textMkr,"备注",this.marginLeft-20,8+minimum_sc*16-minimum_sc/2,"label_tb");
var ys_xy =[[0,yMax]];
ys_xy = ys_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] );
bottomsvg.append("g")
.selectAll("#ys")
.data(ys_xy).enter()
.call(this.textTransMkr,"医生:",d => d[0],d => d[1]-12);
var jcz_xy =[[df,yMax]]
jcz_xy =jcz_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] );
bottomsvg.append("g")
.selectAll("#jcz")
.data(jcz_xy).enter()
.call(this.textTransMkr,"接产者:",d => d[0],d => d[1]-12);
var xh_xy =[[df*2,yMax]]
xh_xy =xh_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] );
bottomsvg.append("g")
.selectAll("#xhys")
.data(xh_xy).enter()
.call(this.textTransMkr,"巡回:",d => d[0],d => d[1]-12);
//画斜线,血压
var domain_X = this.Domain_X;
var domain_Y = this.domain_scale;
let bp_Y1 = domain_Y[3];
let bp_Y2 = domain_Y[2];
for(let i =domain_X[0];i<domain_X[domain_X.length-1];i++){
var ds =[[i,bp_Y1],[i+1,bp_Y2]];
this.lineMkr(xScale,y2Scale,bottomsvg,ds);
}
//宫缩
let uc_Y1 = domain_Y[2];
let uc_Y2 = domain_Y[1];
for(let i =domain_X[0];i<domain_X[domain_X.length-1];i++){
var ds =[[i,uc_Y1],[i+1,uc_Y2]];
this.lineMkr(xScale,y2Scale,bottomsvg,ds);
}
}
/**
*
* @param {x坐标系} xScale
* @param {y坐标系} y2Scale
* @param {选择集} sel
* @param {数据数组} ds
*/
lineMkr(xScale,yScale,sel,ds){
ds = ds.map(d =>[ xScale(d[0]), yScale(d[1])]);
let lnMkr = d3.line();
sel.append("g")
.append("path")
.attr("d",lnMkr(ds))
.attr("fill","none")
.attr("stroke","red");
}
textMkr(sel,label,x,y,style){
let label_ = d3.local(),dsX=d3.local(),dsY =d3.local();
sel.each(function(d ,i){
label_.set(this,typeof label==="function"?label(d,i):label||0)
});
sel.each(function(d ,i){
dsX.set(this,typeof x==="function"?x(d,i):x||0)
});
sel.each(function(d ,i){
dsY.set(this,typeof y==="function"?y(d,i):y||0)
});
sel.append("text")
.attr("x",function(){return dsX.get(this)}||0)
.attr("y",function(){return dsY.get(this)}||5)
.classed(style||"label_tb",true)
.text(function(){return label_.get(this)});
}
textTransMkr(sel,label,x,y){
sel.append("text")
.attr("x",x||0)
.attr("y",y||5)
.attr("transform","translate(5,-2)")
.text(label?label:d=>d);
}
//transform的公用方法
tasf(sel,dx,dy){
var dxs=d3.local(), dys=d3.local();
sel.each(function(d ,i){
dxs.set(this,typeof dx==="function"?dx(d,i):dx||0)
});
sel.each(function(d,i){
dys.set(this,typeof dy==="function"?dy(d,i):dy||0)
});
return sel.attr("transform",function(){
return "translate("+dxs.get(this)+","+dys.get(this)+")"
})
}
//旋转角度
tasR(sel,r){
var dr = d3.local();
sel.each(function(d ,i){
dr.set(this,typeof r==="function"?r(d,i):r||0)
});
return sel.attr("transform",function(){
return "rotate("+dr.get(this)+")"
})
}
//对外接口-----begin
setBottomTextStr(ds,x,y,xOffset=0.1,yOffest=-0.1,style){
let xSc = this._scaleBottomSvgX ;//bottomSvgX轴坐标尺
let ySc = this._scaleBottomSvgY ;//bottomSvgY轴坐标尺
let svg = d3.select("#"+this.bottomSvg.id);
svg.append("g")
.append("text")
.classed(style,true)
.attr("x",xSc(x+xOffset))
.attr("y",ySc(y+yOffest))
.text(ds);
}
//设置普通文本,通过svg来画
setText(id,ds,x,y,style){
let svg = d3.select("#"+id);
svg.append("g")
.append("text")
.classed(style,true)
.attr("x",x)
.attr("y",y)
.text(ds);
}
//
/**
* 宫颈扩张
* @param {ds格式必须是[[0,1],[1,2]]二维数组} ds
* @param {点和线的颜色} color
* @param {点的半径} r
*/
setDilatationCervix(ds,color,r){
let len = ds.length;
var resDs = []
for(let i =0;i<len;i++){
//新数组,x坐标,y坐标,坐标上的值
let newArr =[i,ds[i]];
resDs.push(newArr);
}
let xSc = this._scaleTopSvgX;
let ySc = this._scaleTopSvgLeftY;
resDs = resDs.map(d => [ xSc(d[0]), ySc(d[1]) ] );
let topsvg = d3.select("#"+this.topSvg.id);
topsvg.append("g")
.selectAll("circle")
.data(resDs).enter()
.append("circle").attr("r",r||4)
.attr("cx",d =>d[0]).attr("cy",d => d[1])
.style("fill",color||"blue")
// .style("stroke", "red"); // 8
var lnMaker = d3.line();
topsvg.append("g").append("path")
.attr("d",lnMaker(resDs))
.attr("fill","none")
.attr("stroke",color||"blue");
}
/**
* 胎头下降
@param {ds格式必须是[[0,1],[1,2]]二维数组} ds
* @param {点和线的颜色} color
* @param {点的半径} r
*/
setFetalHeadDescent(ds,color,r){
let len = ds.length;
var resDs = []
for(let i =0;i<len;i++){
//新数组,x坐标,y坐标,坐标上的值
let newArr =[i,ds[i]];
resDs.push(newArr);
}
let xSc = this._scaleTopSvgX;
let ySc = this._scaleTopSvgRightY;
resDs = resDs.map(d => [ xSc(d[0]), ySc(d[1]) ] );
let topsvg = d3.select("#"+this.topSvg.id);
topsvg.append("g")
.selectAll("circle")
.data(resDs).enter()
.append("circle").attr("r",r||4)
.attr("cx",d =>d[0]).attr("cy",d => d[1])
.style("fill",color||"red")
// .style("stroke", "red"); // 8
var lnMaker = d3.line();
topsvg.append("g").append("path")
.attr("d",lnMaker(resDs))
.attr("fill","none")
.attr("stroke",color||"red")
.attr("stroke-dasharray","5,5")
}
/**
*
* @param {数据来源,按一定顺序的一维数组} ds
* @param {在y轴的那个地方显示} y_axis
* @param {偏差} offset
* @param {文本样式} style
*/
setTextData (ds,y_axis,xOffset,yOffset,style){
//把一维数组转化为二维数组
let len = ds.length;
var resDs = []
for(let i =0;i<len;i++){
//新数组,x坐标,y坐标,坐标上的值
let newArr =[i+xOffset,y_axis+yOffset,ds[i]];
resDs.push(newArr);
}
let bottomsvg = d3.select("#"+this.bottomSvg.id);
let scaleX = this._scaleBottomSvgX;
let scaleY = this._scaleBottomSvgY;
let scaleXy = resDs.map(d=>[scaleX(d[0]),scaleY(d[1]),d[2]])
bottomsvg.append("g")
.selectAll("#data")
.data(scaleXy).enter()
.append("text")
.attr("x",d =>d[0])
.attr("y",d =>d[1])
.classed(style,true)
.text(d =>d[2]);
}
}