项目背景
医院医护项目需求,需要用H5做一个通用的体温单
项目线上链接
http://120.238.239.27:9091/temperature/#/?PatientId=595603&VisitId=1&StartTime=2020-12-01
其中链接末尾参数PatientId,VisitId,StartTime可以根据需要可以动态配置,项目里会根据这些参数请求平台接口数据进行展示。
项目代码简介
由vue-cli4脚手架快速搭建生成,主要代码都在thermometer.vue文件里面,后续修改也主要是在这个文件修改。
项目需求难点在中间的网格部分,主要使用到的依赖包是一个canvas类库zrender。
(1)画网格
画竖线:根据开始时间和结束时间,还有网格最小时间间隔计算出竖线的总条数,根据竖线总条数和每条线的间隔使用遍历画线
yLine() {
const totalLine = (this.yRange[1] - this.yRange[0]+1) + (this.yRange[1]-this.yRange[0]) * 4
let preSpace = 0
for (let i = 0; i < totalLine; i++) {
const isBreak = i%5 === 0 && i > 0 && i < totalLine - 1
const lineWidth = isBreak ? 2 : 1
const params = {
x1: 0,
y1: preSpace,
x2: this.areaWidth,
y2: preSpace,
lineWidth,
color: isBreak ? '#000' : '#8B8B8B'
}
preSpace += lineWidth + this.ySpace
this.createLine(params)
}
},
画横线:和画竖线同理,根据最小体温和最大体温来画
(2)根据数据在网格描点
x轴坐标:根据时间点time_point和最大最小时间的间隔计算出百分比,映射到x轴的长度计算出相应的偏移像素值,偏移像素值就是画布上的x轴坐标
// 根据时间点计算横坐标
getXaxis(time) {
return (this.getTimeStamp(time)-this.getTimeStamp(this.timeRange[0]))/(this.getTimeStamp(this.timeRange[1])-this.getTimeStamp(this.timeRange[0])) * this.areaWidth
},
y轴坐标:和计算x轴坐标同理,如体温的话就根据最大最小体温去计算百分比
(3)将多个点用线连接起来形成折线效果
在遍历数组数据描点的同时,将前一个点作为起点,后一个点作为终点,描点同时画线即可
(4)表顶注释和表底注释
因为注释文字要显示在网格里,所以用time_point计算x轴坐标的时候需要先计算出上一个距离最近的时间分割点,因为时间分割点的x轴坐标恰好落在竖线上,所以加多一个固定的间距即可调整到网格中间
// 算出上一个分割时间点
getSplitTime(time) {
const hour = Number(time.slice(-8,-6))
let splitHour = hour - hour % 4
splitHour = splitHour < 10 ? `0${splitHour}` : String(splitHour)
return `${time.slice(0, -8)}${splitHour}:00:00`
},
(5)手术或产后天数
这个需求计算有点复杂,第一次手术需要显示0天,之后一直显示到术后10天,即0-10,插入第二次手术的话会中断成显示II-0…II-10,插入第三次变成III-0…III-10以此类推,主要计算逻辑在这里
formatOperateDateList() {
return this.dateList.map(x => {
if (this.dayInterval(x, this.parseTime(new Date(), '{y}-{m}-{d}')) > 0) return ''
if (!this.operateDateList.length) return ''
// 构造天数差数组
const days = this.operateDateList.map(y => {
return this.dayInterval(x, y)
})
if (days.every(z => z < 0)) return ''
// 找到前一次手术(最后一次天数差是正整数的地方)
let index = 0
for (let i = 0; i < days.length; i++) {
if (days[i] >= 0) index = i
}
if (days[index] <= 10) {
return index === 0 ? days[index] : `${this.numToRome(index+1)}-${days[index]}`
} else {
return ''
}
})
},
(6)表底的数据填入(呼吸、入量那些)
根据最大时间和最小时间,做一个累加遍历,根据时间点time_point判断数据落点,构造一个新的渲染数组。
// 计算底部数据的渲染列表
getFormatList({ timeRange, tList, timeInterval = 24*60*60*1000 }) {
const timeNumRange = timeRange.map(x => this.getTimeNum(x))
const list = []
const targetList = [...tList]
for (let i = timeNumRange[0]; i < timeNumRange[1]; i+= timeInterval) {
const item = { timeNum: i, value: '' }
for (let j = targetList.length - 1; j >=0; j-- ) {
const timeNum = this.getTimeNum(targetList[j].time)
if (timeNum >= i && timeNum < i + timeInterval) {
item.value = targetList[j].value
targetList.splice(j, 1)
break;
}
}
list.push(item)
}
return list
}
(7)分页
因为一页最多能显示7天,如果时间点time_point跨度比较大,大于7天就需要分页展示,因为开始时间固定为入院时间,所以根据入院时间累加7天,保证每个time_piont都能包含在内,在累加的过程中同时构造每一页的最大最小时间跨度,后续点击分页的时候只需修改时间跨度就行
// 计算最大标识时间
const maxTimeNum = Math.max.apply(null, vitalSigns.map(x => new Date(x.time_point).getTime()))
const admissionDateNum = new Date(`${this.patInfo.admission_date.slice(0, 10)} 00:00:00`).getTime()
// 根据入院时间和最大标识时间计算出页数和每页的时间范围
const dateRangeList = []
for (let i = admissionDateNum; i < maxTimeNum; i += 7 * 24 * 60 * 60 * 1000) {
dateRangeList.push([this.parseTime(i, '{y}-{m}-{d}'), this.parseTime(i+6 * 24 * 60 * 60 * 1000, '{y}-{m}-{d}')])
}
this.dateRangeList = dateRangeList
this.pageTotal = dateRangeList.length
现在分页是做在体温单下面,默认隐藏,如果超出1页就会下面显示出来。
后续如果需要在iframe外面做分页操作,如果跨域则需要用postMessage等方法进行页面通信,不跨域的话可以直接取到dom节点进行相关操作。
项目运行命令
npm install 安装依赖包
npm run dev 启动本地开发
npm run build 打包构建,最后打包资源都在dist文件夹里
源码
vx联系