docs(release-notes): 添加住院护士站划价功能说明和发版记录

- 新增住院护士站划价服务流程说明文档,详细描述了从参数预处理到结果响应的五大阶段流程
- 包含耗材类医嘱和诊疗活动类医嘱的差异化处理逻辑
- 添加完整的发版内容记录,涵盖新增菜单功能和各模块优化点
- 记录了住院相关功能的新增和门诊业务流程的修复
```
This commit is contained in:
2025-12-25 14:13:14 +08:00
parent 85fcb7c2e2
commit abc0674531
920 changed files with 107068 additions and 14495 deletions

View File

@@ -1,4 +1,4 @@
import { bodyTemperature, TOP_KEYS, HEAD_HEIGHT, LINE_HEIGHT } from './config'
import { bodyTemperature, TOP_KEYS, HEAD_HEIGHT, LINE_HEIGHT } from './config';
export default class viewConfig {
constructor({
@@ -12,47 +12,44 @@ export default class viewConfig {
strokeWidth = 2, // stroke width of line and dots
strokeLinecap = 'round', // stroke line cap of line
strokeLinejoin = 'round', // stroke line join of line
renderData
renderData,
} = {}) {
// 基础配置赋值
this.width = width
this.height = height
this.stroke = stroke
this.strokeWidth = strokeWidth
this.strokeLinecap = strokeLinecap
this.renderData = renderData
this.strokeLinejoin = strokeLinejoin
this.marginRight = marginRight
this.marginLeft = marginLeft
this.marginBottom = marginBottom
this.marginTop = marginTop
this.width = width;
this.height = height;
this.stroke = stroke;
this.strokeWidth = strokeWidth;
this.strokeLinecap = strokeLinecap;
this.renderData = renderData;
this.strokeLinejoin = strokeLinejoin;
this.marginRight = marginRight;
this.marginLeft = marginLeft;
this.marginBottom = marginBottom;
this.marginTop = marginTop;
// 计算属性赋值
this.contentWidth = width - marginLeft - marginRight
this.step = this.contentWidth / 8
this.bottomPos = height - HEAD_HEIGHT - marginTop - (marginBottom - 30) // 底部坐标30是因为默认的30忘记计算了后续的按照30的偏移量计算
this.tableHeight = height - marginBottom - HEAD_HEIGHT
const { micoStep, verticalHeight } = this.utilsGetMicoPos(
this.step,
this.bottomPos
)
this.micoStep = micoStep
this.verticalHeight = verticalHeight
this.X_OFFSET = micoStep / 2 // 为了让图标在小格子居中展示,需要进行一个偏移
this.xRange = [this.step, width - marginLeft - marginRight] // [60, 860]
this.topPos = marginTop + HEAD_HEIGHT
this.topKeysPos = LINE_HEIGHT * (TOP_KEYS.length + 1) // 1 是时间那一行
this.bottomKeysPosStart = this.topKeysPos + verticalHeight + 20
this.yRange = [this.bottomKeysPosStart - 20, this.topKeysPos]
this.contentWidth = width - marginLeft - marginRight;
this.step = this.contentWidth / 8;
this.bottomPos = height - HEAD_HEIGHT - marginTop - (marginBottom - 30); // 底部坐标30是因为默认的30忘记计算了后续的按照30的偏移量计算
this.tableHeight = height - marginBottom - HEAD_HEIGHT;
const { micoStep, verticalHeight } = this.utilsGetMicoPos(this.step, this.bottomPos);
this.micoStep = micoStep;
this.verticalHeight = verticalHeight;
this.X_OFFSET = micoStep / 2; // 为了让图标在小格子居中展示,需要进行一个偏移
this.xRange = [this.step, width - marginLeft - marginRight]; // [60, 860]
this.topPos = marginTop + HEAD_HEIGHT;
this.topKeysPos = LINE_HEIGHT * (TOP_KEYS.length + 1); // 1 是时间那一行
this.bottomKeysPosStart = this.topKeysPos + verticalHeight + 20;
this.yRange = [this.bottomKeysPosStart - 20, this.topKeysPos];
}
// 获取折线区域的高度
utilsGetMicoPos(step, botpos) {
const micoStep = (step * 7) / 42 // 折线小格子的宽度
const verticalLength = bodyTemperature[1] - bodyTemperature[0] // 根据体温来计算格子
const verticalHeight = micoStep * 5 * verticalLength
const micoStep = (step * 7) / 42; // 折线小格子的宽度
const verticalLength = bodyTemperature[1] - bodyTemperature[0]; // 根据体温来计算格子
const verticalHeight = micoStep * 5 * verticalLength;
return {
micoStep,
verticalHeight
}
verticalHeight,
};
}
}

View File

@@ -1,271 +1,215 @@
import dayjs from 'dayjs'
import dayjs from 'dayjs';
import {
HospitalName,
temperatureName,
showPainFlag,
getInfoKeys,
getBottomKeys
} from './template'
getBottomKeys,
} from './template';
// 存放体温单的配置信息
export const Header = {
HospitalName,
temperatureName
}
temperatureName,
};
// 患者信息
export const INFO_KEYS = getInfoKeys()
export const INFO_KEYS = getInfoKeys();
// 头部信息标签
export const TOP_KEYS = [
//{
// name: '患病日数',
// getValue: (i, renderData) => {
// const { beginDate, hospDays, outdate = '', dateClosed } = renderData.infoData
// const tieml = new Date()
// const timeNew = new Date((tieml / 1000 + 86400) * 1000)
// const todayDate = dayjs(timeNew).format('YYYY-MM-DD')
// const endDate = dayjs(outdate).add(1, 'day').format('YYYY-MM-DD')
// const eachTime = dayjs(beginDate)
// .add(i, 'day')
// .format('YYYY-MM-DD')
// if (eachTime === endDate || todayDate === eachTime) {
// dateClosed.stopNumber = true
// }
// return dateClosed.stopNumber ? hospDays + i + 1 : ''
// }
//},
{
name: '日 期',
getValue: (i, renderData) => {
const { beginDate, outdate = '', hospDate = '', dateClosed } = renderData.infoData
const tieml = new Date()
const timeNew = new Date((tieml / 1000 + 86400) * 1000)
const todayDate = dayjs(timeNew).format('YYYY-MM-DD')
const endDate = dayjs(outdate).format('YYYY-MM-DD')
const startDate = dayjs(hospDate).format('YYYY-MM-DD')
let eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD')
const { beginDate, outdate = '', hospDate = '', dateClosed } = renderData.infoData;
const tieml = new Date();
const timeNew = new Date((tieml / 1000 + 86400) * 1000);
const todayDate = dayjs(timeNew).format('YYYY-MM-DD');
const endDate = dayjs(outdate).format('YYYY-MM-DD');
const startDate = dayjs(hospDate).format('YYYY-MM-DD');
let eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD');
if (eachTime === endDate || eachTime === todayDate) {
dateClosed.stopTime = true
dateClosed.stopTime = true;
}
if ((startDate === eachTime && i === 0) || dayjs(eachTime).format('MM-DD') === '01-01') {
eachTime = dayjs(eachTime).format('YYYY年MM月DD日')
eachTime = dayjs(eachTime).format('YYYY年MM月DD日');
} else if (i === 0 || dayjs(eachTime).format('DD') === '01') {
eachTime = dayjs(eachTime).format('MM月DD日')
eachTime = dayjs(eachTime).format('MM月DD日');
} else {
eachTime = dayjs(eachTime).format('DD日')
eachTime = dayjs(eachTime).format('DD日');
}
return dateClosed.stopTime ? eachTime : ''
}
return dateClosed.stopTime ? eachTime : '';
},
},
{
name: '住院日数',
getValue: (i, renderData) => {
const { beginDate, hospDays, outdate = '', dateClosed } = renderData.infoData
const tieml = new Date()
const timeNew = new Date((tieml / 1000 + 86400) * 1000)
const todayDate = dayjs(timeNew).format('YYYY-MM-DD')
const endDate = dayjs(outdate).add(1, 'day').format('YYYY-MM-DD')
const eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD')
const { beginDate, hospDays, outdate = '', dateClosed } = renderData.infoData;
const tieml = new Date();
const timeNew = new Date((tieml / 1000 + 86400) * 1000);
const todayDate = dayjs(timeNew).format('YYYY-MM-DD');
const endDate = dayjs(outdate).add(1, 'day').format('YYYY-MM-DD');
const eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD');
if (eachTime === endDate || todayDate === eachTime) {
dateClosed.stopNumber = false
dateClosed.stopNumber = false;
}
const num = dateClosed.stopNumber ? hospDays + i + 1 : ''
let hosNum = ''
const num = dateClosed.stopNumber ? hospDays + i + 1 : '';
let hosNum = '';
if (num !== '') {
hosNum = '第' + "\xa0\xa0\xa0" + num + "\xa0\xa0\xa0" + '日'
hosNum = '第' + '\xa0\xa0\xa0' + num + '\xa0\xa0\xa0' + '日';
} else {
hosNum = '第' + "\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0" + '日'
hosNum = '第' + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + '日';
}
return hosNum
}
return hosNum;
},
},
{
name: '术/娩后日数',
getValue: (i, renderData) => {
const { beginDate } = renderData.infoData
const surgeryNumDays = renderData.surgeryNumDays
const eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD')
let num = surgeryNumDays.filter(item => item.date === eachTime)
let hosNum = ''
const { beginDate } = renderData.infoData;
const surgeryNumDays = renderData.surgeryNumDays;
const eachTime = dayjs(beginDate).add(i, 'day').format('YYYY-MM-DD');
let num = surgeryNumDays.filter((item) => item.date === eachTime);
let hosNum = '';
if (num.length > 0) {
hosNum = '第' + "\xa0\xa0\xa0" + num[0].typeValue + "\xa0\xa0\xa0" + '日'
hosNum = '第' + '\xa0\xa0\xa0' + num[0].typeValue + '\xa0\xa0\xa0' + '日';
} else {
hosNum = '第' + "\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0" + '日'
hosNum = '第' + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + '日';
}
return hosNum
}
}
]
return hosNum;
},
},
];
// 最底部的字段绘制
export const BOTTOM_KEYS = getBottomKeys()
export const BOTTOM_KEYS = getBottomKeys();
// 是否显示疼痛评分
export const showPain = showPainFlag
export const showPain = showPainFlag;
/** *********** 以下是固定选项 **************************/
export const timeNumber = [2, 6, 10, 14, 18, 22] // 时间展示
export const nightTime = [2, 18, 22] // 夜间红色高亮时间
//export const leftTEXT1 = [
// // ['脉3搏,(次/分), 180', '160', '140', '120', '100', '80', '60', '40'],
// // ['体4温,(℃), 42', '41', '40', '39', '38', '37', '36', '35']
// // ['华氏,( °F), 107.6', '105.8', '104', '102.2', '100.4', '98.6', '96.8', '95']
// ['华氏,( °F), ', '', '107', '106', '105', '104', '103', '102', '101', '100', '99', '98', '97', '96', '95', '94']
// // ['摄氏,(℃), 42', '41', '40', '39', '38', '37', '36', '35']
//]
//export const leftTEXT2 = [
// ['摄氏,(℃), ', '42', '41', '40', '39', '38', '37', '36', '35']
//]
//export const rightTEXT = [
// [',, ', '180', '160', '140', '120', '100', '80', '60', '40']
//]
//export const painTEXT = [
// ['疼 痛'],
// ['8', '6', '4', '2']
//]
export const timeNumber = [2, 6, 10, 14, 18, 22]; // 时间展示
export const nightTime = [2, 18, 22]; // 夜间红色高亮时间
export const leftTEXT = [
['脉搏,(次/分),', '180', '160', '140', '120', '100', '80', '60', '40'],
['体温,(℃), ', '42', '41', '40', '39', '38', '37', '36', '35']
]
export const painTEXT = [['疼 痛 强 度'], ['10', '8', '6', '4', '2', '0']]
export const inOutItem = ['入观', '分娩', '手术', '转入', '出观', '死亡']
export const otherItem = ['外出', '请假', '拒测', '离院', '其他']
['体温,(℃), ', '42', '41', '40', '39', '38', '37', '36', '35'],
];
export const painTEXT = [['疼 痛 强 度'], ['10', '8', '6', '4', '2', '0']];
export const inOutItem = ['入观', '分娩', '手术', '转入', '出观', '死亡'];
export const otherItem = ['外出', '请假', '拒测', '离院', '其他'];
export const sheetOptions = {
locations: [{
code: '1',
display: '体温'
}, {
code: '2',
display: '口温'
}, {
code: '3',
display: '肛温'
}, {
code: '4',
display: '温'
}],
breath: [{
code: '',
display: '自主呼吸'
}, {
code: '®',
display: '机械通气'
}],
poop: [{
code: '',
display: '正常'
}, {
code: '',
display: '失禁'
}, {
code: '☆',
display: '人工肛门'
}, {
code: '/E',
display: '灌肠'
}],
pee: [{
code: '',
display: '正常'
}, {
code: '※',
display: '失禁'
}, {
code: 'C+',
display: '导尿'
}],
locations: [
{
code: '1',
display: '体温',
},
{
code: '2',
display: '口温',
},
{
code: '3',
display: '温',
},
{
code: '4',
display: '耳温',
},
],
breath: [
{
code: '',
display: '自主呼吸',
},
{
code: '®',
display: '机械通气',
},
],
poop: [
{
code: '',
display: '正常',
},
{
code: '',
display: '失禁',
},
{
code: '☆',
display: '人工肛门',
},
{
code: '/E',
display: '灌肠',
},
],
pee: [
{
code: '',
display: '正常',
},
{
code: '※',
display: '失禁',
},
{
code: 'C+',
display: '导尿',
},
],
heart: ['窦性心律', '起搏心律', '房性心律', '异常心律'],
weight: ['正常', '卧床', '轮椅', '平车']
}
weight: ['正常', '卧床', '轮椅', '平车'],
};
// 此处是显示数字的 要和leftTEXT保持一直
// export const bodyTemperature1 = [94, 107]
// export const starNumEnv1 = bodyTemperature1[0] // 开始体温
// export const endNumEnv1 = bodyTemperature1[1] // 结束体温
export const bodyTemperature = [33, 43]
export const starNumEnv = bodyTemperature[0] // 开始体温
export const endNumEnv = bodyTemperature[1] // 结束体温
export const heartRange = [0, 200]
export const painScore = [0, 10]
export const bodyTemperature = [33, 43];
export const starNumEnv = bodyTemperature[0]; // 开始体温
export const endNumEnv = bodyTemperature[1]; // 结束体温
export const heartRange = [0, 200];
export const painScore = [0, 10];
// 中间图表字段对应
export const CHART_KEYS = [
{
key: '001',
code: 'breath',
name: '呼吸'
name: '呼吸',
},
{
key: '002',
code: 'sphygmus',
name: '脉搏'
name: '脉搏',
},
{
key: '003',
code: 'temperature',
name: '体温'
name: '体温',
},
{
key: '012',
code: 'inOut',
name: '特殊标记'
name: '特殊标记',
},
{
key: '013',
code: 'refuse',
name: '标记内容'
name: '标记内容',
},
{
key: '014',
code: 'heartRate',
name: '心率'
name: '心率',
},
{
key: '015',
code: 'lowerTemp',
name: '物理降温'
name: '物理降温',
},
{
key: '016',
code: 'painScore',
name: '疼痛评分'
}
// {
// key: '017',
// code: 'admission',
// name: '入院'
// },
// {
// key: '018',
// code: 'bigSurgery',
// name: '大手术'
// },
// {
// key: '019',
// code: 'smallSurgery',
// name: '小手术'
// },
// {
// key: '020',
// code: 'discharge',
// name: '出院'
// },
// {
// key: '021',
// code: 'parturition',
// name: '分娩'
// },
// {
// key: '022',
// code: 'transfer',
// name: '转科'
// },
// {
// key: '9507',
// code: 'death',
// name: '死亡'
// }
]
export const HEAD_HEIGHT = 80 // 头部文字预留位置
export const LINE_HEIGHT = 20 // 一行的行高
export const textLeftMargin = 4 // 文字左边边距
export const TEXT_MARGIN_BOTTOM = 6 // 文字向上偏移量
export const symbolArrowHeight = 20
name: '疼痛评分',
},
];
export const HEAD_HEIGHT = 120; // 头部文字预留位置
export const LINE_HEIGHT = 20; // 一行的行高
export const textLeftMargin = 4; // 文字左边边距
export const TEXT_MARGIN_BOTTOM = 6; // 文字向上偏移量
export const symbolArrowHeight = 20;

View File

@@ -4,42 +4,32 @@
// index: 0
// value: 36
// }
export function getMaxList({
list = [],
max = 0,
min = 0,
maxDefault = 0,
minDefault = 0
} = {}) {
return list.map(item => {
if (((item.value > max) || (item.value < min)) && item.value !== null) {
const ismax = item.value > max
export function getMaxList({ list = [], max = 0, min = 0, maxDefault = 0, minDefault = 0 } = {}) {
return list.map((item) => {
if ((item.value > max || item.value < min) && item.value !== null) {
const ismax = item.value > max;
return {
...item,
value: ismax ? maxDefault : minDefault,
sourceValue: item.value,
ismax: ismax,
max,
min
}
min,
};
} else {
return null
return null;
}
})
});
}
export function levelingData({
list = [],
maxDefault = 0,
minDefault = 0
}) {
return list.map(item => {
if (item.value === null) return item
export function levelingData({ list = [], maxDefault = 0, minDefault = 0 }) {
return list.map((item) => {
if (item.value === null) return item;
if (item.value > maxDefault) {
item.value = maxDefault
item.value = maxDefault;
} else if (item.value < minDefault) {
item.value = minDefault
item.value = minDefault;
}
return item
})
return item;
});
}

View File

@@ -1,6 +1,6 @@
// 数据处理
import * as d3 from 'd3'
import { symbol } from 'd3-shape'
import * as d3 from 'd3';
import { symbol } from 'd3-shape';
import {
getTypeData,
getTypeDatas,
@@ -13,12 +13,19 @@ import {
getType,
getHeartRate,
disconnectEvents,
getBrokenLine
} from './utils'
import { HEAD_HEIGHT, LINE_HEIGHT, TOP_KEYS, BOTTOM_KEYS, symbolArrowHeight, textLeftMargin} from './config'
getBrokenLine,
} from './utils';
import {
HEAD_HEIGHT,
LINE_HEIGHT,
TOP_KEYS,
BOTTOM_KEYS,
symbolArrowHeight,
textLeftMargin,
} from './config';
export const iconDrawObj = {
// 绘制圆形图标
getDrawRoundIcon: ({ content,data, x, y, fill = 'blue',stroke = 'blue', r = 5 }) => {
getDrawRoundIcon: ({ content, data, x, y, fill = 'blue', stroke = 'blue', r = 5 }) => {
// 增加icon 红色空心
content
.append('g')
@@ -29,23 +36,32 @@ export const iconDrawObj = {
.data(data)
.join('circle')
.attr('transform', (i) => {
const yVal = y(i) || 0
const yVal = y(i) || 0;
if (!yVal) {
return 'scale(0)'
return 'scale(0)';
}
return ''
return '';
})
.attr('cx', x)
.attr('cy', y)
.attr('r', (i) => {
if (y(i)) {
return r
return r;
}
return 0
})
return 0;
});
},
// 绘制圆形点图标
getDrawRoundDotIcon: ({ content, data, x, y, fill = 'white', stroke = 'blue', deepFill = 'blue', r = 6 }) => {
getDrawRoundDotIcon: ({
content,
data,
x,
y,
fill = 'white',
stroke = 'blue',
deepFill = 'blue',
r = 6,
}) => {
content
.append('g')
.attr('fill', fill)
@@ -55,11 +71,11 @@ export const iconDrawObj = {
.data(data)
.join('circle')
.attr('transform', (i) => {
const yVal = y(i) || 0
const yVal = y(i) || 0;
if (!yVal) {
return 'scale(0)'
return 'scale(0)';
}
return ''
return '';
})
.attr('cx', x)
.attr('cy', y)
@@ -68,7 +84,7 @@ export const iconDrawObj = {
.attr('cx', x)
.attr('cy', y)
.attr('r', 1)
.attr('fill', deepFill)
.attr('fill', deepFill);
},
// 绘制x
getDrawXIcon: ({ content, data, x, y, fill = 'blue', stroke = 'blue' }) => {
@@ -81,43 +97,43 @@ export const iconDrawObj = {
.data(data)
.join('line')
.attr('transform', (i) => {
let yVal = y
let yVal = y;
if (typeof y === 'function') {
yVal = y(i) || 0
yVal = y(i) || 0;
}
if (!yVal) {
return 'scale(0)'
return 'scale(0)';
}
return ''
return '';
})
.attr('x1', function(d) {
return x(d) - 4
.attr('x1', function (d) {
return x(d) - 4;
})
.attr('y1', function(d) {
return y(d) - 4
.attr('y1', function (d) {
return y(d) - 4;
})
.attr('x2', function(d) {
return x(d) + 4
.attr('x2', function (d) {
return x(d) + 4;
})
.attr('y2', function(d) {
return y(d) + 4
.attr('y2', function (d) {
return y(d) + 4;
})
.clone()
.attr('x1', function(d) {
return x(d) + 4
.attr('x1', function (d) {
return x(d) + 4;
})
.attr('y1', function(d) {
return y(d) - 4
.attr('y1', function (d) {
return y(d) - 4;
})
.attr('x2', function(d) {
return x(d) - 4
})
.attr('y2', function(d) {
return y(d) + 4
.attr('x2', function (d) {
return x(d) - 4;
})
.attr('y2', function (d) {
return y(d) + 4;
});
},
// 绘制三角形
drawThreeIcon: ({ content, data, x,y, fill = 'blue', stroke = 'blue', riangle = 48 }) => {
drawThreeIcon: ({ content, data, x, y, fill = 'blue', stroke = 'blue', riangle = 48 }) => {
// 蓝色三角形
content
.append('g')
@@ -126,74 +142,82 @@ export const iconDrawObj = {
.data(data)
.join('g')
.attr('transform', (i) => {
const yVal = y(i) || 0
const yVal = y(i) || 0;
if (!yVal) {
return 'scale(0)'
return 'scale(0)';
}
return `translate(${x(i)},${yVal})`
return `translate(${x(i)},${yVal})`;
})
.append('path')
.call((path) => {
const symbolThree = symbol()
const symbolIndex = 5
symbolThree.type(d3.symbols[symbolIndex])
path.attr('d', symbolThree.size(riangle)).attr('fill', fill) .attr('stroke', stroke)
const symbolThree = symbol();
const symbolIndex = 5;
symbolThree.type(d3.symbols[symbolIndex]);
path.attr('d', symbolThree.size(riangle)).attr('fill', fill).attr('stroke', stroke);
// .transition()
// .duration(1500)
// .attr('d', symbolThree.size(48))
})
}
}
});
},
};
export function getG(svg, viewConfig) {
return svg .append('g') .attr( 'transform', `translate(${viewConfig.marginLeft},${viewConfig.marginTop + HEAD_HEIGHT})` )
return svg
.append('g')
.attr('transform', `translate(${viewConfig.marginLeft},${viewConfig.marginTop + HEAD_HEIGHT})`);
}
// 设置数据
export function getData(allData) {
const rowsData = allData.rows // allData, '【全部数据】'
const infoData = allData.grParamBOS
const typesData = getTypeDatas(allData.types, allData.grParamBOS.beginDate)
const selectOp = allData.selectOp
const symbolTextArr = getTypeData('018', rowsData, false, allData.grParamBOS.beginDate) // 【特殊标记】
const symbolGoUp = getType('003', rowsData, allData.grParamBOS.beginDate) // 不升
const rowsData = allData.rows; // allData, '【全部数据】'
const infoData = allData.grParamBOS;
const typesData = getTypeDatas(allData.types, allData.grParamBOS.beginDate);
const selectOp = allData.selectOp;
const symbolTextArr = getTypeData('018', rowsData, false, allData.grParamBOS.beginDate); // 【特殊标记】
const symbolGoUp = getType('003', rowsData, allData.grParamBOS.beginDate); // 不升
// 体温单特殊数据
const dicData = JSON.parse(window.localStorage.getItem('transDictCode'))
const signsManagementList = dicData ? dicData.filter(item => item.name === '95')[0].concept.sort((a, b) => { return a.displayOrder - b.displayOrder }) :[]
const otherArr = []
const dicData = JSON.parse(window.localStorage.getItem('transDictCode'));
const signsManagementList = dicData
? dicData
.filter((item) => item.name === '95')[0]
.concept.sort((a, b) => {
return a.displayOrder - b.displayOrder;
})
: [];
const otherArr = [];
if (signsManagementList.length > 0) {
for (let j = 0; j < signsManagementList.length; j++) {
if (signsManagementList[j].code !== '9502' & signsManagementList[j].code !== '9503') {
const otherArrItem = getTypeData(signsManagementList[j].code, rowsData, false, allData.grParamBOS.beginDate)
otherArr.push(otherArrItem)
if ((signsManagementList[j].code !== '9502') & (signsManagementList[j].code !== '9503')) {
const otherArrItem = getTypeData(
signsManagementList[j].code,
rowsData,
false,
allData.grParamBOS.beginDate
);
otherArr.push(otherArrItem);
}
}
}
const surgeryNumDays = allData.types.filter(item => item.typeCode === '031')
const surgeryArr = getTypeData('9502', rowsData, false, allData.grParamBOS.beginDate) // '【手术】'
const minSurgeryArr = getTypeData('9503', rowsData, false, allData.grParamBOS.beginDate) // '【小手术】'
const temArr = getTypeData('00301', rowsData, false, allData.grParamBOS.beginDate) // '【体温拒测等】'
// const symbolGoUp = getType('003', rowsData, allData.grParamBOS.beginDate) // symbolGoUp, '不升'
const surgeryNumDays = allData.types.filter((item) => item.typeCode === '031');
const surgeryArr = getTypeData('9502', rowsData, false, allData.grParamBOS.beginDate); // '【手术】'
const minSurgeryArr = getTypeData('9503', rowsData, false, allData.grParamBOS.beginDate); // '【小手术】'
const temArr = getTypeData('00301', rowsData, false, allData.grParamBOS.beginDate); // '【体温拒测等】'
// const symbolGoUp = getType('003', rowsData, allData.grParamBOS.beginDate) // symbolGoUp, '不升'
// 35度线上的内容
const symbolDegreesEvents = degreesOnline(symbolTextArr, selectOp, 0)
const symbolDegreesOnline = disconnectEvents( symbolTextArr, selectOp, 'isBeforeAfter', 0 )
const symbolDegreesEvents = degreesOnline(symbolTextArr, selectOp, 0);
const symbolDegreesOnline = disconnectEvents(symbolTextArr, selectOp, 'isBeforeAfter', 0);
// 40~42线上的内容
const symbolTopOnline = degreesOnline(symbolTextArr, selectOp, 1)
const symbolTopDegreesOnline = disconnectEvents(
symbolTextArr,
selectOp,
'isBeforeAfter',
1
)
const symbolContent = getTypeData('013', rowsData, false, allData.grParamBOS.beginDate) // symbolContent, '【标记内容】'
const mergeTag = setMergeTag(symbolTopOnline, symbolContent) // mergeTag, '【合并标记,标记内容】'
const symbolTopOnline = degreesOnline(symbolTextArr, selectOp, 1);
const symbolTopDegreesOnline = disconnectEvents(symbolTextArr, selectOp, 'isBeforeAfter', 1);
const symbolContent = getTypeData('013', rowsData, false, allData.grParamBOS.beginDate); // symbolContent, '【标记内容】'
const mergeTag = setMergeTag(symbolTopOnline, symbolContent); // mergeTag, '【合并标记,标记内容】'
// 产后日数
infoData.postpartum = postpartumDays('031', typesData) // infoData, '产后日数'
infoData.postpartum = postpartumDays('031', typesData); // infoData, '产后日数'
// 写死的先
infoData.dateClosed = {
stopTime: true, // 控制结束日期
stopNumber: true // 控制住院天数
}
stopNumber: true, // 控制住院天数
};
// 折线
const brokenLineData = getBrokenLine(
'003',
@@ -202,22 +226,23 @@ export function getData(allData) {
symbolGoUp,
symbolTopDegreesOnline,
allData.grParamBOS.beginDate
)
);
// 模拟数据 体温
const datasetAnus = getTypeAnimalHeat('003', rowsData, '1', allData.grParamBOS.beginDate) // datasetAnus, '腋温【x】'
const bodyData = getTypeAnimalHeat('003', rowsData, '2', allData.grParamBOS.beginDate) // dbodyData, '口温'
const datasetAnus = getTypeAnimalHeat('003', rowsData, '1', allData.grParamBOS.beginDate); // datasetAnus, '腋温【x】'
const bodyData = getTypeAnimalHeat('003', rowsData, '2', allData.grParamBOS.beginDate); // dbodyData, '口温'
// const datasetHeartrate = getTypeAnimalHeat('003', rowsData)
const datasetHeartrate = getTypeAnimalHeat('003', rowsData, '3', allData.grParamBOS.beginDate) // datasetHeartrate, '肛温【红空圆】'
const earCool = getTypeAnimalHeat('003', rowsData, '4', allData.grParamBOS.beginDate) // earCool, '耳朵温'
const painScore = getHeartRate( // painScore, '疼痛'
const datasetHeartrate = getTypeAnimalHeat('003', rowsData, '3', allData.grParamBOS.beginDate); // datasetHeartrate, '肛温【红空圆】'
const earCool = getTypeAnimalHeat('003', rowsData, '4', allData.grParamBOS.beginDate); // earCool, '耳朵温'
const painScore = getHeartRate(
// painScore, '疼痛'
'016',
rowsData,
symbolDegreesOnline,
symbolTopDegreesOnline,
true,
allData.grParamBOS.beginDate
)
const allTemperatureData = [bodyData, datasetAnus, datasetHeartrate, earCool] // 所有的温度记录
);
const allTemperatureData = [bodyData, datasetAnus, datasetHeartrate, earCool]; // 所有的温度记录
// '脉搏【红实圆】'
const datasetPulse = getHeartRate(
'002',
@@ -226,7 +251,7 @@ export function getData(allData) {
symbolTopDegreesOnline,
true,
allData.grParamBOS.beginDate
)
);
// 心率【空心】
const datasetHeartRate = getHeartRate(
'014',
@@ -235,14 +260,14 @@ export function getData(allData) {
symbolTopDegreesOnline,
true,
allData.grParamBOS.beginDate
)
);
// const allTemperatureData = getDrawData('003', rowsData, '1', allData.grParamBOS.beginDate) // datasetAnus, '腋温【x】'
// const dataCool = getDrawCoolData('015', rowsData, true, allData.grParamBOS.beginDate) // dataCool, '【物理降温】'
const dataCool = getTypeData('015', rowsData, true, allData.grParamBOS.beginDate) // 【物理降温】
const dataCool = getTypeData('015', rowsData, true, allData.grParamBOS.beginDate); // 【物理降温】
// 呼吸【黑实圆】
const datasetPain = getTypeData('001', rowsData, false, allData.grParamBOS.beginDate)// 呼吸
const datasetPain = getTypeData('001', rowsData, false, allData.grParamBOS.beginDate); // 呼吸
const title = infoData.title
const title = infoData.title;
return {
title,
datasetHeartRate,
@@ -269,8 +294,8 @@ export function getData(allData) {
temArr,
surgeryArr,
minSurgeryArr,
surgeryNumDays
}
surgeryNumDays,
};
}
export function drawTopMask(svg, viewConfig) {
@@ -289,19 +314,17 @@ export function drawTopMask(svg, viewConfig) {
.attr('height', LINE_HEIGHT * (TOP_KEYS.length + 7.5) - 1)
.attr('stroke', viewConfig.stroke)
.attr('fill', '#fff')
.attr('style', 'stroke-width: 0')
.attr('style', 'stroke-width: 0');
drawTopVerticalLine(svg, viewConfig)
drawTopVerticalLine(svg, viewConfig);
}
export function drawTopVerticalLine(svg, viewConfig) {
// 补上下竖线
let start = viewConfig.step
const lineG = getG(svg, viewConfig)
.append('g')
.attr('class', 'maskline-top')
let start = viewConfig.step;
const lineG = getG(svg, viewConfig).append('g').attr('class', 'maskline-top');
while (start < viewConfig.contentWidth) {
// const isLastLine = (start + viewConfig.step) >= viewConfig.contentWidth
// const isLastLine = (start + viewConfig.step) >= viewConfig.contentWidth
lineG
.append('line')
.attr('fill', 'stroke')
@@ -311,13 +334,13 @@ export function drawTopVerticalLine(svg, viewConfig) {
.attr('x2', start)
.attr('stroke', start > viewConfig.step ? '#B22222' : viewConfig.stroke)
// .attr('stroke', isLastLine ? 'black' : (start > viewConfig.step ? '#B22222' : viewConfig.stroke))
.attr('stroke-width', 4) // 设置线条宽度为2
start = start + viewConfig.step
.attr('stroke-width', 4); // 设置线条宽度为2
start = start + viewConfig.step;
}
}
export function drawBottomMask(svg, viewConfig) {
const g = getG(svg, viewConfig)
const g = getG(svg, viewConfig);
// 遮罩层挡住超出的折线
g.append('rect')
.attr('class', 'mask-rect')
@@ -327,18 +350,16 @@ export function drawBottomMask(svg, viewConfig) {
.attr('height', LINE_HEIGHT * (BOTTOM_KEYS.length + 5))
.attr('stroke', viewConfig.stroke)
.attr('fill', '#ffffff')
.attr('style', 'stroke-width: 0')
drawBottomMaskLine(svg, viewConfig)
.attr('style', 'stroke-width: 0');
drawBottomMaskLine(svg, viewConfig);
}
function drawBottomMaskLine(svg, viewConfig) {
// 补上下竖线
let start = viewConfig.step
const lineG = getG(svg, viewConfig)
.append('g')
.attr('class', 'maskline')
let start = viewConfig.step;
const lineG = getG(svg, viewConfig).append('g').attr('class', 'maskline');
while (start < viewConfig.contentWidth) {
// const isLastLine = (start + viewConfig.step) >= viewConfig.contentWidth;
// const isLastLine = (start + viewConfig.step) >= viewConfig.contentWidth;
lineG
.append('line')
.attr('fill', 'stroke')
@@ -348,14 +369,14 @@ function drawBottomMaskLine(svg, viewConfig) {
.attr('x2', start)
.attr('stroke', start > viewConfig.step ? '#B22222' : viewConfig.stroke)
//.attr('stroke', isLastLine ? 'black' : (start > viewConfig.step ? '#B22222' : viewConfig.stroke))
.attr('stroke-width', 4) // 设置线条宽度为2
start = start + viewConfig.step
.attr('stroke-width', 4); // 设置线条宽度为2
start = start + viewConfig.step;
}
}
// 绘制特殊事件文字 --- 有时间
export function drawSpecialText(svg, viewConfig, textData) {
const g = getG(svg, viewConfig)
const g = getG(svg, viewConfig);
g.append('g')
.selectAll('text')
.data(textData)
@@ -363,43 +384,46 @@ export function drawSpecialText(svg, viewConfig, textData) {
.attr('style', 'font-size:14px; fill: red;')
.attr('class', 'mytext')
.html((d, i) => {
const t = (d?.value || '').split(',')
let time = ''
const t = (d?.value || '').split(',');
let time = '';
if (t.length > 1) {
time = t[0] + '于' + transNum(t[1].split(':')[0]) + '时' + transNum(t[1].split(':')[1]) + '分'
time =
t[0] + '于' + transNum(t[1].split(':')[0]) + '时' + transNum(t[1].split(':')[1]) + '分';
// time = t[0] + '㇑' + transNum(t[1].split(':')[0]) + '时' + transNum(t[1].split(':')[1]) + '分'
}
const texts = time.split('')
const texts = time.split('');
return texts
.map((text, i) => {
return `<tspan dx="${i === 1 ? -14 : i === 0 ? 0 : Number(text) ? -8 : -14}" dy="${20}">${text}</tspan>`
return `<tspan dx="${
i === 1 ? -14 : i === 0 ? 0 : Number(text) ? -8 : -14
}" dy="${20}">${text}</tspan>`;
})
.join('')
.join('');
})
.attr('x', (d, i) => {
return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin
return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin;
})
.attr('y', () => {
return viewConfig.topKeysPos - textLeftMargin
})
return viewConfig.topKeysPos - textLeftMargin;
});
}
// 绘制特殊事件文字 --- 大手术小手术
export function drawSurgery(svg, viewConfig, textData) {
const g = getG(svg, viewConfig)
const textDatas = []
const g = getG(svg, viewConfig);
const textDatas = [];
if (textData.length > 0) {
for (let j = 0; j < textData.length; j++) {
let item = {}
let item = {};
if (textData[j].value) {
item = {
index: textData[j].index,
value: textData[j].value.split(',')[0],
date: textData[j].date
}
date: textData[j].date,
};
} else {
item = textData[j]
item = textData[j];
}
textDatas.push(item)
textDatas.push(item);
}
}
g.append('g')
@@ -409,40 +433,39 @@ export function drawSurgery(svg, viewConfig, textData) {
.attr('style', 'font-size:15px; fill: red;')
.attr('class', 'mytext')
.html((d, i) => {
const texts = (d?.value || '').split('')
const texts = (d?.value || '').split('');
return texts
.map((text, i) => {
return `<tspan dx="${
i === 1 ? -15 : i === 0 ? 0 : Number(text) ? -8 : -15
}" dy="${20}">${text}</tspan>`
}" dy="${20}">${text}</tspan>`;
})
.join('')
.join('');
})
.attr('x', (d, i) => {
return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin - 2;
})
.attr(
'x',
(d, i) => { return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin - 2 }
)
.attr('y', () => {
return viewConfig.topKeysPos - textLeftMargin
})
return viewConfig.topKeysPos - textLeftMargin;
});
}
// 绘制特殊事件文字 --- 无时间
export function drawSpecialNoTimeText(svg, viewConfig, textData) {
const g = getG(svg, viewConfig)
const textDatas = []
const g = getG(svg, viewConfig);
const textDatas = [];
if (textData.length > 0) {
for (let j = 0; j < textData.length; j++) {
let item = {}
let item = {};
if (textData[j].value) {
item = {
index: textData[j].index,
value: textData[j].value.split(',')[0],
date: textData[j].date
}
date: textData[j].date,
};
} else {
item = textData[j]
item = textData[j];
}
textDatas.push(item)
textDatas.push(item);
}
}
g.append('g')
@@ -452,7 +475,7 @@ export function drawSpecialNoTimeText(svg, viewConfig, textData) {
.attr('style', 'font-size:15px; fill: red;')
.attr('class', 'mytext')
.html((d, i) => {
const texts = (d?.value || '').split('')
const texts = (d?.value || '').split('');
// let time = ''
// if (t.length > 1) {
// time = t[0] + '㇑' + transNum(t[1].split(':')[0]) + '时' + transNum(t[1].split(':')[1]) + '分'
@@ -460,39 +483,38 @@ export function drawSpecialNoTimeText(svg, viewConfig, textData) {
// const texts = time.split('')
return texts
.map((text, i) => {
return `<tspan dx="${
i === 1 ? -15 : i === 0 ? 0 : Number(text) ? -8 : -15
}" dy="${ i === 0 ? 20 : 80}">${text}</tspan>`
return `<tspan dx="${i === 1 ? -15 : i === 0 ? 0 : Number(text) ? -8 : -15}" dy="${
i === 0 ? 20 : 80
}">${text}</tspan>`;
})
.join('')
.join('');
})
.attr('x', (d, i) => {
return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin - 2;
})
.attr(
'x',
(d, i) => { return viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin - 2 }
)
.attr('y', () => {
return viewConfig.topKeysPos - textLeftMargin
})
return viewConfig.topKeysPos - textLeftMargin;
});
}
// 数字转换成文字
function transNum(num) {
const arr1 = ['', '十']
const arr2 = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
const sw = num.split('')[0]
const gw = num.split('')[1]
const str1 = sw > 1 ? arr2[sw] + '十' : arr1[sw]
let strNum = str1 + arr2[gw]
const arr1 = ['', '十'];
const arr2 = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
const sw = num.split('')[0];
const gw = num.split('')[1];
const str1 = sw > 1 ? arr2[sw] + '十' : arr1[sw];
let strNum = str1 + arr2[gw];
// 若H为十点整小时则把零去掉若m为十分,则去掉零
if (strNum.indexOf('十') > 0 || strNum.indexOf('零') === 1) {
strNum = strNum.replace('零', '')
strNum = strNum.replace('零', '');
}
return strNum
return strNum;
}
// 绘制标签文字
export function drawTagText(svg, viewConfig, textData) {
const g = getG(svg, viewConfig)
const g = getG(svg, viewConfig);
g.append('g')
.selectAll('text')
.data(textData)
@@ -500,25 +522,24 @@ export function drawTagText(svg, viewConfig, textData) {
.attr('style', 'font-size:14px; fill: black;')
.attr('class', 'mytext')
.html((d, i) => {
const texts = (d?.value || '').split('')
const texts = (d?.value || '').split('');
return texts
.map((text, i) => {
return `<tspan dx="${i === 1 ? -14 : i === 0 ? 0 : Number(text) ? -8 : -14}" dy="${20}">${text}</tspan>`
return `<tspan dx="${
i === 1 ? -14 : i === 0 ? 0 : Number(text) ? -8 : -14
}" dy="${20}">${text}</tspan>`;
})
.join('')
.join('');
})
.attr(
'x',
(d, i) => viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin
)
.attr('x', (d, i) => viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin)
.attr('y', () => {
return viewConfig.topKeysPos - textLeftMargin + 198
})
return viewConfig.topKeysPos - textLeftMargin + 198;
});
}
// 绘制特殊事件文字底部
export function drawBottomSpecialText(svg, viewConfig, textData) {
const g = getG(svg, viewConfig)
const g = getG(svg, viewConfig);
g.append('g')
.selectAll('text')
.data(textData)
@@ -526,23 +547,20 @@ export function drawBottomSpecialText(svg, viewConfig, textData) {
.attr('style', 'font-size:14px; fill: blue;')
.attr('class', 'mytext')
.html((d, i) => {
const texts = (d?.value || '').split('')
const texts = (d?.value || '').split('');
return texts
.map((text, i) => {
return `<tspan dx="${
i === 1 ? -14 : i === 0 ? 0 : Number(text) ? -8 : -14
}" dy="${20}">${text}</tspan>`
}" dy="${20}">${text}</tspan>`;
})
.join('')
.join('');
})
.attr(
'x',
(d, i) => viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin
)
.attr('x', (d, i) => viewConfig.step + d.index * viewConfig.micoStep + textLeftMargin)
.attr('y', (d) => {
const texts = (d?.value || '').split('')
return viewConfig.bottomKeysPosStart - texts.length * LINE_HEIGHT - 6 - 58
})
const texts = (d?.value || '').split('');
return viewConfig.bottomKeysPosStart - texts.length * LINE_HEIGHT - 6 - 58;
});
}
export function initArrow(svg) {
@@ -556,12 +574,9 @@ export function initArrow(svg) {
.attr('viewBox', '0 0 12 12')
.attr('refX', '6')
.attr('refY', '6')
.attr('orient', 'auto')
const arrowPath = 'M2,2 L10,6 L2,10 L6,6 L2,2'
arrowMarkerRed
.append('path')
.attr('d', arrowPath)
.attr('fill', 'red')
.attr('orient', 'auto');
const arrowPath = 'M2,2 L10,6 L2,10 L6,6 L2,2';
arrowMarkerRed.append('path').attr('d', arrowPath).attr('fill', 'red');
const arrowMarkerBlue = svg
.append('defs')
.append('marker')
@@ -572,16 +587,13 @@ export function initArrow(svg) {
.attr('viewBox', '0 0 12 12')
.attr('refX', '6')
.attr('refY', '6')
.attr('orient', 'auto')
arrowMarkerBlue
.append('path')
.attr('d', arrowPath)
.attr('fill', 'blue')
.attr('orient', 'auto');
arrowMarkerBlue.append('path').attr('d', arrowPath).attr('fill', 'blue');
}
export function lineArrow({ svg, viewConfig, scale, data } = {}) {
const g = getG(svg, viewConfig)
const vaildData = data.filter((item) => item)
const g = getG(svg, viewConfig);
const vaildData = data.filter((item) => item);
// {
// ...item,
// value: ismax ? maxDefault : minDefault,
@@ -595,96 +607,85 @@ export function lineArrow({ svg, viewConfig, scale, data } = {}) {
.data(vaildData)
.join('line')
.attr('x1', (d, i) => {
return scale(d.index) + +viewConfig.X_OFFSET
return scale(d.index) + +viewConfig.X_OFFSET;
})
.attr('y1', (d, i) => {
if (d.ismax) {
// 顶部箭头
return viewConfig.topKeysPos + symbolArrowHeight * 1.5
return viewConfig.topKeysPos + symbolArrowHeight * 1.5;
} else {
// 底部箭头
return viewConfig.bottomKeysPosStart - symbolArrowHeight * 1.5
return viewConfig.bottomKeysPosStart - symbolArrowHeight * 1.5;
}
})
.attr('x2', (d, i) => {
return scale(d.index) + +viewConfig.X_OFFSET
return scale(d.index) + +viewConfig.X_OFFSET;
})
.attr('y2', (d, i) => {
if (d.ismax) {
// 顶部箭头
return viewConfig.topKeysPos + symbolArrowHeight / 2
return viewConfig.topKeysPos + symbolArrowHeight / 2;
} else {
// 底部箭头
return viewConfig.bottomKeysPosStart - symbolArrowHeight / 2
return viewConfig.bottomKeysPosStart - symbolArrowHeight / 2;
}
})
.attr('stroke', (d) => {
return d.ismax ? 'red' : 'blue'
return d.ismax ? 'red' : 'blue';
})
.attr('stroke-width', 2)
.attr('marker-end', (d, i) => {
return d.ismax ? 'url(#redArrow)' : 'url(#blueArrow)'
})
return d.ismax ? 'url(#redArrow)' : 'url(#blueArrow)';
});
g.selectAll('text')
.data(vaildData)
.join('text')
.attr('class', 'desctextname')
.html((item, i) => {
const value = `${item.sourceValue}`.split('')
const dotIndex = value.indexOf('.')
const value = `${item.sourceValue}`.split('');
const dotIndex = value.indexOf('.');
return value
.map((d, index) => {
let y
let multiple = index
let addition = 0 // dot height
let y;
let multiple = index;
let addition = 0; // dot height
if (item.ismax) {
if (index >= dotIndex) {
addition = 8
multiple = index - 1
addition = 8;
multiple = index - 1;
}
y =
viewConfig.topKeysPos +
symbolArrowHeight * 1.5 +
(2 + multiple * 1.5) * viewConfig.X_OFFSET +
addition
addition;
} else {
if (index >= dotIndex) {
addition = 9
multiple = index - 1
addition = 9;
multiple = index - 1;
}
y =
viewConfig.bottomKeysPosStart -
(2 + (value.length - multiple) * 1.5) * viewConfig.X_OFFSET +
addition
addition;
}
return `
<tspan rotate="90" x="${scale(item.index) +
viewConfig.X_OFFSET -
4}"
y="${y}">${d}</tspan>`
<tspan rotate="90" x="${scale(item.index) + viewConfig.X_OFFSET - 4}"
y="${y}">${d}</tspan>`;
})
.join('')
.join('');
})
.attr('x', (item, i) => {
return scale(item.index) + viewConfig.X_OFFSET - 4
return scale(item.index) + viewConfig.X_OFFSET - 4;
})
.attr('y', item => {
.attr('y', (item) => {
if (item.ismax) {
return (
viewConfig.topKeysPos + symbolArrowHeight * 1.5 + LINE_HEIGHT / 2
)
return viewConfig.topKeysPos + symbolArrowHeight * 1.5 + LINE_HEIGHT / 2;
} else {
return (
viewConfig.bottomKeysPosStart -
symbolArrowHeight / 2 -
LINE_HEIGHT / 2
)
return viewConfig.bottomKeysPosStart - symbolArrowHeight / 2 - LINE_HEIGHT / 2;
}
})
.attr('style', item => {
return `font-size:14px;fill:${
item.ismax ? 'red' : 'blue'
};font-weight: bold;`
})
.attr('style', (item) => {
return `font-size:14px;fill:${item.ismax ? 'red' : 'blue'};font-weight: bold;`;
});
}

View File

@@ -5,14 +5,13 @@
* @LastEditors: 程堡
* @Description: 体温单
* @FilePath: src\action\nurseStation\temperatureSheet\index.js
*/
import Request from '@/axios/index.js'
const temperaturePath = 'app/temperature'
const getTemperaturePath = 'app/temperature/by-encounter-id'
const delTemperaturePath = 'app/temperature/retired'
*/
import Request from '@/axios/index.js';
const temperaturePath = 'app/temperature';
const getTemperaturePath = 'app/temperature/by-encounter-id';
const delTemperaturePath = 'app/temperature/retired';
// import data from '../temperatureSheet/datas.js';
export const API = {
/**
* @description 查询患者体温单
* @param encounterId
@@ -23,27 +22,24 @@ export const API = {
method: 'get', // 提交方法get
verification: true, // 是否统一拦截验证
untoken: false, // 是否不带token
params: { // 参数列表
id
}
})
params: {
// 参数列表
id,
},
});
},
/**
* @description 创建体温单
* @param encounterId
*/
createTemperature(encounterId,
hisNo,
clinicCode,
recordTime,
type,
content) {
createTemperature(encounterId, hisNo, clinicCode, recordTime, type, content) {
return Request({
url: temperaturePath,
method: 'post',
verification: true,
untoken: false,
data: { // 参数列表
data: {
// 参数列表
encounterId,
hisNo,
clinicCode,
@@ -52,28 +48,23 @@ export const API = {
operName: sessionStorage.getItem('userName'),
type,
content,
hospitalOrgId: sessionStorage.getItem('hospitalOrgId')
}
})
hospitalOrgId: sessionStorage.getItem('hospitalOrgId'),
},
});
},
/**
* @description 修改体温单
* @param encounterId
*/
updateTemperature(encounterId,
hisNo,
clinicCode,
recordTime,
type,
content,
id) {
updateTemperature(encounterId, hisNo, clinicCode, recordTime, type, content, id) {
return Request({
url: temperaturePath,
method: 'put',
verification: true,
untoken: false,
data: { // 参数列表
data: {
// 参数列表
encounterId,
hisNo,
clinicCode,
@@ -82,9 +73,9 @@ export const API = {
operName: sessionStorage.getItem('userName'),
type,
content,
id
}
})
id,
},
});
},
/**
* @description 删除记录
@@ -96,10 +87,11 @@ export const API = {
method: 'post',
verification: true,
untoken: false,
data: { // 参数列表
id
}
})
data: {
// 参数列表
id,
},
});
},
/**
* @description 获取患者时间线
@@ -107,24 +99,22 @@ export const API = {
*/
NewSheet(patientInfo) {
return {
grParamBOS:
{
age: patientInfo.age,
birth: 868723200000,
cwh: patientInfo.bedName,
cardNo: patientInfo.hisId,
hospDate: patientInfo.firstInBedTime.substring(0, 10),
inDate: patientInfo.firstInBedTime,
inDiagName: patientInfo.diag,
name: patientInfo.patientName,
deptName: patientInfo.deptName,
operaDays: null,
outdate: patientInfo.checkOutWardTime,
sex: patientInfo.gender.display
},
grParamBOS: {
age: patientInfo.age,
birth: 868723200000,
cwh: patientInfo.bedName,
cardNo: patientInfo.hisId,
hospDate: patientInfo.firstInBedTime.substring(0, 10),
inDate: patientInfo.firstInBedTime,
inDiagName: patientInfo.diag,
name: patientInfo.patientName,
deptName: patientInfo.deptName,
operaDays: null,
outdate: patientInfo.checkOutWardTime,
sex: patientInfo.gender.display,
},
rows: [],
types: []
}
}
}
types: [],
};
},
};

View File

@@ -1,264 +1,296 @@
// 医院名称
// export const HospitalName = '中国人民解放军联勤保障部队第九六四医院'
export const HospitalName = ''
export const HospitalName = '';
// 体温单名称
export const temperatureName = '体 温 单'
export const temperatureName = '体 温 单';
// 患者信息显示项目(修改显示顺序及是否显示)
export const INFO_KEYS = [
{
name: '姓名',
key: 'name',
order: 1,
show: true
show: true,
},
{
name: '科室',
key: 'deptName',
name: '性别',
key: 'sex',
order: 2,
show: true
show: true,
},
{
name: '年龄',
key: 'age',
order: 3,
show: true,
},
{
name: '病室',
key: 'deptName',
order: 4,
show: true,
},
{
name: '床号',
key: 'cwh',
order: 3,
show: true
},
{
name: '病人ID',
key: 'patientId',
order: 4,
show: true
},
{
name: '住院号',
key: 'hosNum',
order: 5,
show: true
show: true,
},
// {
// name: '入院日期',
// key: 'hospDate',
// order: 6,
// show: true
// }
]
{
name: '病历号',
key: 'hosNum',
order: 6,
show: true,
},
{
name: '入院日期',
key: 'hospDate',
order: 6,
show: false,
},
{
name: '诊断',
key: 'inDiagName',
order: 8,
show: false,
},
];
// 体温单录入项目(修改显示顺序及是否显示)
// 新增项目注意 key值、code值唯一性不要与之前的项目重复
export const BOTTOM_KEYS = [
{
name: '血\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0压',
name: '血mmHg',
code: 'bloodPressure',
key: '008',
times: 2,
order: 1,
show: true
show: true,
},
{
name: '大便次数',
name: '大\u00A0便\u00A0次\u00A0数',
code: 'poop',
key: '005',
order: 2,
show: true
show: true,
},
{
name: '体\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0重',
code: 'weight',
key: '009',
order: 3,
show: true
},
{
name: '尿\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0量',
name: '\u00A0便\u00A0\u00A0',
code: 'urine',
key: '004',
order: 4,
show: true
order: 3,
show: true,
},
{
name: '摄入液量',
name: '液体总入量ml',
code: 'input',
key: '006',
order: 5,
show: true
order: 4,
show: true,
},
{
name: '排出液量',
name: '其\u00A0\u00A0他ml',
code: 'output',
key: '007',
order: 6,
show: true
order: 5,
show: true,
},
{
name: '术后天数',
code: 'output',
key: '010',
name: '尿\u00A0\u00A0量ml',
code: 'urineOutput',
key: '011',
order: 6,
show: true,
},
{
name: '体\u00A0\u00A0重kg',
code: 'weight',
key: '009',
order: 8,
show: true,
},
{
name: '身\u00A0\u00A0高cm',
code: 'height',
key: '030',
order: 9,
show: true,
},
{
name: 'SPO2',
code: 'SPO2',
key: '021',
times: 2,
order: 10,
show: false,
},
{
name: '皮\u00A0试\u00A0阳\u00A0性',
code: 'allergy',
key: '017',
order: 11,
show: true,
lines: 2,
},
{
name: '过\u00A0敏\u00A0试\u00A0验',
code: 'allergyTest',
key: '020',
order: 12,
show: false,
lines: 2,
},
{
name: '其\u00A0\u00A0他',
code: 'other',
key: '025',
order: 7,
show: true
show: false,
lines: 2,
},
{
name: '',
code: 'useless1',
key: '100',
order: 20,
show: true
show: true,
},
{
name: '',
code: 'useless2',
key: '100',
order: 21,
show: true
},
{
name: '',
code: 'useless3',
key: '100',
order: 22,
show: true
},
{
name: '',
code: 'useless4',
key: '100',
order: 23,
show: true
},
{
name: '',
code: 'useless5',
key: '100',
order: 24,
show: true
},
{
name: '',
code: 'useless6',
key: '100',
order: 25,
show: true
}
]
];
// 是否显示疼痛评分
export const showPainFlag = true
export const showPainFlag = true;
/** *********** 以上信息是本地化可以修改的部分 **************************/
// 获取患者信息显示项目
export function getInfoKeys() {
return INFO_KEYS.filter(x => x.show).sort((a, b) => a.order - b.order)
return INFO_KEYS.filter((x) => x.show).sort((a, b) => a.order - b.order);
}
// 获取患者信息显示项目
export function getBottomKeys() {
return BOTTOM_KEYS.filter(x => x.show).sort((a, b) => a.order - b.order)
return BOTTOM_KEYS.filter((x) => x.show).sort((a, b) => a.order - b.order);
}
export const timeNumber = [2, 6, 10, 14, 18, 22] // 时间展示
export const nightTime = [2, 18, 22] // 夜间红色高亮时间
export const timeNumber = [2, 6, 10, 14, 18, 22]; // 时间展示
export const nightTime = [2, 18, 22]; // 夜间红色高亮时间
export const leftTEXT = [
['脉搏,(次/分),180', '160', '140', '120', '100', '80', '60', '40'],
['体温,(℃), 42', '41', '40', '39', '38', '37', '36', '35']
]
['体温,(℃), 42', '41', '40', '39', '38', '37', '36', '35'],
];
export const inOutItem = ['入观', '分娩', '手术', '转入', '出观', '死亡']
export const otherItem = ['外出', '请假', '拒测', '离院', '其他']
export const inOutItem = ['入观', '分娩', '手术', '转入', '出观', '死亡'];
export const otherItem = ['外出', '请假', '拒测', '离院', '其他'];
export const sheetOptions = {
locations: [{
code: '1',
display: '体温'
}, {
code: '2',
display: '口温'
}, {
code: '3',
display: '肛温'
}, {
code: '4',
display: '温'
}],
breath: [{
code: '',
display: '自主呼吸'
}, {
code: '®',
display: '机械通气'
}],
poop: [{
code: '',
display: '正常'
}, {
code: '',
display: '失禁'
}, {
code: '☆',
display: '人工肛门'
}, {
code: '/E',
display: '灌肠'
}],
pee: [{
code: '',
display: '正常'
}, {
code: '※',
display: '失禁'
}, {
code: 'C+',
display: '导尿'
}],
locations: [
{
code: '1',
display: '体温',
},
{
code: '2',
display: '口温',
},
{
code: '3',
display: '温',
},
{
code: '4',
display: '耳温',
},
],
breath: [
{
code: '',
display: '自主呼吸',
},
{
code: '®',
display: '机械通气',
},
],
poop: [
{
code: '',
display: '正常',
},
{
code: '',
display: '失禁',
},
{
code: '☆',
display: '人工肛门',
},
{
code: '/E',
display: '灌肠',
},
],
pee: [
{
code: '',
display: '正常',
},
{
code: '※',
display: '失禁',
},
{
code: 'C+',
display: '导尿',
},
],
heart: ['窦性心律', '起搏心律', '房性心律', '异常心律'],
weight: ['正常', '卧床', '轮椅', '平车']
}
weight: ['正常', '卧床', '轮椅', '平车'],
};
// 此处是显示数字的 要和leftTEXT保持一直
export const bodyTemperature = [34, 42]
export const starNumEnv = bodyTemperature[0] // 开始体温
export const endNumEnv = bodyTemperature[1] // 结束体温
export const heartRange = [20, 180]
export const bodyTemperature = [34, 42];
export const starNumEnv = bodyTemperature[0]; // 开始体温
export const endNumEnv = bodyTemperature[1]; // 结束体温
export const heartRange = [20, 180];
// 中间图表字段对应
export const CHART_KEYS = [
{
key: '001',
code: 'breath',
name: '呼吸'
name: '呼吸',
},
{
key: '002',
code: 'sphygmus',
name: '脉搏'
name: '脉搏',
},
{
key: '003',
code: 'temperature',
name: '不升'
name: '不升',
},
{
key: '012',
code: 'inOut',
name: '特殊标记'
name: '特殊标记',
},
{
key: '013',
code: 'refuse',
name: '标记内容'
name: '标记内容',
},
{
key: '014',
code: 'heartRate',
name: '心率'
name: '心率',
},
{
key: '015',
code: 'lowerTemp',
name: '物理降温'
name: '物理降温',
},
{
key: '016',
code: 'painScore',
name: '疼痛评分'
}
]
name: '疼痛评分',
},
];
export const HEAD_HEIGHT = 120 // 头部文字预留位置
export const LINE_HEIGHT = 20 // 一行的行高
export const textLeftMargin = 4 // 文字左边边距
export const TEXT_MARGIN_BOTTOM = 6 // 文字向上偏移量
export const symbolArrowHeight = 20
export const HEAD_HEIGHT = 120; // 头部文字预留位置
export const LINE_HEIGHT = 20; // 一行的行高
export const textLeftMargin = 4; // 文字左边边距
export const TEXT_MARGIN_BOTTOM = 6; // 文字向上偏移量
export const symbolArrowHeight = 20;

View File

@@ -1,9 +1,7 @@
import moment from 'moment'
import moment from 'moment';
export function getG(svg, translateX, translateY) {
return svg
.append('g')
.attr('transform', `translate(${translateX},${translateY})`)
return svg.append('g').attr('transform', `translate(${translateX},${translateY})`);
}
// function generatePointer ({ pathData, type, yScaleInstance }) {
@@ -51,247 +49,259 @@ export function getG(svg, translateX, translateY) {
// }
// }
export function getTypeDatas(typeData, beginDate) {
const types = typeData.sort((a, b) => new Date(a.date) - new Date(b.date))
return types.map(item => {
const types = typeData.sort((a, b) => new Date(a.date) - new Date(b.date));
return types.map((item) => {
return {
index: getIndex(beginDate, item.date, '00:00:00') / 6,
times: 0,
date: item.date,
typeCode: item.typeCode,
typeValue: (item.typeValue) || null
}
})
typeValue: item.typeValue || null,
};
});
}
export function getTypeData(type, allData = [], isNumber = true, beginDate) {
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date,
value: (isNumber ? +cur.typeValue : cur.typeValue) || null
}
value: (isNumber ? +cur.typeValue : cur.typeValue) || null,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
return getList.sort((a, b) => a.index - b.index)
return item;
});
return getList.sort((a, b) => a.index - b.index);
}
// 获取不升
export function getType(type, allData = [], beginDate) {
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date,
value: cur.typeValue !== '' ? (parseFloat(cur.typeValue) <= 35 ? '不升' : null) : null
}
value: cur.typeValue !== '' ? (parseFloat(cur.typeValue) <= 35 ? '不升' : null) : null,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
return getList.sort((a, b) => a.index - b.index)
return item;
});
return getList.sort((a, b) => a.index - b.index);
}
// 合并标记内容
export function setMergeTag(ymbolTextArr = [], symbolContent = []) {
const arr = []
ymbolTextArr.forEach(item => {
symbolContent.forEach(res => {
const arr = [];
ymbolTextArr.forEach((item) => {
symbolContent.forEach((res) => {
if (item.index === res.index) {
arr.push({ ...item, ...res, value: (item.value !== null ? item.value : '') + (res.value !== null ? res.value : '') })
arr.push({
...item,
...res,
value: (item.value !== null ? item.value : '') + (res.value !== null ? res.value : ''),
});
}
})
})
return arr
});
});
return arr;
}
// 筛选手术产后日数
export function postpartumDays(type, arr) {
return arr.filter(item => item.typeCode === type).map(i => i.typeValue)
return arr.filter((item) => item.typeCode === type).map((i) => i.typeValue);
}
// 筛选体温
export function getTypeAnimalHeat(type, allData = [], code, beginDate) {
const getList = allData
.map((rowBOSItem, index) => {
console.log(rowBOSItem.rowBOS, 'rowBOSItem.rowBOS')
const rowBOS = rowBOSItem.rowBOS
const curList = // 改为 filter 来获取所有符合条件的项
rowBOS.filter((item) => {
return item.typeCode === type
}) || []
// 针对每个 cur 处理并生成新的对象数组
return curList.map((cur) => ({
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date,
value: (+cur.collectionMode === +code ? +cur.typeValue : null) || null
}))
value: (+cur.collectionMode === +code ? +cur.typeValue : null) || null,
};
})
.flat() // 将二维数组展平为一维数组
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
console.log(getList, 'getList')
return getList.sort((a, b) => a.index - b.index)
return item;
});
return getList.sort((a, b) => a.index - b.index);
}
// 设置折线
export function getBrokenLine(type, allData = [], arr = [], list = [], topList = [], beginDate) {
const result = []
const result = [];
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
rowBOS.find((item) => {
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date,
value: +cur.typeValue
}
value: +cur.typeValue,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
const _a = arr.filter(item => item.value)
const _b = list.filter(item => item.value)
const _c = topList.filter(item => item.value)
const mergingData = [..._a, ..._b, ..._c, { index: 50 }].sort((a, b) => a.index - b.index)
let start = 0
mergingData.forEach(item => {
const _p = getList.sort((a, b) => a.index - b.index).slice(start, item.index)
start = item.index + 1
result.push(_p)
})
return result
return item;
});
const _a = arr.filter((item) => item.value);
const _b = list.filter((item) => item.value);
const _c = topList.filter((item) => item.value);
const mergingData = [..._a, ..._b, ..._c, { index: 50 }].sort((a, b) => a.index - b.index);
let start = 0;
mergingData.forEach((item) => {
const _p = getList.sort((a, b) => a.index - b.index).slice(start, item.index);
start = item.index + 1;
result.push(_p);
});
return result;
}
// 处理脉搏心率
export function getHeartRate(type, allData = [], arr = [], topList = [], isNumber = true, beginDate) {
const result = []
export function getHeartRate(
type,
allData = [],
arr = [],
topList = [],
isNumber = true,
beginDate
) {
const result = [];
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
rowBOS.find((item) => {
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date,
value: (isNumber ? +cur.typeValue : cur.typeValue) || null
}
value: (isNumber ? +cur.typeValue : cur.typeValue) || null,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
const _a = arr.filter(item => item.value)
const _c = topList.filter(item => item.value)
const mergingData = [..._a, ..._c, { index: 50 }].sort((a, b) => a.index - b.index)
let start = 0
mergingData.forEach(item => {
const _p = getList.slice(start, item.index).sort((a, b) => { return a.index - b.index })
start = item.index
result.push(_p)
})
return result
return item;
});
const _a = arr.filter((item) => item.value);
const _c = topList.filter((item) => item.value);
const mergingData = [..._a, ..._c, { index: 50 }].sort((a, b) => a.index - b.index);
let start = 0;
mergingData.forEach((item) => {
const _p = getList.slice(start, item.index).sort((a, b) => {
return a.index - b.index;
});
start = item.index;
result.push(_p);
});
return result;
}
function getIndex(beginDate, date, time) {
if (beginDate === undefined || date === undefined) return
const diffTime = moment(date.substring(0, 10)).diff(moment(beginDate.substring(0, 10))) / 1000 / 3600 / 24
const diffIndex = parseInt(time.substring(0, 2))
return diffTime * 6 + Math.floor(diffIndex / 4)
if (beginDate === undefined || date === undefined) return;
const diffTime =
moment(date.substring(0, 10)).diff(moment(beginDate.substring(0, 10))) / 1000 / 3600 / 24;
const diffIndex = parseInt(time.substring(0, 2));
return diffTime * 6 + Math.floor(diffIndex / 4);
}
// 筛选35和40~42数据
export function degreesOnline(allData, data, type) {
const arr = allData.map((item, index) => {
const cur = data?.find(res => item.value === res.name && +res.isShowPlace === type) || null
const cur = data?.find((res) => item.value === res.name && +res.isShowPlace === type) || null;
return {
index: index,
date: item.date,
value: cur?.name || null
}
})
return arr
value: cur?.name || null,
};
});
return arr;
}
// 35或42上断开的事件
export function disconnectEvents(allData, data, code, type) {
const arr = allData.map((item, index) => {
const cur = data?.find(res => item.value === res.name && +res.isShowPlace === type && +res[code] === 1) || null
const cur =
data?.find(
(res) => item.value === res.name && +res.isShowPlace === type && +res[code] === 1
) || null;
return {
index: index,
date: item.date,
value: cur?.name || null
}
})
return arr
value: cur?.name || null,
};
});
return arr;
}
export function getDrawCoolData(type, allData = [], isNumber = true, beginDate) {
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date + cur.times,
value: (isNumber ? +cur.typeValue : cur.typeValue) || null
}
value: (isNumber ? +cur.typeValue : cur.typeValue) || null,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
return getList.sort((a, b) => a.index - b.index)
return item;
});
return getList.sort((a, b) => a.index - b.index);
}
// 筛选降温getDrawData
export function getDrawData(type, allData = [], code, beginDate) {
const getList = allData
.map((rowBOSItem, index) => {
const rowBOS = rowBOSItem.rowBOS
const rowBOS = rowBOSItem.rowBOS;
const cur =
rowBOS.find((item) => {
return item.typeCode === type
}) || {}
return item.typeCode === type;
}) || {};
return {
index: getIndex(beginDate, cur.date, cur.times),
date: cur.date + cur.times,
value: (+cur.collectionMode === +code ? +cur.typeValue : null) || null
}
value: (+cur.collectionMode === +code ? +cur.typeValue : null) || null,
};
})
.map((item) => {
if (item.value) {
// item.value = NaN
}
return item
})
return getList.sort((a, b) => a.index - b.index)
return item;
});
return getList.sort((a, b) => a.index - b.index);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -212,4 +212,4 @@ aside {
display: flex;
align-items: center;
width: 100%;
}
}

View File

@@ -2,7 +2,7 @@
<div class="recordBill">
<div :id="'exeSheetTitle' + printData.id" class="printView_header">
<div style="text-align: center; height: 60px">
呼和浩特市第一医院医嘱执行单
长春市朝阳区中医院医嘱执行单
</div>
<div>
<span style="display: inline-block; width: 100px">床号{{ printData.patientInfo.encounterLocationName }}</span>

View File

@@ -2,7 +2,7 @@
<div class="recordBill">
<div id="div1" class="printView_header">
<div style="text-align: center; font-size: 20px; height: 40px">
呼和浩特市第一医院输液执行单
长春市朝阳区中医院输液执行单
</div>
<div>
<span>座位{{ printData.patientInfo.encounterLocationName }}</span>

View File

@@ -1,6 +1,6 @@
<template>
<div class="printTicket">
<p>xx人民医院</p>
<p>长春市朝阳区中医院</p>
<div>
<span>姓名</span>
<span>{{ printData.patientName }}</span>
@@ -21,11 +21,11 @@
<span>分诊时间</span>
<span>{{ printData.triageTime }}</span>
</div>
<img ref="refQr" style="position: absolute; top: 10px; left: 100px">
<img ref="refQr" style="position: absolute; top: 10px; left: 100px" />
</div>
</template>
<script>
import JsBarcode from 'jsbarcode'
import JsBarcode from 'jsbarcode';
export default {
name: 'TriageTicket',
props: {
@@ -37,41 +37,33 @@ export default {
dept: '',
triageLevel: '',
triageTime: '',
hisId: ''
}
}
}
hisId: '',
};
},
},
},
data() {
return {
}
return {};
},
updated() {
JsBarcode(this.$refs.refQr,
this.printData.hisId,
{
format: 'CODE128',
lineColor: '#000',
background: '#fff',
displayValue: false,
height: 30,
margin: 2
})
JsBarcode(this.$refs.refQr, this.printData.hisId, {
format: 'CODE128',
lineColor: '#000',
background: '#fff',
displayValue: false,
height: 30,
margin: 2,
});
},
mounted() {
},
methods: {
}
}
mounted() {},
methods: {},
};
</script>
<style>
.printTicket{
display: block;
width: 300px;
height: 200px;
border: 1px solid #A3A3A3;
}
.printTicket {
display: block;
width: 300px;
height: 200px;
border: 1px solid #a3a3a3;
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div class="page-container">
<div v-if="$slots.header" class="page-header">
<slot name="header" />
</div>
<div class="page-content">
<slot />
</div>
<div v-if="$slots.footer" class="page-footer">
<slot name="footer" />
</div>
</div>
</template>
<style scoped lang="scss">
.page-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 16px;
box-sizing: border-box;
}
.page-header {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 16px;
}
.page-content {
flex: 1;
min-height: 0;
overflow-y: auto;
}
.page-footer {
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #ebeef5;
}
</style>

View File

@@ -0,0 +1,12 @@
import request from '@/utils/request';
/**
* 获取住院患者列表
*/
export function getPatientList(queryParams) {
return request({
url: '/reg-doctorstation/advice-manage/reg-patient-zk',
method: 'get',
params: queryParams,
});
}

View File

@@ -0,0 +1,695 @@
<template>
<div class="patientList-container" :class="{ 'patientList-container-unexpand': !currentExpand }">
<div
class="patientList-operate"
:class="{ 'patientList-operate-unexpand': !currentExpand }"
v-if="currentExpand"
>
<el-input
class="patientList-search-input"
placeholder="床号/住院号/姓名"
v-model="searchKeyword"
@keyup.enter="handleSearch"
:prefix-icon="Search"
/>
<el-button class="icon-btn" circle @click="handleRefresh" type="text" plain>
<el-icon icon-class="Refresh" size="24" :class="{ 'is-rotating': refreshing }">
<Refresh />
</el-icon>
</el-button>
</div>
<transition name="patient-list-toggle" mode="out-in">
<div key="expanded" class="patientList-list" v-if="currentExpand">
<div class="patient-cards" v-loading="isLoading">
<template v-if="filteredCardData && filteredCardData.length > 0">
<el-scrollbar ref="expandScrollbarRef" class="patient-cards-scrollbar">
<div
class="patient-card"
v-for="item in filteredCardData"
:key="item.encounterId"
:id="item.encounterId"
@click="handleItemClick(item)"
:class="{ actived: activeCardId === item.encounterId }"
>
<div class="patient-card-header">
<div class="header-top">
<div class="bed-container">
<div class="bed">
<div class="bed-info">
<div v-if="item.houseName" class="house-name">{{ item.houseName }}</div>
<div class="bed-name">{{ item.bedName || '未分床' }}</div>
</div>
</div>
</div>
<el-space>
<el-tag
v-if="item.statusEnum_enumText"
size="small"
class="payer-tag-status"
effect="light"
:style="getStatusStyle(item.statusEnum)"
>
{{ item.statusEnum_enumText }}
</el-tag>
<el-tag
v-if="item.contractName"
size="small"
class="payer-tag"
effect="light"
>
{{ item.contractName }}
</el-tag>
</el-space>
</div>
<div class="header-bottom">
<span class="bus-no">住院号{{ item.busNo || '-' }}</span>
<span class="insurance-type" v-if="item.insutype_dictText">
险种类型{{ item.insutype_dictText }}
</span>
</div>
</div>
<div class="doctor-parent-line" />
<div class="patient-card-body">
<div class="personal-info-container">
<div class="name-container">
<div class="name">
<el-text :text="item.patientName" tclass="name" width="auto">
{{ item.patientName || '-' }}
</el-text>
</div>
<div class="age">
<el-tag
size="small"
class="age-tag"
effect="plain"
:class="{ 'age-tag-female': item.genderEnum_enumText === '女性' }"
>
{{ item.genderEnum_enumText || '-' }}
<span v-if="item.age"> · {{ item.age }}</span>
</el-tag>
</div>
</div>
</div>
</div>
</div>
</el-scrollbar>
</template>
<el-empty v-else description="暂无数据" />
</div>
</div>
<div
key="collapsed"
class="patientList-list"
v-else
v-loading="isLoading"
:class="{ 'patientList-list-unexpand': !currentExpand }"
>
<el-scrollbar ref="contractScrollbarRef" class="patient-cards-scrollbar">
<template v-if="filteredCardData && filteredCardData.length > 0">
<el-tooltip
v-for="item in filteredCardData"
:show-after="200"
:key="item.encounterId"
:show-arrow="true"
placement="right"
effect="light"
:offset="4"
>
<template #content>
<div class="card-tooltip">
<div class="card-tooltip-main">
<span class="card-tooltip-bed">{{ item.bedName }}</span>
<span class="card-tooltip-name">{{ item.patientName }}</span>
</div>
<div class="card-tooltip-sex">
<span class="card-tooltip-sex-text">
{{ item.genderEnum_enumText || '-' }}
<span v-if="item.age"> · {{ item.age }}</span>
</span>
</div>
</div>
</template>
<div>
<div
class="card-small"
:class="{ 'patient-active': activeCardId === item.encounterId }"
@click="handleSmallCardClick(item)"
:key="item.encounterId"
>
{{ item.bedName }}
</div>
<div class="patient-card-small-border"></div>
</div>
</el-tooltip>
</template>
<el-empty v-else description="暂无数据" :image-size="50" />
</el-scrollbar>
</div>
</transition>
<div
class="patientList-toggle-btn-wrap"
:class="{ 'patientList-toggle-btn-wrap-unexpand': !currentExpand }"
>
<el-button class="icon-btn" circle @click="updateExpand">
<el-icon class="svg-sty-menu" size="24">
<Expand v-if="!currentExpand" />
<Fold v-if="currentExpand" />
</el-icon>
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { Search, Refresh, Expand, Fold } from '@element-plus/icons-vue';
import type { ElScrollbar } from 'element-plus';
interface PatientCardItem {
encounterId: string;
bedName?: string;
busNo?: string;
patientName?: string;
genderEnum_enumText?: string;
age?: number | string;
contractName?: string;
[key: string]: any;
}
interface Props {
// 过滤后的卡片数据
filteredCardData?: PatientCardItem[];
// 当前激活的卡片ID
activeCardId?: string;
// 是否展开(不传则为不受控模式,组件内部自己管理)
expand?: boolean;
// 加载状态
loading?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
filteredCardData: () => [],
activeCardId: '',
expand: undefined,
loading: false,
});
interface Emits {
(e: 'item-click', item: PatientCardItem): void;
(e: 'search', keyword: string): void;
(e: 'refresh'): void;
(e: 'update:expand', value: boolean): void;
}
const emit = defineEmits<Emits>();
const searchKeyword = ref<string>('');
const refreshing = ref<boolean>(false);
const refreshLoading = ref<boolean>(false);
const internalExpand = ref<boolean>(true);
const isControlled = computed<boolean>(() => props.expand !== undefined);
const currentExpand = computed<boolean>(() => {
return isControlled.value ? props.expand! : internalExpand.value;
});
const isLoading = computed<boolean>(() => {
return props.loading || refreshLoading.value;
});
const handleItemClick = (item: PatientCardItem): void => {
emit('item-click', item);
};
const handleSmallCardClick = (item: PatientCardItem): void => {
emit('item-click', item);
};
const handleSearch = (): void => {
emit('search', searchKeyword.value);
};
const handleRefresh = (): void => {
if (refreshing.value) return;
refreshing.value = true;
refreshLoading.value = true;
emit('refresh');
setTimeout(() => {
refreshing.value = false;
}, 600);
setTimeout(() => {
if (!props.loading) {
refreshLoading.value = false;
}
}, 500);
};
const updateExpand = (): void => {
const newValue = !currentExpand.value;
if (isControlled.value) {
emit('update:expand', newValue);
} else {
internalExpand.value = newValue;
}
};
// 根据状态枚举值返回文本颜色
const getStatusColor = (statusEnum?: number): string => {
if (statusEnum === undefined || statusEnum === null) {
return '';
}
switch (statusEnum) {
case 2: // REGISTERED - 待入科
return '#E6A23C'; // 橙色
case 3: // AWAITING_DISCHARGE - 待出院
return '#F56C6C'; // 红色
case 4: // DISCHARGED_FROM_HOSPITAL - 待出院结算
return '#909399'; // 灰色
case 5: // ADMITTED_TO_THE_HOSPITAL - 已入院
return '#67C23A'; // 绿色
case 6: // PENDING_TRANSFER - 待转科
return '#409EFF'; // 蓝色
default:
return '';
}
};
// 根据状态枚举值返回带透明背景的样式
const getStatusStyle = (statusEnum?: number): Record<string, string> => {
const color = getStatusColor(statusEnum);
if (!color) {
return {};
}
// 不同状态对应的半透明背景色
let backgroundColor = '';
switch (statusEnum) {
case 2: // 橙色
backgroundColor = 'rgba(230, 162, 60, 0.12)';
break;
case 3: // 红色
backgroundColor = 'rgba(245, 108, 108, 0.12)';
break;
case 4: // 灰色
backgroundColor = 'rgba(144, 147, 153, 0.12)';
break;
case 5: // 绿色
backgroundColor = 'rgba(103, 194, 58, 0.12)';
break;
case 6: // 蓝色
backgroundColor = 'rgba(64, 158, 255, 0.12)';
break;
default:
backgroundColor = 'rgba(148, 163, 184, 0.12)';
}
return {
color,
backgroundColor,
borderColor: 'transparent',
};
};
// 保险类型映射表
const insuranceTypeMap: Record<number, string> = {
310: '职工基本医疗保险',
320: '公务员医疗补助',
330: '大额医疗费用补助',
340: '离休人员医疗保障',
350: '一至六级残废军人医疗补助',
360: '老红军医疗保障',
370: '企业补充医疗保险',
380: '新型农村合作医疗',
390: '城乡居民基本医疗保险',
391: '城镇居民基本医疗保险',
392: '城乡居民大病医疗保险',
399: '其他特殊人员医疗保障',
410: '长期照护保险',
};
watch(
() => props.loading,
(newLoading) => {
if (!newLoading && refreshLoading.value) {
refreshLoading.value = false;
}
}
);
</script>
<style lang="scss" scoped>
.patient-card {
width: 100%;
overflow: hidden;
background-color: #fff;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.04);
box-shadow: 0 2px 6px 0 rgba(15, 35, 52, 0.12);
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgba(15, 35, 52, 0.18);
transform: translateY(-1px);
}
&.actived {
background-color: rgba(7, 155, 140, 0.06);
border-color: var(--el-color-primary);
box-shadow: 0 0 0 1px rgba(7, 155, 140, 0.3), 0 4px 14px rgba(7, 155, 140, 0.25);
}
.patient-card-header {
display: flex;
flex-direction: column;
padding: 10px 12px 4px;
.header-top {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.bed-container {
display: flex;
flex: 1;
align-items: center;
min-width: 0;
.bed {
flex-grow: 0;
flex-shrink: 1;
min-width: 0;
.bed-info {
display: flex;
flex-direction: column;
line-height: 1.4;
.house-name {
color: #1f2933;
font-weight: 600;
font-size: 16px;
white-space: normal;
word-break: break-all;
}
.bed-name {
color: #1f2933;
font-weight: 600;
font-size: 16px;
white-space: normal;
word-break: break-all;
}
}
}
}
.payer-tag {
max-width: 120px;
font-size: 12px;
border-radius: 999px;
font-weight: bolder;
}
.payer-tag-status {
font-weight: bolder;
border-radius: 999px;
}
}
.header-bottom {
margin-top: 4px;
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
color: #6b7280;
font-size: 12px;
.bus-no {
white-space: nowrap;
}
.insurance-type {
white-space: nowrap;
}
}
}
.doctor-parent-line {
margin: 6px 12px 0;
border-bottom: 1px dashed #e5e7eb;
}
.patient-card-body {
padding: 8px 12px 10px;
.personal-info-container {
display: block;
.name-container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
.name {
color: #111827;
font-weight: 600;
font-size: 16px;
}
.age {
flex-shrink: 0;
.age-tag {
border-radius: 999px;
padding: 0 8px;
}
.age-tag-female {
border-color: rgb(255, 55, 158);
color: rgb(255, 126, 184);
}
}
}
}
}
}
.patientList-container {
height: 100%;
display: flex;
flex-direction: column;
height: 100%;
border-right: 1px solid #ebeef5;
background-color: #ffffff;
width: 240px;
min-width: 240px;
&-unexpand {
width: 84px;
min-width: 84px;
}
.patientList-operate {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 44px;
border-bottom: 1px solid #ebeef5;
flex: none;
padding: 0 0 0 8px;
&-unexpand {
justify-content: space-around;
}
}
.patientList-list {
display: flex;
flex: 1;
flex-direction: column;
height: 0;
width: 240px;
&-unexpand {
width: 84px;
}
.search-operate {
padding: 0 8px;
height: 48px;
display: flex;
align-items: center;
flex: none;
}
.patient-cards {
flex: 1;
padding: 0 8px;
overflow: hidden;
:deep(.patient-cards-scrollbar) {
width: 100%;
height: 100%;
.el-scrollbar__bar {
width: 0;
}
}
}
.card-small {
height: 44px;
padding: 0 10px 0 12px;
overflow: hidden;
font-size: 14px;
line-height: 44px;
white-space: nowrap;
text-overflow: ellipsis;
border-right: none;
cursor: pointer;
}
.patient-active {
background-color: #e6f7ff;
font-weight: 600;
color: var(--el-color-primary);
}
.patient-card-small-border {
display: block;
width: 100%;
height: 2px;
background-color: #f1faff;
}
}
}
.patientList-toggle-btn-wrap {
display: flex;
justify-content: flex-end;
padding: 4px 16px 8px;
&-unexpand {
justify-content: center;
}
}
.patient-list-toggle-enter-active,
.patient-list-toggle-leave-active {
transition: transform 0.2s ease;
}
.patient-list-toggle-enter-from,
.patient-list-toggle-leave-to {
opacity: 0;
transform: translateY(-8px);
}
.card-tooltip {
display: inline-flex;
align-items: center;
justify-content: space-between;
min-width: 140px;
max-width: 220px;
padding: 6px 10px;
border-radius: 6px;
background-color: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
.card-tooltip-main {
display: flex;
flex-direction: column;
margin-right: 8px;
}
.card-tooltip-bed {
font-size: 15px;
font-weight: 600;
color: #111827;
margin-bottom: 2px;
}
.card-tooltip-name {
font-size: 13px;
color: #4b5563;
}
.card-tooltip-sex {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
}
.card-tooltip-sex-text {
font-size: 12px;
color: #6b7280;
}
.svg-gray {
fill: var(--hip-color-text-unit);
}
.icon-btn {
border: none;
background-color: transparent;
box-shadow: none;
padding: 4px;
}
.is-rotating {
animation: patient-refresh-rotate 0.6s linear;
}
@keyframes patient-refresh-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
:deep(.scrollbar) {
width: 100%;
height: 100%;
.el-scrollbar__bar {
width: 0;
}
}
.f-16 {
font-weight: 600;
font-size: 16px;
}
.f-14 {
font-size: 14px;
}
.empty-wrap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.empty-text-sty {
margin-top: 0;
}
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<BasePatientList
:filtered-card-data="filteredCardData"
:active-card-id="cardId"
:loading="queryloading"
@item-click="handleItemClick"
@search="handleSearch"
@refresh="getList"
/>
</template>
<script setup>
import { computed, onMounted, reactive, ref, watch } from 'vue';
import BasePatientList from './index.vue';
import { getPatientList } from '@/views/inpatientDoctor/home/components/api';
defineOptions({
name: 'PatientListWithData',
});
const props = defineProps({
/** 接口 status 参数,默认 5在院 */
status: {
type: [String, Number],
default: undefined,
},
/** 首次加载自动选中第一条 */
autoSelectFirst: {
type: Boolean,
default: true,
},
/** 外部已选中的患者信息(用于避免重复自动选中) */
selectedPatient: {
type: Object,
default: null,
},
/**
* 选中患者时回调(给你外部写 store 用)
* (patient) => void
*/
onSelect: {
type: Function,
default: null,
},
});
const emit = defineEmits(['item-click']);
// 这段逻辑就是你说的 “@index.vue (4-11) 那套带数据的”
const searchData = reactive({
keyword: '',
patientType: 1,
type: 1,
timeLimit: 3,
});
const cardId = ref('');
const cardAllData = ref([]);
const isFirstLoad = ref(true);
const queryloading = ref(false);
const filteredCardData = computed(() => cardAllData.value);
const getList = () => {
queryloading.value = true;
getPatientList({ status: props.status, searchKey: searchData.keyword })
.then((res) => {
cardAllData.value = res?.data?.records || [];
})
.finally(() => {
queryloading.value = false;
});
};
watch(
() => filteredCardData.value,
(newData) => {
if (!props.autoSelectFirst) return;
if (
newData &&
newData.length > 0 &&
!cardId.value &&
isFirstLoad.value &&
!props.selectedPatient?.encounterId
) {
const firstPatient = newData[0];
if (firstPatient?.encounterId) {
handleItemClick(firstPatient);
isFirstLoad.value = false;
}
}
},
{ immediate: true }
);
const handleItemClick = (node) => {
cardId.value = node.encounterId;
props.onSelect?.(node);
emit('item-click', node);
};
const handleSearch = (keyword) => {
searchData.keyword = keyword;
getList();
};
onMounted(() => {
getList();
});
</script>

View File

@@ -0,0 +1,318 @@
<template>
<div class="pending-patient-list">
<div class="patient-cards" v-loading="loading">
<template v-if="list && list.length > 0">
<el-scrollbar class="patient-cards-scrollbar">
<div
class="patient-card"
v-for="(item, index) in list"
:key="item[idKey] ?? index"
:class="{ actived: !!item.active || activeId === item[idKey] }"
draggable="true"
@click="emit('item-click', item, index)"
@dblclick="emit('item-dblclick', item)"
@dragstart="(e) => emit('dragstart', e, item)"
@dragend="(e) => emit('dragend', e)"
>
<div class="patient-card-header">
<div class="header-top">
<div class="bed-container">
<div class="bed">
<el-text truncated tclass="bed-font" width="auto">
{{ item.bedName || '-' }}
</el-text>
</div>
</div>
<div class="header-tags">
<el-tag v-if="item.contractName" size="small" class="payer-tag" effect="light">
{{ item.contractName }}
</el-tag>
<el-tag
v-if="item.encounterStatus_enumText"
size="small"
class="status-tag"
effect="light"
:class="getStatusClass(item)"
>
{{ item.encounterStatus_enumText }}
</el-tag>
</div>
</div>
<div class="header-bottom">
<span class="bus-no">住院号{{ item.busNo || '-' }}</span>
</div>
<div class="meta">
<div class="meta-row">
<span class="meta-label">入院时间</span>
<span class="meta-value">{{
item.startTime ? formatDate(item.startTime) : '-'
}}</span>
</div>
<div class="meta-row">
<span class="meta-label">入科时间</span>
<span class="meta-value">{{
item.admissionTime ? formatDate(item.admissionTime) : '-'
}}</span>
</div>
</div>
</div>
<div class="doctor-parent-line" />
<div class="patient-card-body">
<div class="personal-info-container">
<div class="name-container">
<div class="name">
<el-text :text="item.patientName" tclass="name" width="auto">
{{ item.patientName || '-' }}
</el-text>
</div>
<div class="age">
<el-tag
size="small"
class="age-tag"
effect="plain"
:class="{
'age-tag-female': item.genderEnum_enumText === '女性',
'age-tag-male': item.genderEnum_enumText === '男性',
}"
>
{{ item.genderEnum_enumText || '-' }}
<span v-if="item.age"> · {{ item.age }}</span>
<span v-if="item.priorityEnum_enumText">
· {{ item.priorityEnum_enumText }}</span
>
</el-tag>
</div>
</div>
</div>
<div class="meta"></div>
</div>
</div>
</el-scrollbar>
</template>
<el-empty v-else description="暂无数据" />
</div>
</div>
</template>
<script setup lang="ts">
import { formatDate } from '@/utils/index';
withDefaults(
defineProps<{
list: any[];
activeId?: string | number;
idKey?: string;
loading?: boolean;
}>(),
{
list: () => [],
activeId: '',
idKey: 'id',
loading: false,
}
);
const emit = defineEmits<{
(e: 'item-click', item: any, index: number): void;
(e: 'item-dblclick', item: any): void;
(e: 'dragstart', event: DragEvent, item: any): void;
(e: 'dragend', event: DragEvent): void;
}>();
const getStatusClass = (item: any) => {
if (item?.encounterStatus == 2) return 'status-tag--blue';
if (item?.encounterStatus == 5) return 'status-tag--green';
return '';
};
</script>
<style scoped lang="scss" name="PendingPatientList">
.pending-patient-list {
height: 100%;
display: flex;
flex-direction: column;
min-height: 0;
min-width: 240px;
width: 240px;
}
.patient-cards {
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
:deep(.patient-cards-scrollbar) {
width: 100%;
height: 100%;
.el-scrollbar__bar {
width: 0;
}
}
}
.patient-card {
width: 100%;
overflow: hidden;
background-color: #fff;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.04);
box-shadow: 0 2px 6px 0 rgba(15, 35, 52, 0.12);
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgba(15, 35, 52, 0.18);
transform: translateY(-1px);
}
&.actived {
background-color: rgba(7, 155, 140, 0.06);
border-color: var(--el-color-primary);
box-shadow: 0 0 0 1px rgba(7, 155, 140, 0.3), 0 4px 14px rgba(7, 155, 140, 0.25);
}
}
.patient-card-header {
display: flex;
flex-direction: column;
padding: 10px 12px 4px;
.header-top {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.bed-container {
display: flex;
flex: 1;
align-items: center;
min-width: 0;
.bed {
flex-grow: 0;
flex-shrink: 1;
min-width: 0;
:deep(.bed-font) {
color: #1f2933;
font-weight: 600;
font-size: 16px;
}
}
}
.payer-tag {
max-width: 120px;
font-size: 12px;
border-radius: 999px;
}
.header-tags {
flex-shrink: 0;
display: flex;
align-items: flex-end;
justify-content: flex-end;
gap: 6px;
}
}
.header-bottom {
margin-top: 4px;
width: 100%;
display: flex;
justify-content: flex-start;
color: #6b7280;
font-size: 12px;
.bus-no {
white-space: nowrap;
}
}
}
.doctor-parent-line {
margin: 6px 12px 0;
border-bottom: 1px dashed #e5e7eb;
}
.patient-card-body {
padding: 8px 12px 10px;
}
.personal-info-container {
display: block;
.name-container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
.name {
color: #111827;
font-weight: 600;
font-size: 14px;
}
.age {
flex-shrink: 0;
}
}
}
.age-tag {
border-radius: 999px;
padding: 0 8px;
}
.age-tag-female {
border-color: rgb(255, 55, 158);
color: rgb(255, 126, 184);
}
.age-tag-male {
border-color: #91d5ff;
color: #1677ff;
}
.status-tag {
border-radius: 999px;
padding: 0 8px;
flex-shrink: 0;
font-weight: 600;
}
.status-tag--blue {
border-color: #91d5ff;
background-color: #1677ff;
color: white;
}
.status-tag--green {
border-color: #b7eb8f;
background-color: #389e0d;
color: white;
}
.meta {
margin-top: 6px;
color: #6b7280;
font-size: 12px;
line-height: 18px;
}
.meta-row {
display: flex;
}
.meta-label {
flex-shrink: 0;
}
</style>

View File

@@ -0,0 +1,338 @@
{
"panels": [
{
"index": 0,
"name": 1,
"paperType": "自定义",
"height": 80,
"width": 279,
"paperList": {
"type": "自定义",
"width": 279,
"height": 80
},
"panelPageRule": "none",
"paperHeader": 0,
"paperFooter": 422.3622047244095,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": true,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 15,
"height": 16.5,
"width": 792,
"title": "长春市朝阳区中医院预交金收据",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",
"letterSpacing": 0.75,
"textAlign": "center",
"qrCodeLevel": 0,
"fontSize": 15,
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 46.5,
"height": 14,
"width": 151.5,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "patientName",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 295.5,
"top": 48,
"height": 14,
"width": 148.5,
"title": "住院号",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "encounterNosd",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 480,
"top": 48,
"height": 14,
"width": 162,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "inHospitalOrgName",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 73.5,
"height": 14,
"width": 153,
"title": "ID号",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "patientId",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 295.5,
"top": 73.5,
"height": 14,
"width": 147,
"title": "医保类别",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "contractName",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 480,
"top": 73.5,
"height": 14,
"width": 163.5,
"title": "时间",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "currentTime",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 105,
"height": 25,
"width": 120,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"textContentVerticalAlign": "middle",
"borderLeft": "solid",
"borderTop": "solid",
"borderRight": "solid",
"borderBottom": "solid",
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 231,
"top": 105,
"height": 25,
"width": 393,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"textContentVerticalAlign": "middle",
"borderTop": "solid",
"borderRight": "solid",
"borderBottom": "solid",
"qrCodeLevel": 0,
"field": "balanceAmount",
"hideTitle": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 129,
"height": 30,
"width": 120,
"title": "人民币(大写)",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"textContentVerticalAlign": "middle",
"borderLeft": "solid",
"borderTop": "solid",
"borderRight": "solid",
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 231,
"top": 129,
"height": 30,
"width": 393,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"textContentVerticalAlign": "middle",
"borderTop": "solid",
"borderRight": "solid",
"qrCodeLevel": 0,
"field": "amountInWords",
"hideTitle": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 159,
"height": 30,
"width": 513,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"textContentVerticalAlign": "middle",
"borderLeft": "solid",
"borderRight": "solid",
"borderBottom": "solid",
"qrCodeLevel": 0,
"field": "paymentDetails",
"hideTitle": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 198,
"height": 14,
"width": 120,
"title": "签章",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "patientNamesfs",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 297,
"top": 198,
"height": 14,
"width": 132,
"title": "交款人",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "patientNameada",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 481.5,
"top": 198,
"height": 14,
"width": 124.5,
"title": "收款人",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "cashier",
"fontFamily": "Microsoft YaHei"
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
]
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,454 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A5",
"height": 210,
"width": 148,
"paperList": {
"type": "A5",
"width": 148,
"height": 210
},
"paperHeader": 0,
"paperFooter": 592.4409448818898,
"orient": 1,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.01,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "10px",
"rotate": 25,
"width": 100,
"height": 100,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 50,
"height": 20,
"width": 420,
"title": "处置单",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 154.5,
"top": 75,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 75,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 76.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 100.5,
"height": 13.5,
"width": 99,
"title": "开单医生",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 27,
"top": 102,
"height": 13.5,
"width": 99,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "departmentName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 154.5,
"top": 102,
"height": 13.5,
"width": 120,
"title": "费用性质",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 130.5,
"height": 13.5,
"width": 174,
"title": "门诊号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "encounterNo",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 222,
"top": 130.5,
"height": 13.5,
"width": 165,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 156,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28,
"top": 166.5,
"height": 9.75,
"width": 120,
"title": "Rp",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true,
"fontSize": 18,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 190.5,
"height": 36,
"width": 390,
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"textAlign": "center",
"tableBorder": "border",
"tableHeaderFontWeight": "500",
"field": "prescriptionList",
"columns": [
[
{
"title": "项目名",
"width": 116.47241770689429,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"width": 95.6749835266735,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"width": 87.60415049837947,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "执行次数",
"width": 90.24844826805275,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 10.5,
"top": 508.5,
"height": 13.5,
"width": 100,
"title": "医师",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "发药:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 214.5,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "划价:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 319,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "调配:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 532.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 289,
"top": 551.5,
"height": 13.5,
"width": 120,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "medTotalAmount",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

View File

@@ -0,0 +1,454 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A5",
"height": 210,
"width": 148,
"paperList": {
"type": "A5",
"width": 148,
"height": 210
},
"paperHeader": 0,
"paperFooter": 592.4409448818898,
"orient": 1,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.01,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "10px",
"rotate": 25,
"width": 100,
"height": 100,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市红旗中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 50,
"height": 20,
"width": 420,
"title": "处置单",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 154.5,
"top": 75,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 75,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 76.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 100.5,
"height": 13.5,
"width": 99,
"title": "开单医生",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 27,
"top": 102,
"height": 13.5,
"width": 99,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "departmentName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 154.5,
"top": 102,
"height": 13.5,
"width": 120,
"title": "费用性质",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 130.5,
"height": 13.5,
"width": 174,
"title": "门诊号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "encounterNo",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 222,
"top": 130.5,
"height": 13.5,
"width": 165,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 156,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28,
"top": 166.5,
"height": 9.75,
"width": 120,
"title": "Rp",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true,
"fontSize": 18,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 190.5,
"height": 36,
"width": 390,
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"textAlign": "center",
"tableBorder": "border",
"tableHeaderFontWeight": "500",
"field": "prescriptionList",
"columns": [
[
{
"title": "项目名",
"width": 116.47241770689429,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"width": 95.6749835266735,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"width": 87.60415049837947,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "执行次数",
"width": 90.24844826805275,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 10.5,
"top": 508.5,
"height": 13.5,
"width": 100,
"title": "医师",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "发药:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 214.5,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "划价:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 319,
"top": 509.0000066757202,
"height": 13.5,
"width": 100,
"title": "调配:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 532.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 289,
"top": 551.5,
"height": 13.5,
"width": 120,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "medTotalAmount",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,701 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A4",
"height": 297,
"width": 210,
"paperList": {
"type": "自定义",
"width": 80,
"height": 60
},
"paperHeader": 166.5,
"paperFooter": 760.5,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": true,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 54.5,
"height": 20,
"width": 595.5,
"title": "门诊病历",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 235.5,
"top": 81,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 429,
"top": 81,
"height": 13.5,
"width": 160.5,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 24,
"top": 82.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 24,
"top": 108,
"height": 13.5,
"width": 190.5,
"title": "电话",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "phone"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 235.5,
"top": 108,
"height": 13.5,
"width": 175.5,
"title": "医保类型",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 429,
"top": 108,
"height": 13.5,
"width": 162,
"title": "病历号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "busNo"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 24,
"top": 132,
"height": 13.5,
"width": 200,
"title": "就诊日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "onsetDate"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 235.5,
"top": 132,
"height": 13.5,
"width": 175.5,
"title": "就诊科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 429,
"top": 132,
"height": 13.5,
"width": 162,
"title": "接诊医生",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 163.5,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 25.5,
"top": 171,
"height": 13.5,
"width": 50,
"title": "主诉",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 187.5,
"height": 33,
"width": 550,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "complaint",
"lHeight": 50
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 231,
"height": 13.5,
"width": 50,
"title": "现病史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 253,
"height": 42,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "presentIllness",
"title": "无",
"lHeight": 30
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 305,
"height": 13.5,
"width": 50,
"title": "既往史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 324,
"height": 30,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "pastIllness",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 366,
"height": 13.5,
"width": 50,
"title": "过敏史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 384,
"height": 22.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "allergyHistory",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 412.5,
"height": 13.5,
"width": 50,
"title": "家族史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 432,
"height": 22.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "familyHistory",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 465,
"height": 13.5,
"width": 50,
"title": "个人史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 486,
"height": 28.5,
"width": 550,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "personalHistory",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 522.2500162124634,
"height": 13.5,
"width": 82.5,
"title": "体格检查",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 544.7500162124634,
"height": 30,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"title": "长文",
"lHeight": 20,
"formatter": "function(title, value, options, templateData, target, paperNo) {\n // 获取所有体检数据\n const bloodHigh = templateData.bloodHigh || '';\n const bloodLow = templateData.bloodLow || '';\n const breathe = templateData.breathe || '';\n const temperature = templateData.temperature || '';\n const pulse = templateData.pulse || '';\n \n // 格式化每个字段\n \n let formattedTemperature = '';\n if (temperature && !isNaN(temperature)) {\n formattedTemperature = 'T' + temperature + '℃';\n }\n \n let formattedPulse = '';\n if (pulse && !isNaN(pulse)) {\n formattedPulse = 'P' + pulse + '次/分';\n }\n let formattedR = '';\n if (breathe && !isNaN(breathe)) {\n formattedR = 'R' + breathe + '次/分';\n }\n \n let formattedBP = '';\n if (bloodHigh && !isNaN(bloodHigh)) {\n formattedBP = 'BP' + bloodHigh +'/'+bloodLow+ 'mmHg';\n }\n \n \n // 组合所有信息\n const result = [\n formattedR,\n formattedBP,\n formattedTemperature,\n formattedPulse\n ].filter(item => item !== '').join(' ');\n \n return result;\n}\n\n"
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 582,
"height": 13.5,
"width": 79.5,
"title": "辅助检查",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 601.5,
"height": 30,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "auxiliaryExam",
"title": "长文",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 640.5,
"height": 13.5,
"width": 79.5,
"title": "诊断",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 661.5,
"height": 28.5,
"width": 550,
"title": "无",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "diagnosisText",
"lHeight": 30
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 694.7431640625,
"height": 13.5,
"width": 79.5,
"title": "处置",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 720,
"height": 28.5,
"width": 550,
"title": "无",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "treatment",
"lHeight": 30
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 9,
"top": 766.5,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28.5,
"top": 787.5,
"height": 13.5,
"width": 223.5,
"title": "医生签字:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 399,
"top": 787.5,
"height": 13.5,
"width": 186,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true,
"field": "reqTime"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 28.5,
"top": 810,
"height": 19.5,
"width": 556.5,
"title": "为了您和家人的健康,请勿吸烟!如需戒烟帮助,请到戒烟干预室咨询。",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true,
"fontSize": 12,
"letterSpacing": 1.5,
"textAlign": "center"
},
"printElementType": {
"title": "长文",
"type": "longText"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

View File

@@ -0,0 +1,599 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A5",
"height": 210,
"width": 148,
"paperList": {
"type": "A5",
"width": 148,
"height": 210
},
"paperHeader": 0,
"paperFooter": 592.4409448818898,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.01,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "10px",
"rotate": 0,
"width": 100,
"height": 100,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 50,
"height": 20,
"width": 420,
"title": "处方签",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 90,
"height": 13.5,
"width": 100,
"title": "医保编号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 283,
"top": 90,
"height": 13.5,
"width": 117,
"title": "就诊类型",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "orgId_dictText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 117,
"height": 13.5,
"width": 100,
"title": "处方编号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "prescriptionNo",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 283,
"top": 117,
"height": 13.5,
"width": 117,
"title": "病人性质",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 138,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 160.5,
"top": 153,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 153,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 154.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 27,
"top": 177,
"height": 13.5,
"width": 132,
"title": "地址:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 160.5,
"top": 178.5,
"height": 13.5,
"width": 99,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "departmentName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 178.5,
"height": 13.5,
"width": 99,
"title": "电话",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 205.5,
"height": 13.5,
"width": 189,
"title": "门诊号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "encounterNo"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 219,
"top": 205.5,
"height": 13.5,
"width": 198,
"title": "开具日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 232.5,
"height": 13.5,
"width": 381,
"title": "临床诊断",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "conditionName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 256.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28,
"top": 282,
"height": 9.75,
"width": 120,
"title": "Rp",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true,
"fontSize": 18,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 306,
"height": 36,
"width": 390,
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"textAlign": "center",
"tableBorder": "border",
"tableHeaderFontWeight": "500",
"field": "prescriptionList",
"columns": [
[
{
"title": "名称",
"width": 55.386224315781,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "规格",
"width": 59.41840807183531,
"field": "totalVolume",
"checked": true,
"columnId": "totalVolume",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "产地",
"width": 79.59183673469389,
"field": "manufacturerText",
"checked": true,
"columnId": "manufacturerText",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"width": 45.49640338326492,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"width": 41.658473534111906,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "金额",
"width": 42.9159186212175,
"field": "totalPrice",
"checked": true,
"columnId": "totalPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "等级",
"width": 65.53273533909551,
"field": "contractName",
"checked": true,
"columnId": "contractName",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 25.5,
"top": 379.5,
"height": 13.5,
"width": 241.5,
"title": "用法",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 18,
"top": 513,
"height": 13.5,
"width": 80,
"title": "医师",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 124.5,
"top": 513,
"height": 13.5,
"width": 80,
"title": "发药",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 222,
"top": 513,
"height": 13.5,
"width": 80,
"title": "划价",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 310.5,
"top": 513,
"height": 13.5,
"width": 80,
"title": "调配",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 532.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 289,
"top": 551.5,
"height": 13.5,
"width": 120,
"title": "总金额",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "medTotalAmount"
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

View File

@@ -0,0 +1,407 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A4",
"height": 297,
"width": 210,
"paperList": {
"type": "A4",
"width": 210,
"height": 297
},
"paperHeader": 0,
"paperFooter": 837,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": true,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 54.5,
"height": 20,
"width": 595.5,
"title": "手术记录",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 81,
"height": 13.5,
"width": 190.5,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 460.5,
"top": 81,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 82.5,
"height": 13.5,
"width": 193.5,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 108,
"height": 13.5,
"width": 192,
"title": "医保",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 108,
"height": 13.5,
"width": 190.5,
"title": "电话",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "phone"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 460.5,
"top": 108,
"height": 13.5,
"width": 99,
"title": "医生",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 460.5,
"top": 132.99999618530273,
"height": 13.5,
"width": 124.5,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "department"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 133.5,
"height": 13.5,
"width": 196.5,
"title": "病历号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "busNo"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 133.5,
"height": 13.5,
"width": 200,
"title": "手术日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "operationDate"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 157.5,
"height": 61.5,
"width": 550.5,
"title": "诊断",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "busNosds"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 226.5,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 25.5,
"top": 250.5,
"height": 13.5,
"width": 81,
"title": "详细记录",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 271.5,
"height": 493.5,
"width": 550,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "surgicalDetails",
"lHeight": 60,
"formatter": "function(title, value, options, templateData, target, paperNo) {\n // 返回原始值,让组件自动处理高度\n return value;\n}",
"styler": "function(value, options, target, templateData, paperNo) {\n return { \n 'word-wrap': 'break-word',\n 'word-break': 'break-all',\n 'white-space': 'normal',\n 'line-height': '1.4',\n 'overflow': 'visible'\n };\n}"
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 9,
"top": 774,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28.5,
"top": 787.5,
"height": 13.5,
"width": 223.5,
"title": "病人或家人签字:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 399,
"top": 787.5,
"height": 13.5,
"width": 186,
"title": "医生签字:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 399,
"top": 811.5,
"height": 13.5,
"width": 186,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 28.5,
"top": 813,
"height": 13.5,
"width": 222,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,290 @@
{
"panels": [
{
"index": 1,
"name": 2,
"paperType": "自定义",
"height": 60,
"width": 80,
"paperList": {
"type": "自定义",
"width": 80,
"height": 60
},
"paperHeader": 0,
"paperFooter": 166.5,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": true,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 6,
"top": 7.5,
"height": 10,
"width": 69,
"title": "病区",
"field": "patientName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 81,
"top": 7.5,
"height": 10,
"width": 52.5,
"title": "姓名",
"field": "patientName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 147,
"top": 7.5,
"height": 10,
"width": 72,
"title": "床位号",
"field": "bedNo",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 22,
"height": 12,
"width": 81,
"title": "频次 qd",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 8.25,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 147,
"top": 24,
"height": 10,
"width": 70.5,
"title": "日期",
"field": "date",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 4.5,
"top": 45,
"height": 30,
"width": 216,
"title": "undefined+beforeDragIn",
"field": "infuseData",
"coordinateSync": false,
"widthHeightSync": false,
"tableHeaderBackground": "#ffffff",
"tableBodyCellBorder": "noBorder",
"rowsColumnsMerge": "function(data, col, colIndex, rowIndex, tableData, printData){ \n // 合并前三列 (columnIndex 0-2)\n if (colIndex >= 0 && colIndex <= 2) {\n // 第一列显示合并后的单元格\n if (colIndex === 0) {\n return [1, 3]; // rowspan=1, colspan=3\n } \n // 其他两列不显示(被合并)\n else {\n return [0, 0]; // rowspan=0, colspan=0\n }\n }\n // 其他列正常显示\n return [1, 1]; // rowspan=1, colspan=1\n}",
"tableBodyRowBorder": "topBottomBorder",
"fontSize": 7.5,
"tableHeaderRepeat": "first",
"maxRows": 3,
"tableHeaderRowHeight": 6,
"tableHeaderFontSize": 6.75,
"columns": [
[
{
"title": "用法",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 51.988373079128756,
"field": "data",
"checked": true,
"columnId": "data",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "剂量",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 55.016471480350916,
"checked": true,
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "滴速",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 55.31961796101854,
"field": "",
"checked": true,
"columnId": "",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' ' + row.unitCode_dictText; }",
"width": 53.6755374795018,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 10,
"top": 144,
"height": 9,
"width": 210,
"borderWidth": "0.75",
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 10,
"top": 153,
"height": 9.75,
"width": 82.5,
"title": "执行人",
"field": "prepareName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 111,
"top": 153,
"height": 9.75,
"width": 82.5,
"title": "时间",
"field": "date",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 151.5,
"paperNumberTop": 91
}
]
}

View File

@@ -0,0 +1,245 @@
{
"panels": [
{
"index": 1,
"name": 2,
"paperType": "自定义",
"height": 34,
"width": 58,
"paperList": {
"type": "自定义",
"width": 60,
"height": 40
},
"paperHeader": 0,
"paperFooter": 91.5,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.01,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "10px",
"rotate": 25,
"width": 100,
"height": 100,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 6,
"top": 7.5,
"height": 10,
"width": 51,
"title": "文本",
"field": "patientName",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"hideTitle": true,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 70,
"top": 7.5,
"height": 10,
"width": 33,
"title": "文本",
"field": "genderEnum_enumText",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"hideTitle": true,
"fontSize": 7.5,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 119,
"top": 7.5,
"height": 10,
"width": 45,
"title": "文本",
"field": "age",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"hideTitle": true,
"fontSize": 7.5,
"fontWeight": "bold",
"textAlign": "right"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 19.5,
"height": 12,
"width": 81,
"title": "频次 qd",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"fontSize": 8.25,
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 93,
"top": 19.5,
"height": 10,
"width": 70.5,
"title": "文本",
"field": "date",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"hideTitle": true,
"fontSize": 7.5,
"fontWeight": "bold",
"textAlign": "right"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 4.5,
"top": 36,
"height": 30,
"width": 156,
"title": "undefined+beforeDragIn",
"field": "infuseData",
"coordinateSync": false,
"widthHeightSync": false,
"tableHeaderBackground": "#ffffff",
"tableBodyCellBorder": "noBorder",
"rowsColumnsMerge": "function(data, col, colIndex, rowIndex, tableData, printData){ \n // 合并前三列 (columnIndex 0-2)\n if (colIndex >= 0 && colIndex <= 2) {\n // 第一列显示合并后的单元格\n if (colIndex === 0) {\n return [1, 3]; // rowspan=1, colspan=3\n } \n // 其他两列不显示(被合并)\n else {\n return [0, 0]; // rowspan=0, colspan=0\n }\n }\n // 其他列正常显示\n return [1, 1]; // rowspan=1, colspan=1\n}",
"tableBodyRowBorder": "topBottomBorder",
"fontSize": 7.5,
"tableHeaderRepeat": "first",
"maxRows": 3,
"tableHeaderRowHeight": 6,
"tableHeaderFontSize": 6.75,
"columns": [
[
{
"title": "用法",
"titleSync": false,
"halign": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 37.54715833492632,
"field": "data",
"checked": true,
"columnId": "data",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "剂量",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 39.4492016246979,
"checked": true,
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "速度",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 40.23797408295783,
"checked": true,
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"titleSync": false,
"align": "center",
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"formatter2": "function(value,row,index,options,rowIndex,column){ return value + ' ' + row.unitCode_dictText; }",
"width": 38.765665957417966,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
}
],
"paperNumberLeft": 151.5,
"paperNumberTop": 91
}
]
}

View File

@@ -0,0 +1,734 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A4",
"height": 297,
"width": 210,
"paperList": {
"type": "A4",
"width": 210,
"height": 297
},
"paperHeader": 217.5,
"paperFooter": 771,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": true,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 54.5,
"height": 20,
"width": 595.5,
"title": "门诊病历",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 81,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 460.5,
"top": 81,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 82.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 108,
"height": 13.5,
"width": 99,
"title": "医保",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 108,
"height": 13.5,
"width": 190.5,
"title": "电话",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "phone"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 460.5,
"top": 108,
"height": 13.5,
"width": 99,
"title": "医生",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 133.5,
"height": 13.5,
"width": 196.5,
"title": "病历号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "busNo"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 247.5,
"top": 133.5,
"height": 13.5,
"width": 200,
"title": "发病日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "onsetDate"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 159,
"height": 13.5,
"width": 50,
"title": "诊断",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "vxc"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 174,
"height": 30,
"width": 550,
"hideTitle": true,
"field": "diagnosisText",
"title": "无",
"lHeight": 20,
"formatter": "function(title, value, options, templateData, target, paperNo) {\n return value || '无';\n}",
"styler": "function(value, options, target, templateData, paperNo) {\n return { \n 'word-wrap': 'break-word',\n 'word-break': 'break-all',\n 'white-space': 'normal',\n 'line-height': '1.4'\n };\n}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 10,
"top": 210,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 25.5,
"top": 217.5,
"height": 13.5,
"width": 50,
"title": "主诉",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 235.5,
"height": 33,
"width": 550,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "complaint",
"lHeight": 50
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 277.5,
"height": 13.5,
"width": 50,
"title": "现病史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 299.5,
"height": 31.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "presentIllness",
"title": "无",
"lHeight": 30
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 341,
"height": 13.5,
"width": 50,
"title": "既往史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 360,
"height": 25.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "pastIllness",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 396,
"height": 13.5,
"width": 50,
"title": "过敏史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 414,
"height": 33,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "allergyHistory",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 462,
"height": 13.5,
"width": 50,
"title": "家族史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 481.5,
"height": 22.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "familyHistory",
"title": "无",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 514.5,
"height": 13.5,
"width": 50,
"title": "个人史",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 535.5,
"height": 28.5,
"width": 550,
"title": " ",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "personalHistory",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 571.7500162124634,
"height": 13.5,
"width": 50,
"title": "体检",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 594.2500162124634,
"height": 30,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"title": "长文",
"lHeight": 20,
"formatter": "function(title, value, options, templateData, target, paperNo) {\n // 获取所有体检数据\n const height = templateData.height || '';\n const weight = templateData.weight || '';\n const temperature = templateData.temperature || '';\n const pulse = templateData.pulse || '';\n \n // 格式化每个字段\n let formattedHeight = '';\n if (height && !isNaN(height)) {\n formattedHeight = '身高:' + height + 'cm';\n }\n \n let formattedWeight = '';\n if (weight && !isNaN(weight)) {\n formattedWeight = '体重:' + weight + 'kg';\n }\n \n let formattedTemperature = '';\n if (temperature && !isNaN(temperature)) {\n formattedTemperature = '体温:' + temperature + '℃';\n }\n \n let formattedPulse = '';\n if (pulse && !isNaN(pulse)) {\n formattedPulse = '脉搏:' + pulse + '次/分';\n }\n \n // 组合所有信息\n const result = [\n formattedHeight,\n formattedWeight,\n formattedTemperature,\n formattedPulse\n ].filter(item => item !== '').join(' ');\n \n return result;\n}\n\n"
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 631.5,
"height": 13.5,
"width": 79.5,
"title": "辅助检查",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 651,
"height": 22.5,
"width": 550,
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "auxiliaryExam",
"title": "长文",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 678.2431640625,
"height": 13.5,
"width": 50,
"title": "处置",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 700.5,
"height": 21,
"width": 550,
"title": "无",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "treatment",
"lHeight": 30
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 25.5,
"top": 730,
"height": 13.5,
"width": 50,
"title": "治疗",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "physicalExamdsd",
"fontWeight": "bold"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 747,
"height": 25.5,
"width": 550,
"title": "无",
"coordinateSync": false,
"widthHeightSync": false,
"hideTitle": true,
"field": "physicalExam",
"lHeight": 20
},
"printElementType": {
"title": "长文",
"type": "longText"
}
},
{
"options": {
"left": 9,
"top": 774,
"height": 9,
"width": 576,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28.5,
"top": 787.5,
"height": 13.5,
"width": 223.5,
"title": "病人或家人签字:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 399,
"top": 787.5,
"height": 13.5,
"width": 186,
"title": "医生签字:",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 399,
"top": 811.5,
"height": 13.5,
"width": 186,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 28.5,
"top": 813,
"height": 13.5,
"width": 222,
"title": "日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime"
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,632 @@
{
"panels": [
{
"index": 1,
"name": 3,
"paperType": "A5",
"height": 210,
"width": 148,
"paperList": {
"type": "A5",
"width": 148,
"height": 210
},
"paperHeader": 0,
"paperFooter": 592.4409448818898,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"panelAngle": 0,
"overPrintOptions": {
"content": "",
"opacity": 0.01,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(87, 13, 248, 0.5)",
"fontSize": "10px",
"rotate": 0,
"width": 100,
"height": 100,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 0,
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,
"qrCodeLevel": 0,
"textAlign": "center",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 0,
"top": 50,
"height": 20,
"width": 420,
"title": "处方签",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,
"textAlign": "center",
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 90,
"height": 13.5,
"width": 100,
"title": "医保编号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 283,
"top": 90,
"height": 13.5,
"width": 117,
"title": "就诊类型",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "orgId_dictText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 117,
"height": 13.5,
"width": 100,
"title": "处方编号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "prescriptionNo",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 283,
"top": 117,
"height": 13.5,
"width": 117,
"title": "病人性质",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "contractName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 138,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 142.5,
"top": 153,
"height": 13.5,
"width": 100,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "genderEnum_enumText",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 153,
"height": 13.5,
"width": 99,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "age",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 26,
"top": 154.5,
"height": 13.5,
"width": 100,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "patientName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 142.5,
"top": 178.5,
"height": 13.5,
"width": 99,
"title": "科室",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "departmentName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 288,
"top": 178.5,
"height": 13.5,
"width": 99,
"title": "电话",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 180,
"height": 13.5,
"width": 100,
"title": "门诊号",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "encounterNo",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 205.5,
"height": 13.5,
"width": 141,
"title": "地址",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 195,
"top": 205.5,
"height": 13.5,
"width": 210,
"title": "开具日期",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "reqTime",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 25.5,
"top": 232.5,
"height": 13.5,
"width": 367.5,
"title": "临床诊断",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "conditionName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 256.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 28,
"top": 282,
"height": 9.75,
"width": 120,
"title": "Rp",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true,
"fontSize": 18,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 306,
"height": 36,
"width": 390,
"title": "undefined+beforeDragIn",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"textAlign": "center",
"tableBorder": "border",
"tableHeaderFontWeight": "500",
"field": "prescriptionList",
"columns": [
[
{
"title": "名称",
"width": 55.386224315781,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "规格",
"width": 59.41840807183531,
"field": "totalVolume",
"checked": true,
"columnId": "totalVolume",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "厂家",
"width": 79.59183673469389,
"checked": true,
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"width": 45.49640338326492,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"width": 41.658473534111906,
"field": "quantity",
"checked": true,
"columnId": "quantity",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "金额",
"width": 42.9159186212175,
"field": "totalPrice",
"checked": true,
"columnId": "totalPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "等级",
"width": 65.53273533909551,
"field": "contractName",
"checked": true,
"columnId": "contractName",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 25.5,
"top": 379.5,
"height": 13.5,
"width": 241.5,
"title": "用法",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 8.996109008789062,
"top": 510,
"height": 13.5,
"width": 100,
"title": "医师",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "doctorName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 113,
"top": 510,
"height": 13.5,
"width": 100,
"title": "发药",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 217,
"top": 510,
"height": 13.5,
"width": 100,
"title": "划价",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 318,
"top": 510,
"height": 13.5,
"width": 100,
"title": "调配",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "nickName",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 10,
"top": 532.5,
"height": 9,
"width": 400,
"borderWidth": "1.5",
"coordinateSync": false,
"widthHeightSync": false,
"fixed": true
},
"printElementType": {
"title": "横线",
"type": "hline"
}
},
{
"options": {
"left": 26,
"top": 551.5,
"height": 13.5,
"width": 120,
"title": "制表人",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "1",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 289,
"top": 551.5,
"height": 13.5,
"width": 120,
"title": "金额",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
"qrCodeLevel": 0,
"field": "medTotalAmount",
"fixed": true
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
],
"paperNumberLeft": 236,
"paperNumberTop": 573
}
]
}

View File

@@ -0,0 +1,14 @@
处方签 Prescription.json
处置单 Disposal.json
门诊日结 DailyOutpatientSettlement.json
门诊挂号 OutpatientRegistration.json
门诊收费 OutpatientBilling.json
门诊病历 OutpatientMedicalRecord.json
手术记录 OperativeRecord.json
门诊输液贴 OutpatientInfusion.json
药房打印 Pharmacy.json
红旗门诊病历 HQOutpatientMedicalRecord.json
预交金 AdvancePayment.json
中药药房处方单 ChineseMedicinePrescription.json
中药医生处方单 DocChineseMedicinePrescription.json

View File

@@ -0,0 +1,60 @@
<template>
<div class="table-section" v-loading="loading">
<EditableTable ref="editableTableRef" v-bind="$attrs" class="editable-table">
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</EditableTable>
</div>
</template>
<script setup>
import { ref } from 'vue';
import EditableTable from './EditableTable.vue';
defineOptions({
name: 'EditTable',
inheritAttrs: false,
});
const props = defineProps({
loading: {
type: Boolean,
default: false,
},
});
const editableTableRef = ref(null);
defineExpose({
get formRef() {
return editableTableRef.value?.formRef;
},
get tableRef() {
return editableTableRef.value?.tableRef;
},
validate: (...args) => editableTableRef.value?.validate(...args),
validateField: (...args) => editableTableRef.value?.validateField(...args),
resetFields: (...args) => editableTableRef.value?.resetFields(...args),
clearValidate: (...args) => editableTableRef.value?.clearValidate(...args),
get tableData() {
return editableTableRef.value?.tableData;
},
});
</script>
<style scoped lang="scss">
.table-section {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
.editable-table {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
</style>

View File

@@ -0,0 +1,567 @@
<template>
<el-form ref="formRef" :model="{ tableData }" :rules="rules" class="editable-table-form">
<div
v-if="showAddButton || showDeleteButton || searchFields.length > 0"
class="editable-table-toolbar"
>
<div class="toolbar-left">
<el-button v-if="showAddButton" type="primary" icon="Plus" @click="handleToolbarAdd">
添加行
</el-button>
<el-button
v-if="showDeleteButton"
type="danger"
icon="Delete"
:disabled="selectedRows.length === 0"
@click="handleToolbarDelete"
>
删除行
</el-button>
</div>
<div class="toolbar-right">
<el-input
v-if="searchFields.length > 0"
v-model="searchKeyword"
:placeholder="searchPlaceholder"
clearable
style="width: 300px"
@input="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
<el-table
ref="tableRef"
:data="filteredTableData"
:border="border"
:stripe="stripe"
:max-height="maxHeight || undefined"
:min-height="minHeight || undefined"
:height="!maxHeight && !minHeight ? '100%' : undefined"
:row-key="getRowKey"
:virtualized="useVirtualized"
v-bind="$attrs"
@selection-change="handleSelectionChange"
class="editable-table-inner"
>
<el-table-column v-if="showSelection" type="selection" width="55" align="center" />
<el-table-column
v-if="showRowActions"
:width="rowActionsColumnWidth"
align="center"
fixed="left"
>
<template #header>
<div
v-if="showSelection && selectedRows.length > 0 && !showDeleteButton"
style="display: flex; align-items: center; justify-content: center; gap: 4px"
>
<el-button type="danger" size="small" icon="Delete" link @click="handleDeleteSelected">
删除选中({{ selectedRows.length }})
</el-button>
</div>
<span v-else></span>
</template>
<template #default="scope">
<el-button
v-if="showRowAddButton"
type="primary"
link
icon="CirclePlus"
class="action-btn"
@click="handleAdd(scope.$index)"
title="增加"
/>
<el-button
v-if="showRowDeleteButton"
type="danger"
link
icon="Delete"
class="action-btn"
@click="handleDelete(scope.$index)"
title="删除"
/>
</template>
</el-table-column>
<el-table-column
v-for="col in filteredColumns"
:key="col.prop"
:prop="col.prop"
:label="col.label"
:width="col.width"
:min-width="col.minWidth"
:fixed="col.fixed"
:align="col.align || 'center'"
:formatter="col.formatter"
>
<template #default="scope">
<template v-if="col.type === 'input'">
<el-form-item
:prop="`tableData.${scope.$index}.${col.prop}`"
:rules="col.rules"
style="margin-bottom: 0"
>
<el-input
v-model="scope.row[col.prop]"
:placeholder="col.placeholder || `请输入${col.label}`"
:disabled="col.disabled"
:clearable="col.clearable !== false"
@blur="col.onBlur && col.onBlur(scope.row, scope.$index)"
@input="col.onInput && col.onInput(scope.row, scope.$index)"
@change="col.onChange && col.onChange(scope.row, scope.$index)"
>
<template v-if="col.suffix" #suffix>{{ col.suffix }}</template>
</el-input>
</el-form-item>
</template>
<template v-else-if="col.type === 'number'">
<el-form-item
:prop="`tableData.${scope.$index}.${col.prop}`"
:rules="col.rules"
style="margin-bottom: 0"
>
<el-input-number
v-model="scope.row[col.prop]"
:placeholder="col.placeholder || `请输入${col.label}`"
:disabled="col.disabled"
:min="col.min"
:max="col.max"
:precision="col.precision"
:controls="false"
style="width: 100%"
@change="col.onChange && col.onChange(scope.row, scope.$index)"
/>
</el-form-item>
</template>
<template v-else-if="col.type === 'select'">
<el-form-item
:prop="`tableData.${scope.$index}.${col.prop}`"
:rules="col.rules"
style="margin-bottom: 0"
>
<el-select
v-model="scope.row[col.prop]"
:placeholder="col.placeholder || `请选择${col.label}`"
:disabled="col.disabled"
:clearable="col.clearable !== false"
:filterable="col.filterable"
:multiple="col.multiple"
style="width: 100%"
:class="scope.row.error ? 'error-border' : ''"
@change="
async (value) => {
const checkBeforeChange = col.extraprops?.checkBeforeChange;
if (checkBeforeChange && typeof checkBeforeChange === 'function') {
const result = await checkBeforeChange(scope.row, scope.$index, value);
if (result === false) {
return;
}
}
if (col.onChange) {
col.onChange(scope.row, scope.$index, value);
}
}
"
>
<el-option
v-for="option in typeof col.options === 'function'
? col.options(scope.row, scope.$index)
: col.options || []"
:key="option.value"
:label="option.label"
:value="option.value"
@click="option.onClick && option.onClick(scope.row, option)"
/>
</el-select>
</el-form-item>
</template>
<template v-else-if="col.type === 'date'">
<el-form-item
:prop="`tableData.${scope.$index}.${col.prop}`"
:rules="col.rules"
style="margin-bottom: 0"
>
<el-date-picker
v-model="scope.row[col.prop]"
:type="col.dateType || 'date'"
:placeholder="col.placeholder || `请选择${col.label}`"
:disabled="col.disabled"
:clearable="col.clearable !== false"
:value-format="col.valueFormat || 'YYYY-MM-DD'"
style="width: 100%"
@change="col.onChange && col.onChange(scope.row, scope.$index)"
/>
</el-form-item>
</template>
<template v-else-if="col.type === 'slot'">
<el-form-item
:prop="`tableData.${scope.$index}.${col.prop}`"
:rules="col.rules"
style="margin-bottom: 0"
>
<slot :name="col.slot || col.prop" :row="scope.row" :index="scope.$index" />
</el-form-item>
</template>
<template v-else>
<span>{{
col.formatter
? col.formatter(scope.row, scope.column, scope.row[col.prop])
: scope.row[col.prop]
}}</span>
</template>
</template>
</el-table-column>
</el-table>
<div v-if="$slots.footer" class="editable-table-footer">
<slot name="footer" :tableData="tableData" />
</div>
</el-form>
</template>
<script setup lang="ts">
import { ref, watch, nextTick, computed } from 'vue';
import { Search } from '@element-plus/icons-vue';
import type { EditableTableProps } from '../types/EditableTable.d';
defineOptions({
name: 'EditableTable',
});
const props = withDefaults(defineProps<EditableTableProps>(), {
modelValue: () => [],
rules: () => ({}),
defaultRow: () => ({}),
border: true,
stripe: false,
showSelection: false,
showAddButton: false,
showDeleteButton: false,
showRowActions: true,
showRowAddButton: true,
showRowDeleteButton: true,
searchFields: () => [],
virtualizedThreshold: 100,
});
const emit = defineEmits<{
'update:modelValue': [value: Record<string, any>[]];
add: [row: Record<string, any>, index: number];
delete: [row: Record<string, any>, index: number, isClear: boolean];
'selection-change': [selection: Record<string, any>[]];
'toolbar-add': [];
'toolbar-delete': [rows: Record<string, any>[]];
}>();
const formRef = ref<InstanceType<typeof import('element-plus').ElForm> | null>(null);
const tableRef = ref<InstanceType<typeof import('element-plus').ElTable> | null>(null);
const selectedRows = ref<Record<string, any>[]>([]);
const searchKeyword = ref('');
const tableData = ref([...props.modelValue]);
// 行唯一 key用于虚拟滚动等
const autoRowId = ref(0);
const getRowKey = (row: Record<string, any>) => {
if (row.rowKey !== undefined && row.rowKey !== null) return row.rowKey;
if (row.id !== undefined && row.id !== null) return row.id;
if (!row._etKey) {
row._etKey = `et-${autoRowId.value++}`;
}
return row._etKey;
};
// 是否开启虚拟滚动:优先使用外部传入,其次根据数据量自动开启
const useVirtualized = computed(() => {
if (typeof props.virtualized === 'boolean') {
return props.virtualized;
}
const threshold = props.virtualizedThreshold ?? 100;
return tableData.value.length > threshold;
});
// 过滤列(支持条件显示)
const filteredColumns = computed(() => {
return props.columns.filter((col) => !col.vIf || col.vIf());
});
// 行操作列宽度:同时显示“增加+删除”则宽一点;只显示一个则缩窄
const rowActionsColumnWidth = computed(() => {
const showAdd = !!props.showRowAddButton;
const showDel = !!props.showRowDeleteButton;
if (showAdd && showDel) return 100;
if (showAdd || showDel) return 60;
// 如果两者都不显示,列也不会渲染;这里给个兜底
return 0;
});
const searchPlaceholder = computed(() => {
if (props.searchFields.length === 0) {
return '请输入搜索关键词';
}
const fieldLabels = props.searchFields
.map((field) => {
const column = props.columns.find((col) => col.prop === field);
return column?.label || field;
})
.filter(Boolean);
if (fieldLabels.length === 0) {
return '请输入搜索关键词';
}
if (fieldLabels.length === 1) {
return `请输入${fieldLabels[0]}`;
}
return `请输入${fieldLabels.join('')}`;
});
// 根据搜索关键词过滤表格数据
const filteredTableData = computed(() => {
if (!searchKeyword.value || props.searchFields.length === 0) {
return tableData.value;
}
const keyword = searchKeyword.value.toLowerCase();
return tableData.value.filter((row) => {
return props.searchFields.some((field) => {
const value = row[field];
if (value === null || value === undefined) {
return false;
}
return String(value).toLowerCase().includes(keyword);
});
});
});
watch(
() => props.modelValue,
(newVal) => {
if (newVal !== tableData.value) {
tableData.value = [...newVal];
}
},
{ deep: true }
);
watch(
tableData,
(newVal) => {
emit('update:modelValue', newVal);
},
{ deep: true }
);
const handleAdd = (index) => {
const newRow = { ...props.defaultRow };
tableData.value.splice(index + 1, 0, newRow);
nextTick(() => {
emit('add', newRow, index + 1);
});
};
const handleDelete = (index) => {
if (tableData.value.length === 1) {
Object.keys(tableData.value[0]).forEach((key) => {
tableData.value[0][key] = '';
});
Object.assign(tableData.value[0], { ...props.defaultRow });
emit('delete', tableData.value[0], index, true);
} else {
const deletedRow = tableData.value.splice(index, 1)[0];
emit('delete', deletedRow, index, false);
}
};
const handleSelectionChange = (selection) => {
selectedRows.value = selection;
emit('selection-change', selection);
};
// 删除所有选中的行
const handleDeleteSelected = () => {
if (selectedRows.value.length === 0) {
return;
}
// 获取选中行的索引
const selectedIndexes = selectedRows.value.map((row) => tableData.value.indexOf(row));
// 从后往前删除,避免索引变化问题
selectedIndexes.sort((a, b) => b - a);
// 如果选中了所有行且只剩一行,清空数据而不是删除
if (tableData.value.length === selectedRows.value.length && tableData.value.length === 1) {
Object.keys(tableData.value[0]).forEach((key) => {
tableData.value[0][key] = '';
});
Object.assign(tableData.value[0], { ...props.defaultRow });
emit('delete', tableData.value[0], 0, true);
} else {
// 删除选中的行
selectedIndexes.forEach((index) => {
if (index !== -1 && tableData.value.length > 1) {
const deletedRow = tableData.value.splice(index, 1)[0];
emit('delete', deletedRow, index, false);
}
});
}
// 清空选中状态
if (tableRef.value) {
tableRef.value.clearSelection();
}
selectedRows.value = [];
};
// 工具栏新增按钮
const handleToolbarAdd = () => {
const newRow = { ...props.defaultRow };
tableData.value.push(newRow);
nextTick(() => {
emit('toolbar-add');
emit('add', newRow, tableData.value.length - 1);
});
};
// 工具栏删除按钮
const handleToolbarDelete = () => {
if (selectedRows.value.length === 0) {
return;
}
emit('toolbar-delete', selectedRows.value);
handleDeleteSelected();
};
// 搜索处理
const handleSearch = () => {
// 搜索逻辑已在 computed 中处理
};
const validate = (callback) => {
if (formRef.value) {
return formRef.value.validate(callback);
}
};
const validateField = (props, callback) => {
if (formRef.value) {
return formRef.value.validateField(props, callback);
}
};
const resetFields = () => {
if (formRef.value) {
formRef.value.resetFields();
}
};
const clearValidate = (props) => {
if (formRef.value) {
formRef.value.clearValidate(props);
}
};
defineExpose({
formRef,
tableRef,
validate,
validateField,
resetFields,
clearValidate,
tableData,
});
</script>
<style scoped lang="scss">
.editable-table-form {
display: flex;
flex-direction: column;
height: 100%;
.editable-table-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 0 4px;
.toolbar-left {
display: flex;
gap: 8px;
}
.toolbar-right {
display: flex;
align-items: center;
}
}
:deep(.el-table.editable-table-inner) {
flex: 1;
display: flex;
flex-direction: column;
.el-table__body-wrapper {
flex: 1;
overflow: auto;
}
.el-table__cell {
position: relative;
overflow: visible;
vertical-align: top;
.cell {
position: relative;
overflow: visible;
}
}
}
:deep(.el-table__cell) {
overflow: visible;
vertical-align: top;
.cell {
overflow: visible;
}
}
// 错误信息往下撑开行高不影响上面布局
:deep(.el-form-item) {
margin-bottom: 0;
.el-form-item__error {
position: static;
line-height: 1.5;
padding-top: 4px;
font-size: 12px;
color: var(--el-color-danger);
display: block;
white-space: nowrap;
}
}
.action-btn {
margin: 4px;
:deep(.el-icon) {
font-size: 18px;
}
}
}
.editable-table-footer {
flex-shrink: 0;
margin-top: 16px;
}
</style>

View File

@@ -0,0 +1,157 @@
<template>
<div v-if="show" class="query-form-wrapper">
<el-form
ref="queryFormRef"
:model="queryParams"
:inline="true"
class="query-form"
:label-width="labelWidth"
>
<template v-for="item in displayedFormItems" :key="item.prop">
<FormItem
:item="item"
:model-value="queryParams[item.prop]"
:on-enter="handleQuery"
@update:model-value="(value) => (queryParams[item.prop] = value)"
@change="(value) => item.onChange && item.onChange(value)"
>
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</FormItem>
</template>
<el-form-item v-if="showDefaultButtons" style="margin-left: 20px">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button v-if="needCollapse" type="text" @click="toggleExpand" style="margin-left: 16px">
{{ isExpanded ? '收起' : '展开' }}
<el-icon class="el-icon--right">
<DArrowLeft v-if="isExpanded" class="collapse-arrow collapse-arrow--up" />
<DArrowRight v-else class="collapse-arrow collapse-arrow--down" />
</el-icon>
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import FormItem from './FormItem.vue';
import type { FilterProps } from '../types/Filter.d';
defineOptions({
name: 'Filter'
});
const props = withDefaults(defineProps<FilterProps>(), {
formItems: () => [],
show: true,
showDefaultButtons: true,
labelWidth: '120px',
showLabelColon: true,
});
const emit = defineEmits<{
query: [queryParams: Record<string, any>];
reset: [];
}>();
const queryFormRef = ref<InstanceType<typeof import('element-plus').ElForm> | null>(null);
const isExpanded = ref(true);
const itemsPerRow = 4;
const normalizedFormItems = computed(() =>
(props.formItems || []).map((item) => ({
...item,
labelSuffix: item.labelSuffix ?? (props.showLabelColon ? '' : ''),
}))
);
const needCollapse = computed(() => {
if (!normalizedFormItems.value || normalizedFormItems.value.length === 0) return false;
let totalWidth = 0;
normalizedFormItems.value.forEach((item) => {
if (item.type === 'custom' || item.type === 'daterange') {
totalWidth += 2;
} else {
totalWidth += 1;
}
});
return totalWidth > itemsPerRow * 2;
});
const displayedFormItems = computed(() => {
if (!needCollapse.value || isExpanded.value) {
return normalizedFormItems.value;
}
const maxItems = itemsPerRow * 2;
let count = 0;
const result: any[] = [];
for (const item of normalizedFormItems.value) {
const itemWidth = item.type === 'custom' || item.type === 'daterange' ? 2 : 1;
if (count + itemWidth > maxItems) {
break;
}
result.push(item);
count += itemWidth;
}
return result;
});
const toggleExpand = () => {
isExpanded.value = !isExpanded.value;
};
const handleQuery = () => {
emit('query', props.queryParams);
};
const resetQuery = () => {
if (queryFormRef.value) {
queryFormRef.value.resetFields();
}
if (props.queryParams && Object.prototype.hasOwnProperty.call(props.queryParams, 'pageNum')) {
props.queryParams.pageNum = 1;
}
emit('reset');
};
defineExpose({
queryFormRef,
handleQuery,
resetQuery,
});
</script>
<style scoped lang="scss">
.query-form-wrapper {
flex-shrink: 0;
width: 100%;
.query-form {
width: 100%;
}
}
.collapse-arrow {
transition: transform 0.2s ease;
&.collapse-arrow--up {
transform: rotate(90deg);
}
&.collapse-arrow--down {
transform: rotate(90deg);
}
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<el-form
ref="formRef"
:model="model"
:rules="rules"
:label-width="labelWidth"
:inline="inline"
:label-position="labelPosition"
class="table-layout-form"
>
<template v-for="item in normalizedFormItems" :key="item.prop">
<FormItem
:item="item"
:model-value="model[item.prop]"
@update:model-value="(value) => (model[item.prop] = value)"
@change="(value) => item.onChange && item.onChange(value)"
>
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</FormItem>
</template>
</el-form>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import FormItem from './FormItem.vue';
import type { FormProps } from '../types/Form.d';
defineOptions({
name: 'Form'
});
const props = withDefaults(defineProps<FormProps>(), {
formItems: () => [],
rules: () => ({}),
labelWidth: '120px',
inline: false,
labelPosition: 'right',
showLabelColon: true,
});
const emit = defineEmits<{
validate: [callback?: (valid: boolean) => void];
}>();
const formRef = ref<InstanceType<typeof import('element-plus').ElForm> | null>(null);
const normalizedFormItems = computed(() =>
(props.formItems || []).map((item) => ({
...item,
labelSuffix: item.labelSuffix ?? (props.showLabelColon ? '' : ''),
}))
);
// 表单验证
const validate = (callback) => {
if (formRef.value) {
return formRef.value.validate(callback);
}
};
// 验证指定字段
const validateField = (props, callback) => {
if (formRef.value) {
return formRef.value.validateField(props, callback);
}
};
// 重置表单
const resetFields = () => {
if (formRef.value) {
formRef.value.resetFields();
}
};
// 清除验证
const clearValidate = (props) => {
if (formRef.value) {
formRef.value.clearValidate(props);
}
};
// 滚动到指定字段
const scrollToField = (prop) => {
if (formRef.value) {
formRef.value.scrollToField(prop);
}
};
defineExpose({
formRef,
validate,
validateField,
resetFields,
clearValidate,
scrollToField,
});
</script>
<style scoped lang="scss">
.table-layout-form {
width: 100%;
// 非内联表单样式
&:not(.el-form--inline) {
:deep(.el-form-item) {
display: flex;
margin-right: 0;
}
}
}
</style>

View File

@@ -0,0 +1,196 @@
<template>
<el-form-item
:label="labelWithSuffix"
:prop="item.prop"
:required="item.required"
:class="{ 'form-item-double': item.type === 'custom' || item.type === 'daterange' }"
>
<el-input
v-if="item.type === 'input'"
:model-value="modelValue"
:placeholder="item.placeholder || `请输入${item.label}`"
:clearable="item.clearable !== false"
:style="item.style || { width: item.width || '200px' }"
v-bind="item.extraprops || {}"
@keyup.enter="handleEnter"
@update:model-value="handleUpdate"
/>
<el-select
v-else-if="item.type === 'select'"
:model-value="modelValue"
:placeholder="item.placeholder || `请选择${item.label}`"
:clearable="item.clearable !== false"
:style="item.style || { width: item.width || '200px' }"
:disabled="item.disabled"
v-bind="item.extraprops || {}"
:multiple="item.multiple !== false"
:filterable="item.filterable !== false"
:collapse-tags="item.collapseTags !== false"
@change="handleChange"
@update:model-value="(value) => handleUpdateWithCheck(value, item.checkBeforeChange)"
>
<el-option
v-for="option in item.options || []"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
<el-radio-group
v-else-if="item.type === 'radio'"
:model-value="modelValue"
v-bind="item.extraprops || {}"
@change="handleChange"
@update:model-value="handleUpdate"
>
<el-radio v-for="option in item.options || []" :key="option.value" :label="option.value">
{{ option.label }}
</el-radio>
</el-radio-group>
<!-- 单独日期 -->
<el-date-picker
v-else-if="item.type === 'date'"
:model-value="modelValue"
type="date"
:placeholder="item.placeholder || `请选择${item.label}`"
:clearable="item.clearable !== false"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
:style="item.style || { width: item.width || '200px' }"
:disabled="item.disabled"
v-bind="item.extraprops || {}"
@change="handleChange"
@update:model-value="handleUpdate"
/>
<!-- 日期区间 -->
<QuickDateRange
v-else-if="item.type === 'daterange'"
:model-value="daterangeValue"
:start-placeholder="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
:clearable="item.clearable !== false"
:date-picker-style="daterangeStyle"
:attrs="item.extraprops || {}"
@change="handleChange"
@update:model-value="handleUpdate"
/>
<!-- 纯文本展示 -->
<span
v-else-if="item.type === 'text'"
:style="item.style || { width: item.width || '200px' }"
class="form-item-text"
>
{{ item.formatter ? item.formatter(modelValue) : modelValue ?? '' }}
</span>
<slot
v-else-if="item.type === 'custom'"
:name="item.slot || item.prop"
:item="item"
:modelValue="modelValue"
:updateModelValue="handleUpdate"
/>
</el-form-item>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { FormItemProps } from '../types/FormItem.d';
import QuickDateRange from './QuickDateRange.vue';
defineOptions({
name: 'FormItem',
});
const props = defineProps<FormItemProps>();
const emit = defineEmits<{
'update:modelValue': [value: any];
change: [value: any];
}>();
const labelWithSuffix = computed(() => {
const suffix = props.item.labelSuffix || '';
return `${props.item.label || ''}${suffix}`;
});
// 日期区间组件的值处理
const daterangeValue = computed<string[]>(() => {
if (props.item.type === 'daterange') {
if (Array.isArray(props.modelValue)) {
return props.modelValue.map((v: any) => String(v));
}
return [];
}
return [];
});
// 日期区间组件的样式处理
const daterangeStyle = computed(() => {
if (props.item.type === 'daterange') {
if (
typeof props.item.style === 'object' &&
props.item.style !== null &&
!Array.isArray(props.item.style)
) {
return props.item.style;
}
return { width: props.item.width || 'calc(316px + 7em)' };
}
return {};
});
const handleUpdate = (value: any) => {
emit('update:modelValue', value);
};
const handleUpdateWithCheck = async (value: any, shouldCheck = false) => {
if (shouldCheck) {
if (props.item.onChange && typeof props.item.onChange === 'function') {
const result = await props.item.onChange(value);
if (result === false) {
return;
}
}
}
handleUpdate(value);
};
const handleChange = (value: any) => {
emit('change', value);
};
const handleEnter = () => {
if (props.onEnter && typeof props.onEnter === 'function') {
props.onEnter();
}
};
</script>
<style scoped lang="scss">
:deep(.el-form-item) {
margin-bottom: 16px;
display: inline-flex;
align-items: flex-start;
vertical-align: top;
margin-right: 16px;
.el-form-item__label {
width: 7em !important;
min-width: 7em;
white-space: normal;
word-break: break-all;
line-height: 1.5;
padding-right: 8px;
text-align: right;
padding-top: 0;
display: flex;
align-items: center;
justify-content: flex-end;
}
.el-form-item__content {
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,143 @@
<template>
<el-form
ref="formRef"
:model="model"
:rules="rules"
:label-width="labelWidth"
:label-position="labelPosition"
class="form-layout-form"
>
<div class="form-items-container" :class="columns > 0 ? `form-layout-${columns}col` : ''">
<template v-for="(item, index) in normalizedFormItems" :key="item.prop">
<FormItem
:item="item"
:model-value="model[item.prop]"
@update:model-value="
async (value) => {
if (item.onChange && typeof item.onChange === 'function') {
const result = await item.onChange(value);
if (result === false) {
return;
}
}
model[item.prop] = value;
}
"
>
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</FormItem>
<span
v-if="
columns > 0 &&
index > 0 &&
(index + 1) % columns === 0 &&
index < normalizedFormItems.length - 1
"
class="form-item-break"
/>
</template>
</div>
</el-form>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import FormItem from './FormItem.vue';
import type { FormLayoutProps } from '../types/FormLayout.d';
defineOptions({
name: 'FormLayout',
});
const props = withDefaults(defineProps<FormLayoutProps>(), {
formItems: () => [],
rules: () => ({}),
labelWidth: '120px',
labelPosition: 'right',
showLabelColon: true,
columns: 0,
});
const formRef = ref<InstanceType<typeof import('element-plus').ElForm> | null>(null);
const normalizedFormItems = computed(() =>
(props.formItems || []).map((item) => ({
...item,
labelSuffix: item.labelSuffix ?? (props.showLabelColon ? '' : ''),
}))
);
const validate = (callback) => {
if (formRef.value) {
return formRef.value.validate(callback);
}
};
const validateField = (props, callback) => {
if (formRef.value) {
return formRef.value.validateField(props, callback);
}
};
const resetFields = () => {
if (formRef.value) {
formRef.value.resetFields();
}
};
const clearValidate = (props) => {
if (formRef.value) {
formRef.value.clearValidate(props);
}
};
const scrollToField = (prop) => {
if (formRef.value) {
formRef.value.scrollToField(prop);
}
};
defineExpose({
formRef,
validate,
validateField,
resetFields,
clearValidate,
scrollToField,
});
</script>
<style scoped lang="scss">
.form-layout-form {
width: 100%;
.form-items-container {
display: flex;
flex-wrap: wrap;
column-gap: 16px;
row-gap: 16px;
.form-item-break {
flex-basis: 100%;
width: 0;
height: 0;
margin: 0;
padding: 0;
border: none;
}
}
:deep(.el-form-item) {
margin-bottom: 0;
justify-content: flex-start;
flex: 0 0 auto;
.el-form-item__content {
justify-content: flex-start;
text-align: left;
}
}
}
</style>

View File

@@ -0,0 +1,18 @@
<template>
<div class="form-section">
<slot />
</div>
</template>
<script setup>
defineOptions({
name: 'FormSection',
});
</script>
<style scoped lang="scss">
.form-section {
flex-shrink: 0;
margin-bottom: 1rem;
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
<div class="form-section">
<FormLayout ref="formLayoutRef" v-bind="$attrs">
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</FormLayout>
</div>
</template>
<script setup>
import { ref } from 'vue';
import FormLayout from './FormLayout.vue';
defineOptions({
name: 'FormSectionLayout',
inheritAttrs: false,
});
const formLayoutRef = ref(null);
defineExpose({
get formRef() {
return formLayoutRef.value?.formRef;
},
validate: (...args) => formLayoutRef.value?.validate(...args),
validateField: (...args) => formLayoutRef.value?.validateField(...args),
resetFields: (...args) => formLayoutRef.value?.resetFields(...args),
clearValidate: (...args) => formLayoutRef.value?.clearValidate(...args),
scrollToField: (...args) => formLayoutRef.value?.scrollToField(...args),
});
</script>
<style scoped lang="scss">
.form-section {
flex-shrink: 0;
margin-bottom: 1rem;
}
</style>

View File

@@ -0,0 +1,136 @@
<template>
<el-input
:model-value="displayValue"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
@input="handleInput"
@blur="handleBlur"
@change="handleChange"
>
<template v-if="suffix" #suffix>{{ suffix }}</template>
</el-input>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
const props = defineProps({
modelValue: [Number, String],
placeholder: String,
disabled: Boolean,
clearable: {
type: Boolean,
default: true,
},
suffix: String,
precision: Number, // 小数位数
min: Number,
max: Number,
});
const emit = defineEmits(['update:modelValue', 'blur', 'change']);
const displayValue = computed(() => {
if (props.modelValue === null || props.modelValue === undefined || props.modelValue === '') {
return '';
}
return String(props.modelValue);
});
const handleInput = (value) => {
// 只允许数字、小数点和负号
let newValue = value.replace(/[^\d.-]/g, '');
// 只允许一个小数点
const parts = newValue.split('.');
if (parts.length > 2) {
newValue = parts[0] + '.' + parts.slice(1).join('');
}
// 只允许一个负号,且必须在开头
if (newValue.indexOf('-') > 0) {
newValue = newValue.replace(/-/g, '');
}
if (newValue.startsWith('-') && newValue.split('-').length > 2) {
newValue = '-' + newValue.replace(/-/g, '');
}
// 如果为空,直接返回空字符串
if (newValue === '' || newValue === '-') {
emit('update:modelValue', '');
return;
}
// 转换为数字
const numValue = parseFloat(newValue);
if (isNaN(numValue)) {
emit('update:modelValue', '');
return;
}
// 限制最小值
if (props.min !== undefined && numValue < props.min) {
newValue = String(props.min);
}
// 限制最大值
if (props.max !== undefined && numValue > props.max) {
newValue = String(props.max);
}
// 处理精度
if (props.precision !== undefined && newValue.includes('.')) {
const parts = newValue.split('.');
if (parts[1] && parts[1].length > props.precision) {
parts[1] = parts[1].substring(0, props.precision);
newValue = parts.join('.');
}
}
emit('update:modelValue', newValue);
};
const handleBlur = (event) => {
const value = event.target.value;
if (value === '' || value === '-') {
emit('update:modelValue', '');
emit('blur', event);
return;
}
const numValue = parseFloat(value);
if (isNaN(numValue)) {
emit('update:modelValue', '');
emit('blur', event);
return;
}
// 应用精度
let finalValue = numValue;
if (props.precision !== undefined) {
finalValue = parseFloat(numValue.toFixed(props.precision));
}
// 限制范围
if (props.min !== undefined && finalValue < props.min) {
finalValue = props.min;
}
if (props.max !== undefined && finalValue > props.max) {
finalValue = props.max;
}
emit('update:modelValue', String(finalValue));
emit('blur', event);
};
const handleChange = (value) => {
emit('change', value);
};
</script>
<style scoped lang="scss">
:deep(.el-input__inner) {
text-align: left;
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<Layout>
<template #default>
<div class="page-wrapper">
<slot />
</div>
</template>
<template v-if="$slots.footer" #footer>
<slot name="footer" />
</template>
</Layout>
</template>
<script setup>
import Layout from '@/components/Layout/index.vue';
defineOptions({
name: 'PageLayout',
});
</script>
<style scoped lang="scss">
.page-wrapper {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<div class="page-wrapper">
<slot />
</div>
</template>
<script setup>
defineOptions({
name: 'PageWrapper',
});
</script>
<style scoped lang="scss">
.page-wrapper {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,171 @@
<template>
<div class="quick-date-range">
<el-select v-model="quickType" class="quick-select" @change="handleQuickChange">
<el-option label="自定义时间段" value="custom" />
<el-option label="今天" value="today" />
<el-option label="昨天" value="yesterday" />
<el-option label="本周" value="thisWeek" />
<el-option label="上周" value="lastWeek" />
<el-option label="最近30日" value="last30Days" />
</el-select>
<el-date-picker
v-model="innerValue"
type="daterange"
range-separator="-"
:start-placeholder="startPlaceholder || '开始日期'"
:end-placeholder="endPlaceholder || '结束日期'"
:value-format="valueFormat"
:clearable="clearable"
:style="datePickerStyle"
v-bind="attrs"
@change="handleDateChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue';
import type { QuickDateRangeProps } from '../types/QuickDateRange.d';
defineOptions({
name: 'QuickDateRange'
});
const props = withDefaults(defineProps<QuickDateRangeProps>(), {
modelValue: () => [],
startPlaceholder: '',
endPlaceholder: '',
valueFormat: 'YYYY-MM-DD',
clearable: true,
datePickerStyle: () => ({}),
attrs: () => ({}),
});
const emit = defineEmits<{
'update:modelValue': [value: string[]];
change: [value: string[]];
}>();
const innerValue = ref<string[]>(props.modelValue && props.modelValue.length ? [...props.modelValue] : []);
const quickType = ref<string>('custom');
watch(
() => props.modelValue,
(val) => {
if (!val || !val.length) {
innerValue.value = [];
quickType.value = 'custom';
} else {
innerValue.value = [...val];
}
},
{ deep: true }
);
const datePickerStyle = computed(() => {
return Object.assign({ width: '300px' }, props.datePickerStyle || {});
});
function formatDate(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
function getToday() {
const today = new Date();
const d = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const s = formatDate(d);
return [s, s];
}
function getYesterday() {
const today = new Date();
const d = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
const s = formatDate(d);
return [s, s];
}
function getThisWeek() {
const today = new Date();
const day = today.getDay() || 7; // 周日返回 7
const monday = new Date(today);
monday.setDate(today.getDate() - day + 1);
const sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
return [formatDate(monday), formatDate(sunday)];
}
function getLastWeek() {
const today = new Date();
const day = today.getDay() || 7;
const lastMonday = new Date(today);
lastMonday.setDate(today.getDate() - day - 6);
const lastSunday = new Date(lastMonday);
lastSunday.setDate(lastMonday.getDate() + 6);
return [formatDate(lastMonday), formatDate(lastSunday)];
}
function getLast30Days() {
const today = new Date();
const end = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const start = new Date(end);
start.setDate(end.getDate() - 29);
return [formatDate(start), formatDate(end)];
}
function handleQuickChange(val: string) {
if (val === 'custom') {
// 自定义时间段,清空日期值
innerValue.value = [];
emit('update:modelValue', []);
emit('change', []);
return;
}
let range: string[] = [];
switch (val) {
case 'today':
range = getToday();
break;
case 'yesterday':
range = getYesterday();
break;
case 'thisWeek':
range = getThisWeek();
break;
case 'lastWeek':
range = getLastWeek();
break;
case 'last30Days':
range = getLast30Days();
break;
default:
range = [];
}
innerValue.value = range;
emit('update:modelValue', range);
emit('change', range);
}
function handleDateChange(val: string[] | null) {
// 用户手动选择时间段时,将预设切换为自定义
quickType.value = 'custom';
innerValue.value = val || [];
emit('update:modelValue', innerValue.value);
emit('change', innerValue.value);
}
</script>
<style scoped lang="scss">
.quick-date-range {
display: inline-flex;
align-items: center;
gap: 8px;
.quick-select {
width: 130px;
}
}
</style>

View File

@@ -0,0 +1,373 @@
<template>
<div class="table-container">
<div ref="tableWrapperRef" class="table-wrapper">
<el-table
ref="tableRef"
v-loading="loading"
:data="computedTableData"
:border="border"
:stripe="stripe"
:size="size"
:height="computedTableHeight"
:row-key="rowKey"
:highlight-current-row="highlightCurrentRow"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
style="width: 100%; height: 100%"
>
<!-- 通过配置数组生成的列 -->
<template v-for="column in tableColumns" :key="column.prop || column.type">
<el-table-column
v-if="column.type && column.type !== 'expand'"
:type="column.type"
:width="column.width"
:min-width="column.minWidth"
:align="column.align || 'center'"
:fixed="
column.type === 'selection'
? column.fixed !== undefined
? column.fixed
: 'left'
: column.fixed
"
:selectable="column.selectable"
/>
<!-- 展开列支持自定义插槽内容 -->
<el-table-column
v-else-if="column.type === 'expand'"
type="expand"
:width="column.width"
:min-width="column.minWidth"
:fixed="column.fixed"
>
<template #default="scope">
<slot :name="column.slot || 'expand'" :row="scope.row" :scope="scope" />
</template>
</el-table-column>
<!-- 普通数据列 -->
<el-table-column
v-else
:prop="column.prop"
:label="column.label"
:width="column.width"
:min-width="column.minWidth"
:align="column.align || 'left'"
:fixed="column.fixed"
:show-overflow-tooltip="column.showOverflowTooltip !== false"
>
<template v-if="column.slot" #default="scope">
<slot :name="column.slot" :row="scope.row" :scope="scope" />
</template>
<template v-else-if="column.formatter" #default="scope">
{{
column.formatter(
scope.row,
scope.column,
column.prop ? scope.row[column.prop] : undefined,
scope.$index
)
}}
</template>
</el-table-column>
</template>
<!-- 通过插槽自定义的列 -->
<slot name="table" />
</el-table>
</div>
<div v-if="showPagination" ref="paginationWrapperRef" class="pagination-wrapper">
<div
class="pagination-content"
:class="{ 'has-left-content': paginationLeftText || $slots.paginationLeft }"
>
<div v-if="paginationLeftText || $slots.paginationLeft" class="pagination-left">
<slot name="paginationLeft">
{{ paginationLeftText }}
</slot>
</div>
<pagination
v-show="computedTotal > 0"
:total="computedTotal"
:page="computedPageNo"
:limit="computedPageSize"
v-bind="paginationProps"
@pagination="handlePagination"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
import Pagination from '@/components/Pagination/index.vue';
import type { TableProps } from '../types/Table.d';
defineOptions({
name: 'Table',
});
const props = withDefaults(defineProps<TableProps>(), {
tableData: () => [],
loading: false,
border: true,
stripe: false,
size: 'default',
highlightCurrentRow: false,
tableColumns: () => [],
showPagination: false,
total: 0,
pageNo: 1,
pageSize: 20,
isAllData: false,
paginationLeftText: '',
paginationProps: () => ({}),
});
const emit = defineEmits<{
'row-click': [row: Record<string, any>, column: any, event: Event];
'selection-change': [selection: Record<string, any>[]];
'sort-change': [sortInfo: { column: any; prop: string; order: string }];
pagination: [pagination: { page: number; limit: number }];
}>();
const internalPageNo = ref(props.pageNo);
const internalPageSize = ref(props.pageSize);
watch(
() => [props.pageNo, props.pageSize],
([newPageNo, newPageSize]) => {
if (!props.isAllData) {
internalPageNo.value = newPageNo;
internalPageSize.value = newPageSize;
}
}
);
watch(
() => props.isAllData,
(isAllData) => {
if (isAllData) {
internalPageNo.value = props.pageNo;
internalPageSize.value = props.pageSize;
}
}
);
const computedPageNo = computed(() => {
return props.isAllData ? internalPageNo.value : props.pageNo;
});
const computedPageSize = computed(() => {
return props.isAllData ? internalPageSize.value : props.pageSize;
});
const computedTotal = computed(() => {
return props.isAllData ? props.tableData.length : props.total;
});
const computedTableData = computed(() => {
if (!props.isAllData) {
return props.tableData;
}
const start = (computedPageNo.value - 1) * computedPageSize.value;
const end = start + computedPageSize.value;
return props.tableData.slice(start, end);
});
const handlePagination = (pagination: { page: number; limit: number }) => {
if (props.isAllData) {
internalPageNo.value = pagination.page;
internalPageSize.value = pagination.limit;
} else {
emit('pagination', pagination);
}
nextTick(() => {
calculateTableHeight();
});
};
const tableRef = ref<InstanceType<typeof import('element-plus').ElTable> | null>(null);
const tableWrapperRef = ref<HTMLDivElement | null>(null);
const paginationWrapperRef = ref<HTMLDivElement | null>(null);
const dynamicTableHeight = ref<number | null>(null);
const paginationHeight = ref<number>(0);
const computedTableHeight = computed(() => {
if (props.tableHeight) {
return props.tableHeight;
}
if (props.maxHeight) {
return props.maxHeight;
}
if (dynamicTableHeight.value) {
const height = dynamicTableHeight.value - paginationHeight.value;
return height > 0 ? height : dynamicTableHeight.value;
}
return null;
});
const calculateTableHeight = () => {
nextTick(() => {
if (tableWrapperRef.value) {
const tableContainer = tableWrapperRef.value.parentElement;
if (tableContainer) {
const containerRect = tableContainer.getBoundingClientRect();
let height = containerRect.height;
if (props.showPagination && paginationWrapperRef.value && computedTotal.value > 0) {
const paginationRect = paginationWrapperRef.value.getBoundingClientRect();
paginationHeight.value = paginationRect.height;
height -= paginationRect.height;
} else {
paginationHeight.value = 0;
}
if (height > 0) {
dynamicTableHeight.value = height;
}
}
}
});
};
let resizeObserver: ResizeObserver | null = null;
let paginationObserver: ResizeObserver | null = null;
onMounted(() => {
calculateTableHeight();
const tableContainer = tableWrapperRef.value?.parentElement;
if (tableContainer && window.ResizeObserver) {
resizeObserver = new ResizeObserver(() => {
calculateTableHeight();
});
resizeObserver.observe(tableContainer);
} else {
window.addEventListener('resize', calculateTableHeight);
}
});
watch(
() => props.showPagination && computedTotal.value > 0 && paginationWrapperRef.value,
(shouldObserve) => {
if (shouldObserve && paginationWrapperRef.value && window.ResizeObserver) {
if (!paginationObserver) {
paginationObserver = new ResizeObserver(() => {
calculateTableHeight();
});
}
paginationObserver.observe(paginationWrapperRef.value);
} else if (paginationObserver && paginationWrapperRef.value) {
paginationObserver.unobserve(paginationWrapperRef.value);
}
},
{ immediate: true }
);
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect();
}
if (paginationObserver) {
paginationObserver.disconnect();
}
if (!resizeObserver) {
window.removeEventListener('resize', calculateTableHeight);
}
});
watch(
() => props.tableData,
() => {
calculateTableHeight();
if (props.isAllData && internalPageNo.value !== 1) {
internalPageNo.value = 1;
}
},
{ deep: true }
);
watch(
() => [props.showPagination, computedTotal.value],
() => {
calculateTableHeight();
}
);
const handleRowClick = (row: Record<string, any>, column: any, event: Event) => {
emit('row-click', row, column, event);
};
const handleSelectionChange = (selection: Record<string, any>[]) => {
emit('selection-change', selection);
};
const handleSortChange = ({
column,
prop,
order,
}: {
column: any;
prop: string;
order: string;
}) => {
emit('sort-change', { column, prop, order });
};
defineExpose({
tableRef,
tableWrapperRef,
});
</script>
<style scoped lang="scss">
.table-container {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.table-wrapper {
flex: 1;
min-height: 0;
overflow: hidden;
position: relative;
}
.pagination-wrapper {
flex-shrink: 0;
margin-top: 8px;
padding-bottom: 0;
overflow: visible;
}
.pagination-content {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
&.has-left-content {
justify-content: space-between;
}
}
.pagination-left {
flex-shrink: 0;
display: flex;
justify-content: flex-start;
align-items: center;
color: var(--el-text-color-regular);
font-size: 14px;
}
.pagination-content :deep(.pagination-container) {
.el-pagination {
margin-right: 16px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}
</style>

View File

@@ -0,0 +1,27 @@
<template>
<div class="table-section">
<slot />
</div>
</template>
<script setup>
defineOptions({
name: 'TableSection',
});
</script>
<style scoped lang="scss">
.table-section {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
:deep(.editable-table) {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
</style>

View File

@@ -0,0 +1,411 @@
<template>
<div class="table-layout-container">
<div class="card-content-wrapper">
<div
v-if="showSideQuery"
class="side-query-wrapper"
:class="{ collapsed: sideQueryCollapsed }"
>
<div v-if="!sideQueryCollapsed" class="side-query-header">
<el-input v-model="sideSearchKeyword" placeholder="搜索树节点" clearable size="small">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div v-if="!sideQueryCollapsed" class="side-query-content">
<el-tree
ref="treeRef"
:data="treeDataWithAll"
:props="defaultProps"
:node-key="treeNodeKey"
:expand-on-click-node="false"
default-expand-all
highlight-current
@node-click="handleNodeClick"
@current-change="handleCurrentChange"
></el-tree>
</div>
</div>
<div v-if="showSideQuery" class="collapse-divider">
<el-button
circle
size="small"
class="collapse-btn"
@click="sideQueryCollapsed = !sideQueryCollapsed"
>
<el-icon>
<ArrowRight v-if="sideQueryCollapsed" />
<ArrowLeft v-else />
</el-icon>
</el-button>
</div>
<!-- 主内容区域 -->
<div
class="main-content-wrapper"
:class="{ 'with-side-query': showSideQuery && !sideQueryCollapsed }"
>
<Filter
v-if="showTopQuery"
ref="queryFormComponentRef"
:query-params="queryParams"
:form-items="formItems"
:show-default-buttons="showDefaultButtons"
@query="handleQuery"
@reset="resetQuery"
>
<template
v-for="item in customFormItems"
:key="item.prop"
v-slot:[item.slotName]="slotProps"
>
<slot :name="item.slotName" :item="slotProps.item" :queryParams="props.queryParams" />
</template>
<template #default="{ queryParams, handleQuery, resetQuery }">
<slot
name="topQuery"
:queryParams="queryParams"
:handleQuery="handleQuery"
:resetQuery="resetQuery"
/>
</template>
</Filter>
<!-- 操作按钮区域 -->
<div class="table-operation-bar">
<slot name="operations" />
</div>
<!-- 表格区域 -->
<Table
:table-data="tableData"
:loading="loading"
:border="border"
:stripe="stripe"
:size="size"
:table-height="tableHeight"
:max-height="maxHeight"
:row-key="rowKey"
:highlight-current-row="highlightCurrentRow"
:table-columns="tableColumns"
:show-pagination="showPagination"
:total="total"
:page-no="props.queryParams.pageNo"
:page-size="props.queryParams.pageSize"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
@pagination="handlePagination"
>
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps" />
</template>
</Table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import Filter from './Filter.vue';
import Table from './Table.vue';
import type { TableLayoutProps, TreeNodeData } from '../types/TableLayout.d';
defineOptions({
name: 'TableLayout',
});
const props = withDefaults(defineProps<TableLayoutProps>(), {
tableData: () => [],
loading: false,
total: 0,
queryParams: () => ({
pageNo: 1,
pageSize: 20,
}),
sideQueryParams: () => ({}),
formItems: () => [],
showTopQuery: true,
showSideQuery: false,
showPagination: true,
showDefaultButtons: true,
sideWidth: 6,
border: true,
stripe: false,
size: 'default',
highlightCurrentRow: false,
siderData: () => [],
treeNodeKey: 'id',
tableColumns: () => [],
});
const emit = defineEmits<{
query: [queryParams: Record<string, any>];
reset: [];
pagination: [pagination: { page: number; limit: number }];
'row-click': [row: Record<string, any>, column: any, event: Event];
'selection-change': [selection: Record<string, any>[]];
'sort-change': [sortInfo: { column: any; prop: string; order: string }];
'side-query': [node: TreeNodeData];
'reset-side-query': [];
}>();
const queryFormRef = ref<InstanceType<typeof import('element-plus').ElForm> | null>(null);
import type { FilterExpose } from '../types/Filter.d';
const queryFormComponentRef = ref<FilterExpose | null>(null);
const sideSearchKeyword = ref<string>('');
const treeRef = ref<InstanceType<typeof import('element-plus').ElTree> | null>(null);
const currentTreeNode = ref<TreeNodeData | null>(null);
const sideQueryCollapsed = ref<boolean>(false);
const customFormItems = computed(() => {
return props.formItems
.filter((item) => item.type === 'custom')
.map((item) => ({
...item,
slotName: item.slot || item.prop,
}));
});
const defaultProps = {
children: 'children',
label: 'label',
};
const filteredSiderData = computed(() => {
if (!sideSearchKeyword.value || !props.siderData || props.siderData.length === 0) {
return props.siderData;
}
const keyword = sideSearchKeyword.value.toLowerCase();
const filterTree = (nodes: TreeNodeData[]): TreeNodeData[] => {
if (!nodes || nodes.length === 0) return [];
return nodes
.map((node: TreeNodeData) => {
const label = (node[defaultProps.label] || '').toLowerCase();
const match = label.includes(keyword);
const children = node[defaultProps.children];
let filteredChildren: TreeNodeData[] | null = null;
if (children && children.length > 0) {
filteredChildren = filterTree(children);
}
if (match || (filteredChildren && filteredChildren.length > 0)) {
return {
...node,
[defaultProps.children]: filteredChildren,
};
}
return null;
})
.filter(Boolean) as TreeNodeData[];
};
return filterTree(props.siderData);
});
const treeDataWithAll = computed(() => {
const children = filteredSiderData.value || [];
return [
{
[props.treeNodeKey]: '__ALL__',
[defaultProps.label]: '全部',
[defaultProps.children]: children || [],
},
];
});
const handleQuery = () => {
props.queryParams.pageNo = 1;
emit('query', props.queryParams);
if (currentTreeNode.value) {
emit('side-query', currentTreeNode.value);
}
};
const handleNodeClick = (data: TreeNodeData, node: any) => {
currentTreeNode.value = data;
if (treeRef.value && data && data[props.treeNodeKey]) {
treeRef.value.setCurrentKey(data[props.treeNodeKey]);
}
handleQuery();
};
const handleCurrentChange = (data: TreeNodeData, node: any) => {
currentTreeNode.value = data;
};
const resetQuery = () => {
if (queryFormComponentRef.value?.queryFormRef) {
queryFormComponentRef.value.queryFormRef.resetFields();
}
if (props.queryParams) {
Object.keys(props.queryParams).forEach((key) => {
if (key !== 'pageNo' && key !== 'pageSize') {
if (Array.isArray(props.queryParams[key])) {
props.queryParams[key] = [];
} else if (typeof props.queryParams[key] === 'object' && props.queryParams[key] !== null) {
props.queryParams[key] = null;
} else {
props.queryParams[key] = '';
}
}
});
if (Object.prototype.hasOwnProperty.call(props.queryParams, 'pageNo')) {
props.queryParams.pageNo = 1;
}
}
emit('reset');
handleQuery();
};
const handlePagination = (pagination) => {
if (props.queryParams) {
props.queryParams.pageNo = pagination.page;
props.queryParams.pageSize = pagination.limit;
}
emit('pagination', pagination);
emit('query', props.queryParams);
if (currentTreeNode.value) {
emit('side-query', currentTreeNode.value);
}
};
const handleRowClick = (row, column, event) => {
emit('row-click', row, column, event);
};
const handleSelectionChange = (selection) => {
emit('selection-change', selection);
};
const handleSortChange = ({ column, prop, order }) => {
emit('sort-change', { column, prop, order });
};
defineExpose({
queryFormRef: computed(() => queryFormComponentRef.value?.queryFormRef),
handleQuery,
resetQuery,
});
</script>
<style scoped lang="scss">
.table-layout-container {
height: 100%;
padding: 8px;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
.main-content-card {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
min-height: 0;
overflow: visible;
:deep(.el-card__body) {
flex: 1;
display: flex;
flex-direction: column;
padding: 16px 16px 8px 16px;
min-height: 0;
overflow: visible;
}
}
.card-content-wrapper {
flex: 1;
display: flex;
gap: 0;
min-height: 0;
position: relative;
}
.collapse-divider {
flex-shrink: 0;
width: 1px;
background-color: #ebeef5;
position: relative;
display: flex;
align-items: flex-start;
justify-content: center;
margin: 0 12px;
.collapse-btn {
position: absolute;
left: 50%;
top: 18px;
transform: translateX(-50%);
background-color: #fff;
border: 1px solid #ebeef5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
z-index: 10;
width: 24px;
height: 24px;
&:hover {
background-color: #409eff;
color: #fff;
border-color: #409eff;
}
}
}
.side-query-wrapper {
flex-shrink: 0;
display: flex;
flex-direction: column;
transition: width 0.3s, opacity 0.3s;
overflow: hidden;
&.collapsed {
width: 0;
opacity: 0;
padding: 0;
border: none;
}
.side-query-header {
flex-shrink: 0;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.side-query-content {
flex: 1;
min-height: 0;
overflow-y: auto;
:deep(.el-tree--highlight-current) {
background-color: #fff !important;
}
}
}
.main-content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
min-width: 0;
overflow: hidden;
}
.table-operation-bar {
flex-shrink: 0;
margin-bottom: 8px;
}
}
</style>

View File

@@ -1,188 +1,125 @@
<!--
* @Author: sjjh
* @Date: 2025-04-07 20:42:45
* @Description:住院患者信息给医生用带折叠
-->
<template>
<div class="inPatientBarDoctorFold-container">
<div class="basic_info">
<div class="patient-header white-bg">
<div class="select_wrapper_div">
<b class="bedNumber" style="margin-left: 12px">{{ patientInfo?.bedName }}</b>
<label class="content-text-color" style="margin-left: 12px; color: #a15209">
{{ patientInfo?.patientName }}
<span class="sex-age"> {{ patientInfo?.sexName }}/{{ patientInfo?.age }} </span>
<b class="bedNumber">{{ patientInfo?.bedName || '未分床' }}</b>
<label class="patient-name">
{{ patientInfo?.patientName || '-' }}
<span class="sex-age">
{{ formatSexAge(patientInfo?.sexName, patientInfo?.age) }}
</span>
</label>
<div style="display: flex; margin-left: 8px">
<!-- 状态展示// TODO 后端给状态,前段 -->
<div class="tag-list" v-if="patientInfo?.list && patientInfo.list.length > 0">
<ball-tag
style="margin-right: 4px"
v-for="item in patientInfo?.list"
v-for="item in patientInfo.list"
:key="item"
:tagId="item"
class="tag-item"
></ball-tag>
</div>
<div
class="gray-border"
v-show="patientInfo?.feeTypeName && patientInfo?.feeTypeName !== ''"
>
{{ patientInfo?.feeTypeName }}
<div class="gray-border" v-if="patientInfo?.feeTypeName">
{{ patientInfo.feeTypeName }}
</div>
<label style="margin-left: 24px">
<label class="info-label">
<span class="label-text-color">住院</span>
<span class="content-text-color">{{ patientInfo?.inHospitalDays + '天' }}</span>
<span class="content-text-color">{{ formatDays(patientInfo?.inHospitalDays) }}</span>
</label>
<label style="margin-left: 24px">
<label class="info-label" v-if="patientInfo?.inOrgTime">
<span class="label-text-color">入科</span>
<span class="content-text-color">{{ patientInfo?.inDeptDate }}</span>
<span class="content-text-color">{{ patientInfo.inOrgTime }}</span>
</label>
<label style="margin-left: 24px">
<label class="info-label" v-if="patientInfo?.inHospitalTime">
<span class="label-text-color">入院时间</span>
<span class="content-text-color">{{ patientInfo?.inHospitalTime }}</span>
<span class="content-text-color">{{ patientInfo.inHospitalTime }}</span>
</label>
<label style="margin-left: 24px">
<span class="label-text-color">住院号:{{ patientInfo?.busNo }}</span>
<label class="info-label" v-if="patientInfo?.busNo">
<span class="label-text-color">住院号</span>
<span class="content-text-color">{{ patientInfo.busNo }}</span>
</label>
<svg-icon icon-class="hipCopy" height="20px" width="20px" class="copy-svg" />
<label style="margin-left: 30px">
<label class="info-label diagnosis-label" v-if="patientInfo?.regDiagnosisName">
<span class="label-text-color">诊断</span>
<span class="content-text-color">{{ patientInfo?.regDiagnosisName }}</span>
<span class="content-text-color">{{ patientInfo.regDiagnosisName }}</span>
</label>
<label class="info-label">
<span class="label-text-color">费用</span>
<span class="content-text-color">{{ formatMoney(patientInfo?.totalAmount) }}</span>
</label>
<label class="info-label">
<span class="label-text-color">余额</span>
<span class="content-text-color">{{ formatMoney(patientInfo?.balanceAmount) }}</span>
</label>
<!-- <div style="margin-left: auto">
<el-icon v-if="expand" @click="toggleExpand"><ArrowUpBold /></el-icon>
<el-icon v-else @click="toggleExpand"><ArrowDownBold /></el-icon>
</div> -->
</div>
</div>
</div>
<div v-if="expand" class="expand_more">
<div style="background-color: #ffffff">
<div style="margin-top: -10px">
<label style="font-size: 14px">
<div class="expand-content">
<div class="expand-section">
<label class="expand-label">
<span class="primary-text">过敏</span>
<span class="primary-text">{{ patientInfo?.allergies || '无过敏史' }}</span>
</label>
<label style="font-size: 14px; margin-left: 32px" v-show="patientInfo?.insuplcAdmdvsName">
<span class="primary-text">医保统筹区:</span>
<span class="primary-text">{{ patientInfo?.insuplcAdmdvsName }}</span>
<label class="expand-label" v-if="patientInfo?.insuplcAdmdvsName">
<span class="primary-text">医保统筹区</span>
<span class="primary-text">{{ patientInfo.insuplcAdmdvsName }}</span>
</label>
<label style="font-size: 14px; margin-left: 32px" v-show="patientInfo?.ciType">
<label class="expand-label" v-if="patientInfo?.ciType">
<span class="primary-text">商保信息</span>
<span class="primary-text">{{ patientInfo?.ciType }}</span>
<span class="primary-text">{{ patientInfo.ciType }}</span>
</label>
<div style="display: flex; flex-wrap: nowrap; margin-top: 8px; white-space: nowrap">
<div
class="blue-bg"
style="background-color: #f1faff; flex-shrink: 0; min-width: fit-content"
>
<div class="info-tags">
<div class="blue-bg">
<span class="content-text-color">
{{
patientInfo?.height && patientInfo?.weight
? `${patientInfo?.height}cm/${patientInfo?.weight}kg`
: '身高/体重'
}}
{{ formatHeightWeight(patientInfo?.height, patientInfo?.weight) }}
</span>
</div>
<div
class="blue-bg"
style="
margin-left: 24px;
background-color: #f1faff;
flex-shrink: 0;
min-width: fit-content;
"
v-show="patientInfo?.postoperativeDays"
>
<span class="content-text-color">术后{{ patientInfo?.postoperativeDays }}</span>
<div class="blue-bg" v-if="patientInfo?.postoperativeDays">
<span class="content-text-color">术后{{ patientInfo.postoperativeDays }}</span>
</div>
<div
class="blue-bg"
style="
margin-left: 16px;
background-color: #f1faff;
flex-shrink: 0;
min-width: fit-content;
"
v-show="patientInfo?.poorTypeName"
>
<span class="label-text-color">贫困类型:</span>
<span class="content-text-color" style="margin-left: 4px">{{
patientInfo?.poorTypeName
}}</span>
<div class="blue-bg" v-if="patientInfo?.poorTypeName">
<span class="label-text-color">贫困类型</span>
<span class="content-text-color">{{ patientInfo.poorTypeName }}</span>
</div>
<div
class="blue-bg"
style="
margin-left: 16px;
background-color: #f1faff;
flex-shrink: 0;
min-width: fit-content;
"
v-show="patientInfo?.pathwayName"
>
<span class="label-text-color">路径情况:</span>
<span class="content-text-color" style="margin-left: 4px">{{
patientInfo?.pathwayName
}}</span>
<div class="blue-bg" v-if="patientInfo?.pathwayName">
<span class="label-text-color">路径情况</span>
<span class="content-text-color">{{ patientInfo.pathwayName }}</span>
</div>
</div>
</div>
</div>
<div style="background-color: #ffffff">
<div style="margin-top: -10px">
<div class="expand-content">
<div class="expand-section">
<div class="patient-board">
<div class="item-center">
<div class="line-block">
<div class="line-block-top">
<span class="label-text-color">科室</span>
<span class="content-text-color">{{ patientInfo?.admissionDeptName }}</span>
<span class="content-text-color">{{
patientInfo?.admissionDeptName || '-'
}}</span>
</div>
<div class="line-block-bottom">
<span class="label-text-color">病区</span>
<span class="content-text-color">{{ patientInfo?.deptNurseName }}</span>
<span class="content-text-color">{{ patientInfo?.deptNurseName || '-' }}</span>
</div>
</div>
<div class="line-block">
<div class="line-block-top">
<span class="label-text-color">主治医生</span>
<span class="content-text-color">{{ patientInfo?.masterDoctorName }}</span>
<span class="content-text-color">{{ patientInfo?.masterDoctorName || '-' }}</span>
</div>
<div class="line-block-bottom">
<span class="label-text-color">责任护士</span>
<span class="content-text-color">{{ patientInfo?.masterNurseName }}</span>
</div>
</div>
<div class="line-blockMoney">
<div class="line-blockMoney-top">
<span class="label-text-color">费用</span>
</div>
<div class="line-blockMoney-bottom">
<b class="money-content size-15">{{
patientInfo?.totalAmount ? patientInfo?.totalAmount : 0
}}</b>
<span class="content-text-color">{{ patientInfo?.masterNurseName || '-' }}</span>
</div>
</div>
<div class="line-blockMoney">
<div class="line-blockMoney-top">
<span class="label-text-color">预交金</span>
</div>
<div class="line-blockMoney-bottom">
<b class="money-content size-15">{{
patientInfo?.prepayAmount ? patientInfo?.prepayAmount : 0
}}</b>
</div>
</div>
<div class="line-blockMoney">
<div class="line-blockMoney-top">
<span class="label-text-color">余额</span>
</div>
<div class="line-blockMoney-bottom">
<b class="money-content size-15">{{
patientInfo?.balance ? patientInfo?.balance : 0
}}</b>
<b class="money-content size-15">{{ formatMoney(patientInfo?.prepayAmount) }}</b>
</div>
</div>
</div>
@@ -192,45 +129,57 @@
</div>
</div>
</template>
<script setup>
import { computed, onMounted, ref, watch } from 'vue';
<script setup lang="ts">
import { ref, watch } from 'vue';
import BallTag from './components/BallTag.vue';
import { patientInfo } from '@/views/inpatientDoctor/home/store/patient.js';
// import { ElMessage } from 'element-plus'
const expand = ref(false);
const showDividers = ref(true);
// 示例方法:切换显示状态
const toggleDividers = () => {
showDividers.value = !showDividers.value;
};
const iconClass = ref('hipBarDown');
// 切换展开状态的方法
function toggleExpand() {
expand.value = !expand.value;
iconClass.value = expand.value ? 'hipBarUp' : 'hipBarDown';
toggleDividers();
interface Props {
visitCode?: string;
}
const fetchPatientInfoById = async (patientId) => {
// 查询患者信息
console.log(patientId);
};
const props = defineProps({
const props = withDefaults(defineProps<Props>(), {
visitCode: '',
});
watch(
() => props.visitCode,
(val) => {
if (val !== null && val !== '') {
fetchPatientInfoById(val);
}
const expand = ref<boolean>(false);
// 格式化性别和年龄
const formatSexAge = (sexName?: string, age?: number | string): string => {
const sex = sexName || '';
const ageStr = age !== undefined && age !== null ? String(age) : '';
if (sex && ageStr) {
return `${sex}/${ageStr}`;
} else if (sex) {
return sex;
} else if (ageStr) {
return ageStr;
}
);
return '-';
};
// 格式化天数
const formatDays = (days?: number | string): string => {
if (days === undefined || days === null || days === '') {
return '-';
}
return `${days}`;
};
// 格式化身高体重
const formatHeightWeight = (height?: number | string, weight?: number | string): string => {
if (height && weight) {
return `${height}cm/${weight}kg`;
}
return '身高/体重';
};
// 格式化金额
const formatMoney = (amount?: number | string): number => {
if (amount === undefined || amount === null || amount === '') {
return 0;
}
return Number(amount) || 0;
};
defineOptions({
name: 'NurserDoctorPatientBarminimal',
@@ -240,27 +189,77 @@ defineOptions({
<style lang="scss" scoped>
.inPatientBarDoctorFold-container {
border-bottom: 1px solid #ebeef5;
background-color: #ffffff;
align-items: center;
.basic_info {
height: 43px;
min-height: 44px;
padding: 0 8px;
display: flex;
align-items: center;
}
/* expand_more */
.expand_more {
width: 100%;
height: 56px;
display: flex;
justify-content: space-between;
flex-direction: column;
padding: 0 8px;
}
.expand-content {
background-color: #ffffff;
}
.expand-section {
margin-top: -10px;
padding: 8px 0;
}
.patient-header {
width: 100%;
padding: 6px 0;
font-size: 13px;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
.select_wrapper_div {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
width: 100%;
}
.bedNumber {
font-weight: bold;
font-size: 18px;
margin-left: 12px;
color: #1f2933;
}
.patient-name {
margin-left: 12px;
color: #a15209;
font-weight: 500;
}
.sex-age {
margin-left: 20px;
margin-left: 8px;
color: var(--hip-color-text-description);
font-size: 14px;
font-weight: normal;
}
.tag-list {
display: flex;
align-items: center;
margin-left: 8px;
gap: 4px;
}
.tag-item {
margin-right: 4px;
}
.gray-border {
@@ -270,43 +269,66 @@ defineOptions({
align-items: center;
border: 1px solid var(--hip-color-text-description);
border-radius: 20px;
padding: 0px 8px;
padding: 0 8px;
font-size: 12px;
}
.info-label {
margin-left: 24px;
white-space: nowrap;
&:first-of-type {
margin-left: 0;
}
}
.diagnosis-label {
margin-left: 30px;
}
.copy-svg {
fill: var(--hip-color-primary);
cursor: pointer;
margin-left: 4px;
transition: opacity 0.2s;
&:hover {
opacity: 0.8;
}
}
}
.expand-label {
font-size: 14px;
margin-right: 32px;
white-space: nowrap;
&:first-child {
margin-right: 32px;
}
}
.info-tags {
display: flex;
flex-wrap: nowrap;
margin-top: 8px;
white-space: nowrap;
gap: 16px;
}
.size-15 {
font-size: 15px;
}
.bedNumber {
font-weight: bold;
font-size: 18px;
}
.primary-text {
color: var(--hip-color-primary);
}
.flex-between {
display: flex;
justify-content: space-between;
}
.item-center {
display: flex;
align-items: center;
}
.flex-row {
display: flex;
}
.patient-board {
margin-left: 20px;
@@ -330,10 +352,8 @@ defineOptions({
margin-bottom: 8px;
}
.money-content {
color: #ff8616;
font-size: 20px;
font-weight: 500;
&-bottom {
margin-top: 4px;
}
}
@@ -354,7 +374,7 @@ defineOptions({
}
&-top {
margin-bottom: 0px;
margin-bottom: 0;
}
.money-content {
@@ -378,13 +398,24 @@ defineOptions({
justify-content: center;
align-items: center;
background-color: #f1faff;
padding-left: 8px;
padding-right: 8px;
border-radius: 4px; /*圆角*/
padding: 4px 8px;
border-radius: 4px;
flex-shrink: 0;
min-width: fit-content;
white-space: nowrap;
.content-text-color {
margin-left: 0;
}
.label-text-color {
margin-right: 4px;
}
}
.label-text-color {
font-size: 14px;
color: #666666;
}
.content-text-color {

View File

@@ -0,0 +1,42 @@
/**
* Dialog 尺寸类型
*/
export type DialogSize = 'small' | 'medium' | 'large';
/**
* Dialog 组件的 Props 类型
*/
export interface DialogProps {
/** 对话框标题 */
title?: string;
/** 对话框尺寸 */
size?: DialogSize;
/** 自定义宽度 */
width?: string | number;
/** 自定义高度 */
height?: string | number;
/** 是否显示对话框 */
modelValue?: boolean;
/** 是否在关闭时销毁子元素 */
destroyOnClose?: boolean;
/** 是否将对话框追加到 body 上 */
appendToBody?: boolean;
/** 是否可以通过点击遮罩层关闭对话框 */
closeOnClickModal?: boolean;
/** 是否可以通过按下 ESC 关闭对话框 */
closeOnPressEscape?: boolean;
/** 是否显示关闭按钮 */
showClose?: boolean;
/** 是否在对话框出现时将 body 滚动锁定 */
lockScroll?: boolean;
/** 自定义类名 */
customClass?: string;
/** 是否可拖拽 */
draggable?: boolean;
/** 是否全屏 */
fullscreen?: boolean;
/** 是否显示加载状态 */
loading?: boolean;
/** 对话框打开前的回调 */
beforeClose?: (done: () => void) => void;
}

View File

@@ -0,0 +1,99 @@
import type { FormItemOption } from './FormItem.d';
/**
* 可编辑表格列配置类型
*/
export interface EditableTableColumn {
/** 列字段名 */
prop: string;
/** 列标签 */
label: string;
/** 列宽度 */
width?: string | number;
/** 最小宽度 */
minWidth?: string | number;
/** 是否固定列 */
fixed?: boolean | 'left' | 'right';
/** 对齐方式 */
align?: 'left' | 'center' | 'right';
/** 列类型 */
type?: 'input' | 'number' | 'select' | 'date' | 'slot';
/** 占位符 */
placeholder?: string;
/** 是否禁用 */
disabled?: boolean | ((row: Record<string, any>, index: number) => boolean);
/** 是否可清空 */
clearable?: boolean;
/** 是否可搜索select 类型) */
filterable?: boolean;
/** 是否多选select 类型) */
multiple?: boolean;
/** 最小值number 类型) */
min?: number;
/** 最大值number 类型) */
max?: number;
/** 精度number 类型) */
precision?: number;
/** 日期类型date 类型) */
dateType?: 'date' | 'datetime' | 'daterange';
/** 日期格式 */
valueFormat?: string;
/** 选项列表select 类型) */
options?: FormItemOption[] | ((row: Record<string, any>, index: number) => FormItemOption[]);
/** 自定义插槽名称slot 类型) */
slot?: string;
/** 格式化函数 */
formatter?: (row: Record<string, any>, column: any, cellValue: any) => string;
/** 验证规则 */
rules?: any;
/** 后缀文本input 类型) */
suffix?: string;
/** 条件显示 */
vIf?: () => boolean;
/** 失焦回调 */
onBlur?: (row: Record<string, any>, index: number) => void;
/** 输入回调 */
onInput?: (row: Record<string, any>, index: number) => void;
/** 变更回调 */
onChange?: (row: Record<string, any>, index: number, value?: any) => void;
}
/**
* EditableTable 组件的 Props 类型
*/
export interface EditableTableProps {
/** 表格数据 */
modelValue: Record<string, any>[];
/** 列配置 */
columns: EditableTableColumn[];
/** 表单验证规则 */
rules?: Record<string, any>;
/** 默认行数据 */
defaultRow?: Record<string, any>;
/** 是否显示边框 */
border?: boolean;
/** 是否显示斑马纹 */
stripe?: boolean;
/** 最大高度 */
maxHeight?: string | number;
/** 最小高度 */
minHeight?: string | number;
/** 是否开启虚拟滚动(不传则根据数据量自动开启) */
virtualized?: boolean;
/** 自动开启虚拟滚动的行数阈值,默认 100 */
virtualizedThreshold?: number;
/** 是否显示选择列 */
showSelection?: boolean;
/** 是否显示新增按钮 */
showAddButton?: boolean;
/** 是否显示删除按钮 */
showDeleteButton?: boolean;
/** 是否显示行级增删按钮 */
showRowActions?: boolean;
/** 是否显示行级“增加”按钮 */
showRowAddButton?: boolean;
/** 是否显示行级“删除”按钮 */
showRowDeleteButton?: boolean;
/** 搜索字段列表(用于筛选,不为空时自动显示搜索框) */
searchFields?: string[];
}

View File

@@ -0,0 +1,30 @@
import type { FormItemConfig } from './FormItem.d';
import type { DefineComponent } from 'vue';
/**
* Filter 组件的 Props 类型
*/
export interface FilterProps {
/** 查询参数对象 */
queryParams: Record<string, any>;
/** 表单项配置数组 */
formItems: FormItemConfig[];
/** 是否显示 */
show?: boolean;
/** 是否显示默认按钮 */
showDefaultButtons?: boolean;
/** 标签宽度 */
labelWidth?: string;
/** 标签后是否添加冒号 */
showLabelColon?: boolean;
}
/**
* Filter 组件暴露的方法
*/
export interface FilterExpose {
queryFormRef: InstanceType<typeof import('element-plus').ElForm> | null;
handleQuery: () => void;
resetQuery: () => void;
}

View File

@@ -0,0 +1,21 @@
import type { FormItemConfig } from './FormItem.d';
/**
* Form 组件的 Props 类型
*/
export interface FormProps {
/** 表单数据对象 */
model: Record<string, any>;
/** 表单项配置数组 */
formItems: FormItemConfig[];
/** 表单验证规则 */
rules?: Record<string, any>;
/** 标签宽度 */
labelWidth?: string;
/** 是否内联表单 */
inline?: boolean;
/** 标签位置 */
labelPosition?: 'left' | 'right' | 'top';
/** 标签后是否添加冒号 */
showLabelColon?: boolean;
}

View File

@@ -0,0 +1,82 @@
import type { CSSProperties } from 'vue';
/**
* 表单项选项类型
*/
export interface FormItemOption {
/** 选项标签 */
label: string;
/** 选项值 */
value: string | number | boolean | null | undefined;
/** 是否禁用该选项 */
disabled?: boolean;
}
/**
* onChange 回调函数类型
* @param value - 新的值
* @returns 返回 false 时阻止更新,返回其他值或 undefined 时允许更新
*/
export type OnChangeCallback = (
value: string | number | Array<string | number> | Date | null | undefined
) => Promise<boolean | void> | boolean | void;
/**
* 表单项配置类型
*/
export interface FormItemConfig {
/** 表单项类型 */
type: 'input' | 'select' | 'radio' | 'date' | 'daterange' | 'custom' | 'text';
/** 表单项标签 */
label: string;
/** 表单项字段名(对应 queryParams 的 key */
prop: string;
/** 占位符文本 */
placeholder?: string;
/** 开始日期占位符(仅 daterange 类型) */
startPlaceholder?: string;
/** 结束日期占位符(仅 daterange 类型) */
endPlaceholder?: string;
/** 是否可清空 */
clearable?: boolean;
/** 自定义样式对象或字符串 */
style?: CSSProperties | string | { width?: string; [key: string]: any };
/** 表单项宽度(字符串,如 '200px' */
width?: string;
/** 额外的属性(会通过 v-bind 传递给组件) */
extraprops?: Record<string, any>;
/** 是否多选(仅 select 类型) */
multiple?: boolean;
/** 是否可搜索(仅 select 类型) */
filterable?: boolean;
/** 多选时是否折叠标签(仅 select 类型) */
collapseTags?: boolean;
/** 选项列表select 和 radio 类型) */
options?: FormItemOption[];
/** 日期格式date 和 daterange 类型) */
valueFormat?: string;
/** 自定义插槽名称custom 类型) */
slot?: string;
/** 是否必填 */
required?: boolean;
/** 标签后缀 */
labelSuffix?: string;
/** 值变更时的回调函数 */
onChange?: OnChangeCallback;
/** 是否在变更前进行检查(仅 select 类型,为 true 时会执行 onChange 回调) */
checkBeforeChange?: boolean;
/** 格式化函数text 类型,用于格式化显示的文本) */
formatter?: (value: any) => string;
}
/**
* FormItem 组件的 Props 类型
*/
export interface FormItemProps {
/** 表单项配置对象 */
item: FormItemConfig;
/** 表单项的值 */
modelValue?: string | number | Array<string | number> | Date | null;
/** 回车键按下时的回调函数 */
onEnter?: () => void;
}

View File

@@ -0,0 +1,21 @@
import type { FormItemConfig } from './FormItem.d';
/**
* FormLayout 组件的 Props 类型
*/
export interface FormLayoutProps {
/** 表单数据对象 */
model: Record<string, any>;
/** 表单项配置数组 */
formItems: FormItemConfig[];
/** 表单验证规则 */
rules?: Record<string, any>;
/** 标签宽度 */
labelWidth?: string;
/** 标签位置 */
labelPosition?: 'left' | 'right' | 'top';
/** 标签后是否添加冒号 */
showLabelColon?: boolean;
/** 列数0 表示不限制) */
columns?: number;
}

View File

@@ -0,0 +1,21 @@
import type { DefineComponent } from 'vue';
/**
* Pagination 组件的 Props 类型
*/
export interface PaginationProps {
/** 总记录数 */
total: number;
/** 当前页码 */
page: number;
/** 每页条数 */
limit: number;
}
/**
* 为 Pagination.vue 组件提供类型声明
*/
declare module '@/components/Pagination/index.vue' {
const component: DefineComponent<PaginationProps>;
export default component;
}

View File

@@ -0,0 +1,21 @@
import type { DefineComponent } from 'vue';
/**
* QuickDateRange 组件的 Props 类型
*/
export interface QuickDateRangeProps {
/** 日期范围值(字符串数组,如 ['2024-01-01', '2024-01-31'] */
modelValue?: string[];
/** 开始日期占位符 */
startPlaceholder?: string;
/** 结束日期占位符 */
endPlaceholder?: string;
/** 日期格式 */
valueFormat?: string;
/** 是否可清空 */
clearable?: boolean;
/** 日期选择器的样式 */
datePickerStyle?: Record<string, any>;
/** 透传给日期选择器的其他属性 */
attrs?: Record<string, any>;
}

View File

@@ -0,0 +1,48 @@
import type { TableColumn } from './TableLayout.d';
/**
* Table 组件的 Props 类型
*/
export interface TableProps {
/** 表格数据 */
tableData: Record<string, any>[];
/** 加载状态 */
loading?: boolean;
/** 是否显示边框 */
border?: boolean;
/** 是否显示斑马纹 */
stripe?: boolean;
/** 表格尺寸 */
size?: 'large' | 'default' | 'small';
/** 表格高度 */
tableHeight?: string | number;
/** 最大高度 */
maxHeight?: string | number;
/** 行键 */
rowKey?: string | ((row: Record<string, any>) => string);
/** 是否高亮当前行 */
highlightCurrentRow?: boolean;
/** 表格列配置 */
tableColumns?: TableColumn[];
/** 是否显示分页 */
showPagination?: boolean;
/** 总记录数 */
total?: number;
/** 当前页码 */
pageNo?: number;
/** 每页条数 */
pageSize?: number;
/** 是否所有数据都在客户端,如果是则在组件内部进行分页 */
isAllData?: boolean;
/** 分页左侧自定义文案 */
paginationLeftText?: string;
/** 分页组件自定义属性 */
paginationProps?: {
pageSizes?: number[];
pagerCount?: number;
layout?: string;
background?: boolean;
autoScroll?: boolean;
hidden?: boolean;
};
}

View File

@@ -0,0 +1,89 @@
import type { FormItemConfig } from './FormItem.d';
/**
* 表格列配置类型
*/
export interface TableColumn {
/** 列类型 */
type?: 'selection' | 'index' | 'expand';
/** 列字段名 */
prop?: string;
/** 列标签 */
label?: string;
/** 列宽度 */
width?: string | number;
/** 最小宽度 */
minWidth?: string | number;
/** 是否固定列 */
fixed?: boolean | 'left' | 'right';
/** 对齐方式 */
align?: 'left' | 'center' | 'right';
/** 是否显示溢出提示 */
showOverflowTooltip?: boolean;
/** 自定义插槽名称 */
slot?: string;
/** 格式化函数 */
formatter?: (row: Record<string, any>, column: any, cellValue: any, index?: number) => string;
/** 是否可选selection 类型) */
selectable?: (row: Record<string, any>, index: number) => boolean;
}
/**
* 树节点数据类型
*/
export interface TreeNodeData {
[key: string]: any;
children?: TreeNodeData[];
}
/**
* TableLayout 组件的 Props 类型
*/
export interface TableLayoutProps {
/** 表格数据 */
tableData: Record<string, any>[];
/** 加载状态 */
loading?: boolean;
/** 总记录数 */
total?: number;
/** 查询参数对象 */
queryParams: {
pageNo: number;
pageSize: number;
[key: string]: any;
};
/** 侧边查询参数 */
sideQueryParams?: Record<string, any>;
/** 表单项配置数组 */
formItems?: FormItemConfig[];
/** 是否显示顶部查询 */
showTopQuery?: boolean;
/** 是否显示侧边查询 */
showSideQuery?: boolean;
/** 是否显示分页 */
showPagination?: boolean;
/** 是否显示默认按钮 */
showDefaultButtons?: boolean;
/** 侧边栏宽度 */
sideWidth?: number;
/** 是否显示边框 */
border?: boolean;
/** 是否显示斑马纹 */
stripe?: boolean;
/** 表格尺寸 */
size?: 'large' | 'default' | 'small';
/** 表格高度 */
tableHeight?: string | number;
/** 最大高度 */
maxHeight?: string | number;
/** 行键 */
rowKey?: string | ((row: Record<string, any>) => string);
/** 是否高亮当前行 */
highlightCurrentRow?: boolean;
/** 侧边栏数据 */
siderData?: TreeNodeData[];
/** 树节点键名 */
treeNodeKey?: string;
/** 表格列配置 */
tableColumns?: TableColumn[];
}

View File

@@ -0,0 +1,13 @@
/**
* TableLayout 组件相关的类型定义
*/
export * from './FormItem.d';
export * from './QuickDateRange.d';
export * from './EditableTable.d';
export * from './Filter.d';
export * from './FormLayout.d';
export * from './Form.d';
export * from './Table.d';
export * from './TableLayout.d';
export * from './Dialog.d';

View File

@@ -0,0 +1,90 @@
const KEY_DELTA_MAP = {
ArrowLeft: -1,
ArrowUp: -1,
ArrowRight: 1,
ArrowDown: 1,
}
const FOCUSABLE_SELECTORS = [
'input:not([type="hidden"]):not([disabled])',
'textarea:not([disabled])',
'select:not([disabled])',
'.el-input__inner',
'.el-input-number',
'.el-select',
'.el-tree-select',
'[tabindex]:not([tabindex="-1"])',
]
function focusControl(container) {
if (!container) return
const focus = (el) => {
if (!el) return
el.focus?.()
if (el.select && !el.readOnly) {
el.select()
}
}
if (container.matches?.('input, textarea, select')) {
focus(container)
return
}
const directTarget = container.querySelector(FOCUSABLE_SELECTORS.join(', '))
if (directTarget) {
focusControl(directTarget)
return
}
focus(container)
}
function getFormItems(root) {
const propItems = Array.from(root.querySelectorAll('[data-prop]'))
if (propItems.length) {
return propItems
}
return Array.from(root.querySelectorAll('.el-form-item'))
}
function createHandler(root) {
return function handleKeyDown(event) {
const delta = KEY_DELTA_MAP[event.key]
if (!delta) return
const currentItem = event.target.closest('[data-prop], .el-form-item')
if (!currentItem || !root.contains(currentItem)) return
const items = getFormItems(root)
if (!items.length) return
const currentIndex = items.indexOf(currentItem)
if (currentIndex === -1) return
event.preventDefault()
event.stopPropagation()
const nextIndex = (currentIndex + delta + items.length) % items.length
const targetItem = items[nextIndex]
if (targetItem) {
focusControl(targetItem)
}
}
}
export default {
mounted(el) {
const handler = createHandler(el)
el.__arrowNavigateHandler = handler
el.addEventListener('keydown', handler, true)
},
beforeUnmount(el) {
if (el.__arrowNavigateHandler) {
el.removeEventListener('keydown', el.__arrowNavigateHandler, true)
delete el.__arrowNavigateHandler
}
},
}

View File

@@ -3,6 +3,7 @@ import hasPermi from './permission/hasPermi'
import copyText from './common/copyText'
import horizontalScroll from './common/horizontalScroll'
import clickOutsideRow from './common/clickOutsideRow'
import arrowNavigate from './common/arrowNavigate'
export default function directive(app){
app.directive('hasRole', hasRole)
@@ -10,4 +11,5 @@ export default function directive(app){
app.directive('copyText', copyText)
app.directive('horizontal-scroll', horizontalScroll)
app.directive('click-outside-row', clickOutsideRow)
app.directive('arrow-navigate', arrowNavigate)
}

View File

@@ -1,51 +1,70 @@
<template>
<div class="navbar">
<!-- <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> -->
<!-- <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" /> -->
<!-- <top-nav id="topmenu-container" class="topmenu-container" style="border: 1px solid blue;"/> -->
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" />
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="openhis-git" class="right-menu-item hover-effect" />
</el-tooltip> -->
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="openhis-doc" class="right-menu-item hover-effect" />
</el-tooltip> -->
<!-- <screenfull id="screenfull" class="right-menu-item hover-effect" /> -->
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="userStore.avatar" class="user-avatar" />
<span class="nick-name">{{ userStore.nickName }}</span>
<!-- <el-icon><caret-bottom /></el-icon> -->
</div>
<template #dropdown>
<el-dropdown-menu>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item command="switch">
<span>切换科室</span>
</el-dropdown-item>
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="avatar-wrapper">
<el-dropdown
@command="handleCommand"
class="user-info-dropdown hover-effect"
trigger="click"
teleported
popper-class="navbar-dropdown"
:popper-options="{
modifiers: [
{
name: 'offset',
options: {
offset: [0, 12],
},
},
],
}"
>
<div class="user-info">
<img :src="userStore.avatar" class="user-avatar" />
<span class="nick-name">{{ userStore.nickName }}</span>
</div>
<template #dropdown>
<el-dropdown-menu>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<!-- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item> -->
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<span class="divider">|</span>
<el-dropdown
@command="handleOrgSwitch"
trigger="click"
teleported
popper-class="navbar-dropdown"
:placement="'bottom-start'"
>
<span class="org-name">{{ userStore.orgName }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in orgOptions"
:key="item.orgId"
:command="item.orgId"
:class="{ 'is-active': item.orgId === userStore.orgId }"
>
{{ item.orgName }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<span class="divider" />
</div>
</div>
</div>
<el-dialog title="切换科室" v-model="showDialog" width="400px" append-to-body destroy-on-close>
@@ -68,6 +87,7 @@
</template>
<script setup>
import { onMounted } from 'vue';
import { ElMessageBox } from 'element-plus';
import Breadcrumb from '@/components/Breadcrumb';
import TopNav from '@/components/TopNav';
@@ -86,8 +106,39 @@ const settingsStore = useSettingsStore();
const orgOptions = ref([]);
const showDialog = ref(false);
const orgId = ref('');
function toggleSideBar() {
appStore.toggleSideBar();
function loadOrgList() {
getOrg().then((res) => {
orgOptions.value = res.data;
});
}
onMounted(() => {
loadOrgList();
});
function handleOrgSwitch(selectedOrgId) {
if (selectedOrgId === userStore.orgId) {
return;
}
const selectedOrg = orgOptions.value.find((item) => item.orgId === selectedOrgId);
const orgName = selectedOrg ? selectedOrg.orgName : '该科室';
ElMessageBox.confirm(`确定要切换到科室"${orgName}"吗?`, '切换科室', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
orgId.value = selectedOrgId;
switchOrg(selectedOrgId).then((res) => {
if (res.code === 200) {
userStore.logOut().then(() => {
location.href = '/index';
});
}
});
});
}
function handleCommand(command) {
@@ -98,13 +149,6 @@ function handleCommand(command) {
case 'logout':
logout();
break;
case 'switch':
orgId.value = userStore.orgId;
getOrg().then((res) => {
orgOptions.value = res.data;
showDialog.value = true;
});
break;
default:
break;
}
@@ -143,29 +187,35 @@ function setLayout() {
<style lang='scss' scoped>
.navbar {
height: 50px;
overflow: hidden;
overflow: visible;
position: relative;
background-color: transparent;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
flex-shrink: 0;
min-width: 200px;
z-index: 1002;
.right-menu {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
display: flex;
align-items: center;
padding: 0 6px;
height: 100%;
font-size: 16px;
color: #606266;
vertical-align: text-bottom;
flex-shrink: 0;
&.hover-effect {
cursor: pointer;
@@ -180,25 +230,70 @@ function setLayout() {
.avatar-container {
margin-right: 0;
margin-left: 5px;
flex-shrink: 0;
position: relative;
z-index: 1003;
display: flex;
align-items: center;
.avatar-wrapper {
display: flex;
align-items: center;
margin-top: 5px;
gap: 8px;
position: relative;
.user-info-dropdown {
display: flex;
align-items: center;
cursor: pointer;
}
.user-info {
display: flex;
align-items: center;
gap: 10px;
}
.user-avatar {
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 6px;
flex-shrink: 0;
}
.nick-name {
font-weight: 500;
color: #606266;
font-size: 14px;
width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100px;
flex-shrink: 0;
}
.divider {
color: #dcdfe6;
font-size: 14px;
margin: 0 8px;
flex-shrink: 0;
}
.org-name {
font-weight: 500;
color: #606266;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 120px;
flex-shrink: 0;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #409eff;
}
}
.el-icon {
cursor: pointer;
position: absolute;
@@ -210,5 +305,99 @@ function setLayout() {
}
}
}
@media (max-width: 768px) {
min-width: 150px;
padding-right: 5px;
.right-menu {
.avatar-container {
.avatar-wrapper {
gap: 4px;
.nick-name {
max-width: 60px;
font-size: 12px;
}
.org-name {
max-width: 80px;
font-size: 12px;
}
.divider {
margin: 0 4px;
font-size: 12px;
}
.user-avatar {
width: 24px;
height: 24px;
}
}
}
}
}
@media (max-width: 480px) {
min-width: 120px;
padding-right: 5px;
.right-menu {
.avatar-container {
.avatar-wrapper {
.nick-name {
display: none;
}
.divider {
display: none;
}
.org-name {
max-width: 80px;
font-size: 11px;
}
}
}
}
}
}
</style>
<style lang="scss">
.navbar-dropdown {
margin-bottom: 8px !important;
z-index: 10010 !important;
.el-dropdown-menu {
margin-bottom: 0 !important;
z-index: 10010 !important;
max-height: 300px !important;
overflow-y: auto !important;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
&:hover {
background: rgba(0, 0, 0, 0.3);
}
}
&::-webkit-scrollbar-track {
background: transparent;
}
.el-dropdown-menu__item.is-active {
color: #409eff !important;
font-weight: 500 !important;
background-color: #ecf5ff !important;
}
}
}
</style>

View File

@@ -105,6 +105,7 @@ function setLayout() {
left: 0;
z-index: 1001;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.35);
overflow: visible;
}
.sidebar-container {

View File

@@ -0,0 +1,449 @@
<template>
<div class="medical-form">
<div class="patient-name">
患者姓名{{ patient?.patientName || '未知' }} &nbsp;&nbsp; 病历号{{
patient?.busNo || '未知'
}}
</div>
<h2 style="text-align: center">长春市朝阳区中医院</h2>
<h2 style="text-align: center">出院诊断病历</h2>
<!-- 滚动内容区域 -->
<div class="form-scroll-container">
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="100px"
label-align="left"
class="medical-full-form"
>
<h4 class="section-title">基础信息</h4>
<!-- 1. 基础信息单行自适应排列 -->
<el-form-item class="form-section">
<div class="single-row-layout">
<el-form-item label="姓名" prop="patientName" class="row-item">
<div class="input-with-unit">
<el-input
disabled
v-model="formData.patientName"
type="text"
placeholder="请输入"
/>
</div>
</el-form-item>
<el-form-item label="年龄" prop="age" class="row-item">
<div class="input-with-unit">
<el-input disabled v-model="formData.age" type="text" placeholder="请输入" />
</div>
</el-form-item>
<el-form-item label="性别" prop="gender" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.gender" type="text" placeholder="请输入" />
</div>
</el-form-item>
<el-form-item label="住院号" prop="busNo" class="row-item">
<div class="input-with-unit">
<el-input disabled v-model="formData.busNo" type="text" placeholder="请输入" />
</div>
</el-form-item>
<el-form-item label="职业" prop="temperature" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.temperature" type="text" placeholder="请输入" />
</div>
</el-form-item>
<el-form-item label="入院日期" prop="admissionDate" class="row-item">
<el-date-picker
v-model="formData.admissionDate"
type="date"
placeholder="选择入院日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="出院日期" prop="dischargeDate" class="row-item">
<el-date-picker
v-model="formData.dischargeDate"
type="date"
placeholder="选择出院日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="住院天数" prop="hospitalDays" class="row-item">
<div class="input-with-unit">
<el-input disabled v-model="formData.hospitalDays" placeholder="请输入" />
</div>
</el-form-item>
</div>
</el-form-item>
<h4 class="section-title">诊断</h4>
<!-- 3. 出院诊断必填 -->
<el-form-item label="出院诊断" prop="DischargeDiagnosis" class="required form-item-single">
<el-input
v-model="formData.DischargeDiagnosis"
type="textarea"
placeholder="请输入出院诊断"
:autosize="{ minRows: 1 }"
style="resize: none; padding-right: 10px"
/>
</el-form-item>
<!-- 4. 出院病情摘要及诊疗经过 -->
<el-form-item
label="出院病情摘要及诊疗经过"
prop="SummaryAndDiagnosisAndTreatmentProcess"
class="form-item-single"
>
<el-input
v-model="formData.SummaryAndDiagnosisAndTreatmentProcess"
type="textarea"
placeholder="请输入出院病情摘要及诊疗经过"
:autosize="{ minRows: 1 }"
style="resize: none; padding-right: 10px"
/>
</el-form-item>
<el-form-item
label="出院后要求及注意事项"
prop="RequirementsAndPrecautionsAfterDischarge"
class="form-item-single"
>
<el-input
v-model="formData.RequirementsAndPrecautionsAfterDischarge"
type="textarea"
placeholder="请输入出院后要求及注意事项"
:autosize="{ minRows: 1 }"
style="resize: none; padding-right: 10px"
/>
</el-form-item>
<el-form-item
label="中医调护"
prop="TraditionalChineseMedicineNursing"
class="form-item-single"
>
<el-input
v-model="formData.TraditionalChineseMedicineNursing"
type="textarea"
placeholder="请输入中医调护"
:autosize="{ minRows: 1 }"
style="resize: none; padding-right: 10px"
/>
</el-form-item>
</el-form>
</div>
</div>
<DisDiagnMedicalRecord v-if="isShowprintDom" ref="recordPrintRef"></DisDiagnMedicalRecord>
</template>
<script setup>
import { reactive, ref, onMounted, watch, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { previewPrint } from '../utils/printUtils';
import DisDiagnMedicalRecord from '../views/hospitalRecord/components/disDiagnMedicalRecord.vue';
defineOptions({
name: 'DischargeDiagnosisCertificate',
});
// Props与事件
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
const recordPrintRef = ref();
const isShowprintDom = ref(false);
const patient = props.patientInfo;
// const props = defineProps({});
const emits = defineEmits(['submitOk']);
// 数据初始化
// const patient = ref(null);
// 表单数据
const formData = reactive({
patientName: '', // 姓名
age: '', // 年龄
gender: '', // 性别
busNo: '', // 住院号
admissionDate: '', // 入院日期
dischargeDate: '', // 出院日期
hospitalDays: '', // 住院天数
DischargeDiagnosis: '', // 出院诊断
SummaryAndDiagnosisAndTreatmentProcess: '', // 出院病情摘要及诊疗经过
RequirementsAndPrecautionsAfterDischarge: '', // 出院后要求及注意事项
TraditionalChineseMedicineNursing: '', // 中医调护
});
// 表单校验规则
const rules = reactive({
dischargeDiagnosis: [
{
required: true,
message: '请填写出院诊断',
trigger: ['blur', 'submit'],
},
],
});
// 提交函数
const submit = () => {
emits('submitOk', formData);
};
// 表单数据赋值
const setFormData = (data) => {
if (data) {
Object.assign(formData, data);
}
};
// 生命周期
onMounted(() => {
// patient.value = patientInfo.value;
// 组件挂载时自动填充患者信息
// fillPatientInfo(patientInfo.value);
if (!formData.patientName) {
formData.patientName = patient?.patientName || '';
}
if (!formData.gender) {
formData.gender = patient?.genderEnum_enumText || '';
}
if (!formData.age) {
formData.age = patient?.age || '';
}
if (!formData.department) {
formData.department = patient?.inHospitalOrgName || '';
}
if (!formData.bedNo) {
formData.bedNo = patient?.houseName + '-' + patient?.bedName;
}
if (!formData.busNo) {
formData.busNo = patient?.busNo || '';
}
if (!formData.admissionDate) {
formData.admissionDate = patient?.inHospitalTime || '';
}
if (!formData.hospitalDays) {
formData.hospitalDays = patient?.inHospitalDays;
}
console.log('patientInfo========>', JSON.stringify(props.patientInfo));
});
// 监听患者信息变化,实现联动显示和自动填充
// watch(
// () => patientInfo.value,
// (newPatientInfo) => {
// patient.value = newPatientInfo;
// // 患者信息变化时自动填充
// fillPatientInfo(newPatientInfo);
// },
// { deep: true }
// );
// 自动填充患者信息到表单
const fillPatientInfo = (patientData) => {
if (!patientData) {
ElMessage.warning('未获取到患者信息,请确保已选择患者');
return;
}
try {
// 填充基本信息,处理可能的数据缺失情况
formData.patientName = patientData.patientName || '';
formData.age = patientData.age || '';
formData.gender = patientData.genderEnum_enumText || '';
formData.busNo = patientData.busNo || '';
formData.hospitalDays = patientData.inHospitalDays || '';
} catch (error) {
console.error('填充患者信息时发生错误:', error);
ElMessage.error('自动填充患者信息失败,请检查数据源格式');
}
};
// 打印方法
const printFun = () => {
isShowprintDom.value = true;
nextTick(() => {
recordPrintRef?.value.setData(formData);
nextTick(() => {
previewPrint(recordPrintRef?.value.getDom());
isShowprintDom.value = false;
});
});
};
// 暴露接口
defineExpose({ formData, submit, setFormData, fillPatientInfo, printFun });
</script>
<style scoped>
/* 表单外层容器 */
.medical-form {
max-width: 1200px;
width: 100%;
min-height: 800px;
height: auto;
margin: 15px auto;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
font-family: Arial, sans-serif;
box-sizing: border-box;
overflow: hidden; /* 防止内部内容溢出 */
position: relative;
}
/* 顶部姓名样式 */
.patient-name {
display: inline-block;
margin-bottom: 15px;
font-size: 14px;
color: #333;
font-weight: 500;
}
/* 滚动内容容器 */
.form-scroll-container {
width: 100%;
max-height: 55vh;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: #ccc #f5f5f5;
position: relative;
}
.form-scroll-container::-webkit-scrollbar {
width: 6px;
}
.form-scroll-container::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
.form-scroll-container::-webkit-scrollbar-track {
background-color: #f5f5f5;
}
/* 完整表单容器 */
.medical-full-form {
width: 100%;
min-width: 0; /* 防止内容强制拉伸容器 */
box-sizing: border-box;
}
/* 区域通用样式 */
.form-section {
margin-bottom: 20px;
}
.section-title {
margin: 0 0 12px;
padding-bottom: 6px;
border-bottom: 1px solid #f0f0f0;
color: #333;
font-size: 16px;
font-weight: bold;
}
/* 通用单行自适应布局(基础信息+病史信息共用) */
.single-row-layout {
display: flex;
flex-wrap: wrap; /* 自动换行 */
align-items: flex-start; /* 顶部对齐,适配文本域高度 */
gap: 15px; /* 统一元素间距 */
width: 100%;
box-sizing: border-box;
}
.row-item {
margin-bottom: 0; /* 取消底部间距,避免换行重叠 */
display: flex;
flex-direction: column;
}
/* 基础信息项:适配短输入框 */
.row-item:not(.history-item) {
min-width: 160px; /* 基础信息项最小宽度 */
}
/* 带单位的输入框样式 */
.input-with-unit {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.input-with-unit .el-input {
flex: 1;
}
.unit {
font-weight: 500;
color: #333;
white-space: nowrap;
font-size: 14px;
}
/* 单行表单项样式(主诉、查体等) */
.form-item-single {
margin: 18px 0;
}
/* 必填项红色星号 */
.required .el-form-item__label::before {
content: '* ';
color: #ff4d4f;
}
/* 输入框统一样式 */
.el-form-item .el-input,
.el-form-item .el-input__wrapper {
width: 100%;
box-sizing: border-box;
}
.el-form-item .el-input__inner {
font-size: 14px;
padding: 8px 12px;
}
/* 确保textarea的rows属性生效 */
.el-textarea__inner {
min-height: auto !important;
height: auto !important;
resize: none;
}
/* 根据rows属性设置高度 */
.el-input--textarea {
height: auto;
}
/* 响应式调整 */
@media (max-width: 768px) {
.medical-form {
height: 80vh;
padding: 10px;
overflow: hidden;
}
.form-scroll-container {
height: calc(100% - 35px);
max-height: none;
}
.el-form {
label-width: 70px !important;
}
.row-item:not(.history-item) {
min-width: 130px;
}
.history-item {
min-width: 100%; /* 移动端病史信息全屏宽度,单行显示 */
}
.form-item-single,
.form-section {
margin-bottom: 15px;
}
}
</style>

View File

@@ -0,0 +1,393 @@
<script lang="ts" setup>
import { ref } from 'vue';
// 1. 基础信息(复用已有变量,补充一致性格式)
const patientInfo = ref({
name: '',
department: '',
bed: '',
inpatientNo: '',
});
defineOptions({
name: 'ProgressNoteform',
});
// 2. 首次病程记录(复用已有变量,补充文本格式)
const firstRecordTime = ref('');
const firstRecordIntro = ref(
''
);
const caseFeatures =
ref('');
const chinaDiscussion = ref('');
const westDiscussion = ref('');
const preliminaryDiagnosis = ref('');
const treatmentPlan = ref(''); // 添加缺失的变量
// 3. 后续查房/会诊记录新增还原PDF所有章节
const roundRecords = ref([
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '主治医师签名:' }, // 区分普通医师和主治医师签名
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '副主任医师签名:' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
{
title: '',
time: '',
content: '',
signature: { doctor: '', physician: '' },
},
]);
// 4. 签名变量(支持所有记录的签名输入)
interface Signatures {
firstDoctor: string;
[key: string]: string;
}
const signatures = ref<Signatures>({
firstDoctor: '', // 首次病程记录医师签名
...roundRecords.value.reduce((acc, record, index) => {
acc[`round${index}Doctor`] = '';
acc[`round${index}Physician`] = '';
return acc;
}, {} as Record<string, string>),
});
// 5. 打印功能:控制打印范围+样式
const handlePrint = () => {
// 1. 触发浏览器打印
window.print();
};
// 暴露接口
defineExpose({ patientInfo, firstRecordTime, firstRecordIntro, caseFeatures, chinaDiscussion, westDiscussion, preliminaryDiagnosis, treatmentPlan, roundRecords, signatures });
</script>
<template>
<div class="medical-record-container">
<!-- 打印按钮固定在顶部非打印内容 -->
<div class="print-btn-container no-print">
<el-button type="primary" @click="handlePrint">打印病历</el-button>
</div>
<!-- 病历主体打印核心内容 -->
<div class="medical-record">
<!-- 1. 医院头部每一页PDF均包含复用已有样式 -->
<div class="hospital-header">
<img src="./imgs/logo.png" alt="长春市朝阳区中医院Logo" class="header-logo" />
<h1 class="hospital-name">长春市朝阳区中医院</h1>
</div>
<!-- 2. 患者信息栏每一页PDF均包含下划线样式 -->
<div class="patient-info">
<span class="info-item">姓名:{{ patientInfo.name }}</span>
<span class="info-item">科室:{{ patientInfo.department }}</span>
<span class="info-item">床号:{{ patientInfo.bed }}</span>
<span class="info-item">住院号:{{ patientInfo.inpatientNo }}</span>
</div>
<!-- 3. 首次病程记录 -->
<div class="record-section">
<h2 class="section-main-title"> </h2>
<div class="record-time">{{ firstRecordTime }}</div>
<el-input v-model="firstRecordIntro" autosize type="textarea" class="clean-textarea" />
<!-- 病例特点 -->
<h3 class="section-sub-title">病例特点</h3>
<el-input v-model="caseFeatures" autosize type="textarea" class="clean-textarea" />
<!-- 拟诊讨论 -->
<h3 class="section-sub-title">拟诊讨论</h3>
<el-input v-model="chinaDiscussion" autosize type="textarea" class="clean-textarea" />
<el-input v-model="westDiscussion" autosize type="textarea" class="clean-textarea" />
<!-- 初步诊断 -->
<el-input v-model="preliminaryDiagnosis" autosize type="textarea" class="clean-textarea" />
<!-- 诊疗计划 -->
<el-input v-model="treatmentPlan" autosize type="textarea" class="clean-textarea" />
<!-- 首次病程记录签名 -->
<div class="signature-group">
<span class="signature-label">医师签名:</span>
<el-input v-model="signatures.firstDoctor" autosize type="textarea" class="clean-textarea signature-input"
:rows="1" />
</div>
</div>
<!-- 4. 分页分隔线模拟PDF分页打印时自动分页 -->
<div class="page-break"></div>
<!-- 5. 后续查房/会诊记录按时间顺序 -->
<div v-for="(record, index) in roundRecords" :key="index" class="record-section">
<!-- 重复患者信息与PDF一致 -->
<div class="patient-info page-repeated-info">
<span class="info-item">姓名:{{ patientInfo.name }}</span>
<span class="info-item">科室:{{ patientInfo.department }}</span>
<span class="info-item">床号:{{ patientInfo.bed }}</span>
<span class="info-item">住院号:{{ patientInfo.inpatientNo }}</span>
</div>
<!-- 查房标题+时间 -->
<h2 class="section-main-title">{{ record.title }}</h2>
<div class="record-time">{{ record.time }}</div>
<!-- 查房内容 -->
<el-input v-model="record.content" autosize type="textarea" class="clean-textarea" />
<!-- 查房签名区分普通医师/上级医师 -->
<div class="signature-group">
<span class="signature-label">医师签名:</span>
<el-input v-model="signatures[`round${index}Doctor`]" autosize type="textarea"
class="clean-textarea signature-input" :rows="1" />
<span v-if="record.signature.physician" class="signature-label ml-20">
{{ record.signature.physician }}
</span>
<el-input v-if="record.signature.physician" v-model="signatures[`round${index}Physician`]" autosize
type="textarea" class="clean-textarea signature-input" :rows="1" />
</div>
<!-- 分页分隔线最后一条记录无需分页 -->
<div v-if="index !== roundRecords.length - 1" class="page-break"></div>
</div>
</div>
</div>
</template>
<style scoped>
/* 1. 容器基础样式 */
.medical-record-container {
padding: 20px;
background-color: #f9f9f9;
}
.print-btn-container {
margin-bottom: 20px;
text-align: right;
}
/* 2. 病历主体样式模拟A4纸 */
.medical-record {
max-width: 210mm;
/* A4宽度 */
min-height: 297mm;
/* A4高度 */
margin: 0 auto;
padding: 20mm;
/* A4页边距 */
background-color: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
font-family: 'SimSun', '宋体', serif;
/* 病历标准字体 */
}
/* 3. 医院头部样式 */
.hospital-header {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.header-logo {
width: 60px;
height: 60px;
margin-right: 15px;
}
.hospital-name {
font-size: 24px;
font-weight: bold;
color: #000;
margin: 0;
}
/* 4. 患者信息样式 */
.patient-info {
border-bottom: 1px solid #000;
padding: 5px 0;
margin-bottom: 15px;
font-size: 16px;
line-height: 1.5;
}
.info-item {
margin-right: 30px;
white-space: nowrap;
}
/* 5. 记录章节样式 */
.record-section {
margin-bottom: 30px;
}
.section-main-title {
text-align: center;
font-size: 22px;
font-weight: bold;
margin: 15px 0;
}
.section-sub-title {
font-size: 18px;
font-weight: bold;
margin: 10px 0;
}
.record-time {
font-size: 14px;
margin-bottom: 15px;
color: #666;
}
/* 6. 签名区域样式 */
.signature-group {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 20px;
gap: 10px;
flex-wrap: wrap;
width: 100%;
}
.signature-label {
font-size: 16px;
font-weight: bold;
white-space: nowrap;
}
.signature-input {
width: 200px;
flex-shrink: 0;
}
.ml-20 {
margin-left: 20px;
}
/* 7. 分页分隔线模拟PDF分页 */
.page-break {
height: 1px;
background-color: #eee;
margin: 30px 0;
page-break-after: always;
/* 打印时强制分页 */
}
/* 8. 重复信息样式(后续页面的患者信息) */
.page-repeated-info {
margin-top: 20px;
}
/* 9. 清洁输入框样式(复用已有,确保无边框) */
:deep(.clean-textarea .el-textarea__wrapper) {
background-color: transparent;
padding: 0;
border: none;
}
:deep(.clean-textarea .el-textarea__inner) {
border: none;
background-color: transparent;
padding: 0;
resize: none;
word-break: break-word;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: inherit;
font-size: 16px;
line-height: 1.8;
/* 病历标准行高 */
color: #000;
}
:deep(.clean-textarea .el-textarea__inner:focus) {
outline: none;
box-shadow: none;
}
/* 10. 打印专属样式:控制打印效果 */
@media print {
/* 隐藏非打印内容(如打印按钮) */
.no-print {
display: none !important;
}
/* 强制A4尺寸+无边距 */
@page {
size: A4;
margin: 15mm;
/* 打印页边距匹配PDF */
}
/* 确保背景色打印(部分浏览器默认不打印背景) */
body {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
background-color: #fff;
}
/* 病历主体无边框阴影,仅打印内容 */
.medical-record {
box-shadow: none;
padding: 0;
margin: 0;
}
/* 文本不换行优化 */
.info-item {
margin-right: 20px;
}
/* 确保输入框内容正常打印 */
:deep(.el-textarea__inner) {
border: none !important;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,289 @@
<template>
<div class="assessment-page">
<div class="page-container">
<!-- 医院头部 -->
<div class="hospital-header">
<h1 class="hospital-name">
<span class="hospital-text">长春市朝阳区中医院</span>
</h1>
</div>
<!-- 页面标题 -->
<h2 class="form-title">住院病人风险评估表</h2>
<!-- 表单卡片 -->
<el-form :model="formData" label-width="100px">
<el-row>
<el-col :span="8">
<el-form-item label="科室" label-position="top">
<el-input v-model="formData.department" readonly="true"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="床号" label-position="top" class="comment-padding">
<el-input v-model="formData.bedNo" readonly="true"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="住院号" label-position="top" class="comment-padding">
<el-input v-model="formData.busNo" readonly="true"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="姓名" label-position="top">
<el-input
v-model="formData.patientName"
readonly="true"
class="auto-resize-input"
></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="性别" label-position="top" class="comment-padding">
<el-input
v-model="formData.gender"
readonly="true"
class="auto-resize-input"
></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="年龄" label-position="top" class="comment-padding">
<el-input v-model="formData.age" readonly="true" class="auto-resize-input"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="病情简介" label-position="top">
<el-input
type="textarea"
v-model="formData.adm_cond"
:autosize="{ minRows: 1, maxRows: 100 }"
class="full-width-textarea"
></el-input>
</el-form-item>
<el-form-item label="可能发生的不良后果及预后" label-position="top">
<el-input
type="textarea"
v-model="formData.effectless"
:autosize="{ minRows: 1, maxRows: 100 }"
class="full-width-textarea"
></el-input>
</el-form-item>
<el-form-item label="评估等级" label-position="top">
<el-radio-group v-model="formData.evalLevel">
<el-radio label="一般">一般</el-radio>
<el-radio label="病重">病重</el-radio>
<el-radio label="病危">病危</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="护理等级" label-position="top">
<el-radio-group v-model="formData.nurseLevel">
<el-radio label="特级护理">特级护理</el-radio>
<el-radio label="一级护理">一级护理</el-radio>
<el-radio label="二级护理">二级护理</el-radio>
<el-radio label="三级护理">三级护理</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="收集资料时间:">
<span class="date-display">{{ currentDate }}</span>
</el-form-item>
<el-row>
<el-col :span="8">
<el-form-item label="评估医师签名:">
<el-input
disabled
v-model="formData.sign_doc"
:autosize="{ minRows: 1 }"
class="auto-resize-input"
></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="主治医师签名:" class="comment-padding">
<el-input
v-model="formData.sign_maindoc"
:autosize="{ minRows: 1 }"
class="auto-resize-input"
></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="科主任签名:" class="comment-padding">
<el-input
v-model="formData.sign_leader"
:autosize="{ minRows: 1 }"
class="auto-resize-input"
></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
<inAssessmentForm v-if="isShowprintDom" ref="recordPrintRef"></inAssessmentForm>
</template>
<script setup>
import { computed, onMounted, nextTick, reactive, ref } from 'vue';
import inAssessmentForm from '../views/hospitalRecord/components/inAssessmentForm.vue';
import useUserStore from '@/store/modules/user';
import { previewPrint } from '../utils/printUtils';
defineOptions({
name: 'InHospitalCaseForm',
});
const isShowprintDom = ref(false);
const recordPrintRef = ref();
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
const patient = props.patientInfo;
const userStore = useUserStore();
// 表单数据 - 修复:将 formData 定义移到 patient 之后
const formData = reactive({
department: patient?.inHospitalOrgName || '',
bedNo: patient?.bedName || '',
busNo: patient?.busNo || '',
patientName: patient?.patientName || '',
gender: patient?.genderEnum_enumText || '',
age: patient?.age || '',
adm_cond: '',
effectless: '',
evalLevel: '',
nurseLevel: '',
sign_doc: userStore.nickName || '',
sign_maindoc: '',
sign_leader: '',
});
// 当前日期YYYY-MM-DD
const currentDate = computed(() => {
const d = new Date();
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
});
//表单引用
const formRef = ref(null);
//提交表单
const submit = () => {
// 如果需要表单验证,可以使用以下代码
// formRef.value.validate((valid) => {
// if (valid) {
// emits('submitOk', formData);
// }
// });
// 简化版本:
emits('submitOk', formData);
};
//表单数据赋值
const setFormData = (data) => {
if (data) {
Object.assign(formData, data);
}
};
// 定义 emits
const emits = defineEmits(['submitOk']);
onMounted(() => {
// 页面加载完成后触发一次 resize 事件,确保输入框高度正确
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 100);
console.log('@@@@@=======>', JSON.stringify(props.patientInfo));
});
// 打印方法
const printFun = () => {
console.log('入院记录打印');
isShowprintDom.value = true;
nextTick(() => {
recordPrintRef?.value.setData(formData);
nextTick(() => {
previewPrint(recordPrintRef?.value.getDom());
isShowprintDom.value = false;
});
});
};
//暴露接口
defineExpose({ formData, submit, setFormData, printFun });
</script>
<style scoped>
/* ===== 页面容器与背景 ===== */
.comment-padding {
padding-left: 10px;
}
.assessment-page {
font-family: 'Microsoft YaHei', 宋体, sans-serif;
width: 100%;
}
.page-container {
width: 100%;
}
/* ===== 医院头部 ===== */
.hospital-header {
display: flex;
align-items: center;
justify-content: center;
margin-top: 40px;
}
.hospital-name {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
margin: 0;
}
.hospital-text {
line-height: 1;
}
/* ===== 表单标题与操作 ===== */
.form-title {
margin-top: 10px;
font-size: 1.25rem;
font-weight: 600;
text-align: center;
margin: 1.25rem 0;
color: #1f2937;
}
/* ===== 表单卡片 ===== */
.form-card {
border: 1px solid #e5e7eb;
border-radius: 4px;
}
/* ===== Textarea 自动扩展样式 ===== */
.full-width-textarea {
width: 100%;
}
:deep(.full-width-textarea textarea) {
overflow: hidden;
resize: none;
min-height: auto;
}
/* ===== 日期显示 ===== */
.date-display {
font-size: 0.95rem;
color: #666;
}
</style>

View File

@@ -6,7 +6,7 @@
<template>
<div class="container">
<div class="header">
<h2 class="title">乾安县人民医院</h2>
<h2 class="title">长春市朝阳区中医院</h2>
<h3 class="subtitle">患者护理记录单</h3>
</div>

View File

@@ -0,0 +1,455 @@
<template>
<div class="medical-form">
<div class="patient-name">
患者姓名{{ patient?.patientName || '未知' }} &nbsp;&nbsp; 病历号{{
patient?.busNo || '未知'
}}
</div>
<h2 style="text-align: center">{{ userStore.hospitalName || '长春市朝阳区中医院' }}</h2>
<h2 style="text-align: center">门诊病历</h2>
<!-- 滚动内容区域 -->
<div class="form-scroll-container">
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="100px"
label-align="left"
class="medical-full-form"
>
<h4 class="section-title">基础信息</h4>
<!-- 1. 基础信息单行自适应排列 -->
<el-form-item class="form-section">
<div class="single-row-layout">
<el-form-item label="呼吸" prop="breathe" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.breathe" type="text" placeholder="请输入" />
<span class="unit">/</span>
</div>
</el-form-item>
<!-- <el-form-item label="血压" prop="blood" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.blood" type="text" placeholder="请输入" />
<span class="unit">mmHg</span>
</div>
</el-form-item> -->
<el-form-item label="血压" prop="blood" class="row-item">
<div class="input-with-unit blood-input-group">
<el-input
v-model="formData.bloodHigh"
type="text"
placeholder="高压"
style="width: 80px"
/>
<span class="divider">/</span>
<el-input
v-model="formData.bloodLow"
type="text"
placeholder="低压"
style="width: 80px"
/>
<span class="unit">(/)mmHg</span>
</div>
</el-form-item>
<el-form-item label="体温" prop="temperature" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.temperature" type="text" placeholder="请输入" />
<span class="unit"></span>
</div>
</el-form-item>
<el-form-item label="脉搏" prop="pulse" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.pulse" type="text" placeholder="请输入" />
<span class="unit">/</span>
</div>
</el-form-item>
<el-form-item label="就诊日期" prop="onsetDate" class="row-item">
<el-date-picker
v-model="formData.onsetDate"
type="date"
placeholder="选择就诊日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
<!-- <el-input v-model="formData.onsetDate" type="date" /> -->
</el-form-item>
</div>
</el-form-item>
<h4 class="section-title">病史信息</h4>
<!-- 2. 病史信息单行自适应排列新增调整 -->
<el-form-item class="form-section">
<div class="single-row-layout">
<el-form-item label="现病史" prop="presentIllness" class="row-item history-item">
<el-input
v-model="formData.presentIllness"
type="textarea"
placeholder="无"
autosize
/>
</el-form-item>
<el-form-item label="既往史" prop="pastIllness" class="row-item history-item">
<el-input v-model="formData.pastIllness" type="textarea" placeholder="无" autosize />
</el-form-item>
<el-form-item label="个人史" prop="personalHistory" class="row-item history-item">
<el-input
v-model="formData.personalHistory"
type="textarea"
placeholder="无"
autosize
/>
</el-form-item>
<el-form-item label="过敏史" prop="allergyHistory" class="row-item history-item">
<el-input
v-model="formData.allergyHistory"
type="textarea"
placeholder="无"
autosize
/>
</el-form-item>
<el-form-item label="家族史" prop="familyHistory" class="row-item history-item">
<el-input
v-model="formData.familyHistory"
type="textarea"
placeholder="无"
autosize
/>
</el-form-item>
</div>
</el-form-item>
<h4 class="section-title">主诉处置辅助检查</h4>
<!-- 3. 主诉必填 -->
<el-form-item label="主诉" prop="complaint" class="required form-item-single">
<el-input
v-model="formData.complaint"
type="textarea"
placeholder="请输入主诉"
class="tall-textarea"
autosize
/>
</el-form-item>
<!-- 4. 查体处理辅助检查 -->
<!-- <el-form-item label="查体(治疗)" prop="physicalExam" class="form-item-single">
<el-input
v-model="formData.physicalExam"
type="textarea"
placeholder="请输入查体结果"
class="tall-textarea"
autosize
/>
</el-form-item> -->
<el-form-item label="处置" prop="treatment" class="form-item-single">
<el-input
v-model="formData.treatment"
type="textarea"
placeholder="请输入处理方案"
class="tall-textarea"
autosize
/>
</el-form-item>
<el-form-item label="辅助检查" prop="auxiliaryExam" class="form-item-single">
<el-input
v-model="formData.auxiliaryExam"
type="textarea"
placeholder="请输入辅助检查结果"
class="tall-textarea"
autosize
/>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onBeforeMount, onMounted, watch } from 'vue';
import useUserStore from '../store/modules/user';
import { ElInput, ElMessage, ElForm, ElFormItem } from 'element-plus';
import { patientInfo } from '../views/doctorstation/components/store/patient';
import { pa } from 'element-plus/es/locales.mjs';
defineOptions({
name: 'OutpatientMedicalRecord1.1',
components: { ElInput, ElMessage, ElForm, ElFormItem },
});
// // Props与事件,去掉props.patientInfo改为直接从store获取
// const props = defineProps({
// patientInfo: {
// type: Object,
// required: true,
// },
// });
const props = defineProps({});
const emits = defineEmits(['submitOk']);
// 数据初始化
const userStore = useUserStore();
const patient = ref(null);
const formRef = ref(null);
// 表单数据(全部字符类型)
const formData = reactive({
breathe: '', // 呼吸
bloodHigh: '', //高压
bloodLow: '', //低压
temperature: '', // 体温
pulse: '', // 脉搏
onsetDate: '', // 就诊日期
complaint: '', // 主诉(必填)
presentIllness: '', // 现病史
pastIllness: '', // 既往史
personalHistory: '', // 个人史
allergyHistory: '', // 过敏史
physicalExam: '', // 查体
treatment: '', // 处理
auxiliaryExam: '', // 辅助检查
familyHistory: '', // 家族史
});
// 表单校验规则
const rules = reactive({
complaint: [
{
required: true,
message: '请填写主诉',
trigger: ['blur', 'submit'],
},
],
});
// 提交函数
const submit = () => {
formRef.value.validate((isValid) => {
if (isValid) {
emits('submitOk', formData);
}
});
};
// 日期格式化工具
const formatDateTime = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
};
// 表单数据赋值
const setFormData = (data) => {
if (data) {
Object.assign(formData, data);
}
};
// 生命周期
onBeforeMount(() => {});
onMounted(() => {
console.log('当前患者信息:', patientInfo);
patient.value = patientInfo.value;
// 初始化发病日期为当前时间
if (!formData.onsetDate) {
formData.onsetDate = formatDateTime(new Date());
}
});
// 监听患者信息变化,实现联动显示
watch(
() => patientInfo.value,
(newPatientInfo) => {
patient.value = newPatientInfo;
},
{ deep: true }
);
// 暴露接口
defineExpose({ formData, submit, setFormData });
</script>
<style scoped>
/* 表单外层容器 */
.medical-form {
max-width: 1200px;
width: 100%;
min-height: 800px;
height: 900px;
margin: 15px auto;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
font-family: Arial, sans-serif;
box-sizing: border-box;
overflow: visible;
}
/* 顶部姓名样式 */
.patient-name {
display: inline-block;
margin-bottom: 15px;
font-size: 14px;
color: #333;
font-weight: 500;
}
/* 滚动内容容器 */
.form-scroll-container {
width: 100%;
max-height: 80vh;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: #ccc #f5f5f5;
}
.form-scroll-container::-webkit-scrollbar {
width: 6px;
}
.form-scroll-container::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
.form-scroll-container::-webkit-scrollbar-track {
background-color: #f5f5f5;
}
/* 完整表单容器 */
.medical-full-form {
width: 100%;
}
/* 区域通用样式 */
.form-section {
margin-bottom: 20px;
}
.section-title {
margin: 0 0 12px;
padding-bottom: 6px;
border-bottom: 1px solid #f0f0f0;
color: #333;
font-size: 16px;
font-weight: bold;
}
/* 通用单行自适应布局(基础信息+病史信息共用) */
.single-row-layout {
display: flex;
flex-wrap: wrap; /* 自动换行 */
align-items: flex-start; /* 顶部对齐,适配文本域高度 */
gap: 15px; /* 统一元素间距 */
}
.row-item {
margin-bottom: 0; /* 取消底部间距,避免换行重叠 */
display: flex;
flex-direction: column;
}
.blood-input-group {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.blood-input-group .el-input {
flex: none;
}
.divider {
font-weight: bold;
color: #333;
font-size: 14px;
}
.unit {
font-weight: 500;
color: #333;
white-space: nowrap;
font-size: 14px;
}
/* 基础信息项:适配短输入框 */
.row-item:not(.history-item) {
min-width: 160px; /* 基础信息项最小宽度 */
}
/* 病史信息项:适配文本域,设置更大最小宽度 */
.history-item {
min-width: 220px; /* 确保文本域有足够宽度 */
}
/* 带单位的输入框样式 */
.input-with-unit {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.input-with-unit .el-input {
flex: 1;
}
.unit {
font-weight: 500;
color: #333;
white-space: nowrap;
font-size: 14px;
}
/* 单行表单项样式(主诉、查体等) */
.form-item-single {
margin-bottom: 18px;
}
/* 文本域高度控制 */
.tall-textarea {
--el-input-textarea-min-height: 100px;
}
/* 病史信息文本域:适当降低高度,适配单行布局 */
.history-item .el-input__inner {
--el-input-textarea-min-height: 60px;
}
/* 必填项红色星号 */
.required .el-form-item__label::before {
content: '* ';
color: #ff4d4f;
}
/* 输入框统一样式 */
.el-form-item .el-input,
.el-form-item .el-input__wrapper {
width: 100%;
box-sizing: border-box;
}
.el-form-item .el-input__inner {
font-size: 14px;
padding: 8px 12px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.medical-form {
height: 80vh;
padding: 10px;
}
.form-scroll-container {
height: calc(100% - 35px);
}
.el-form {
label-width: 70px !important;
}
.row-item:not(.history-item) {
min-width: 130px;
}
.history-item {
min-width: 100%; /* 移动端病史信息全屏宽度,单行显示 */
}
.form-item-single,
.form-section {
margin-bottom: 15px;
}
.tall-textarea {
--el-input-textarea-min-height: 80px;
}
}
</style>

View File

@@ -7,7 +7,7 @@
<div class="surgicalPatientHandover-container">
<div class="handover-form">
<div class="form-header">
<h1 class="hospital-name">**医院</h1>
<h1 class="hospital-name">长春市朝阳区中医院</h1>
<h2 class="form-title">手术患者交接单</h2>
</div>

View File

@@ -1,10 +1,10 @@
/**
* 判断url是否是http或https
* 判断url是否是http或https
* @param {string} path
* @returns {Boolean}
*/
export function isHttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
export function isHttp(url) {
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1;
}
/**
@@ -12,8 +12,8 @@
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path);
}
/**
@@ -21,8 +21,8 @@
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
const valid_map = ['admin', 'editor'];
return valid_map.indexOf(str.trim()) >= 0;
}
/**
@@ -30,8 +30,9 @@ export function validUsername(str) {
* @returns {Boolean}
*/
export function validURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
const reg =
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
return reg.test(url);
}
/**
@@ -39,8 +40,8 @@ export function validURL(url) {
* @returns {Boolean}
*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/
return reg.test(str)
const reg = /^[a-z]+$/;
return reg.test(str);
}
/**
@@ -48,8 +49,8 @@ export function validLowerCase(str) {
* @returns {Boolean}
*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
const reg = /^[A-Z]+$/;
return reg.test(str);
}
/**
@@ -57,8 +58,8 @@ export function validUpperCase(str) {
* @returns {Boolean}
*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
const reg = /^[A-Za-z]+$/;
return reg.test(str);
}
/**
@@ -66,8 +67,9 @@ export function validAlphabets(str) {
* @returns {Boolean}
*/
export function validEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
const reg =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return reg.test(email);
}
/**
@@ -76,9 +78,9 @@ export function validEmail(email) {
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
return true;
}
return false
return false;
}
/**
@@ -87,7 +89,39 @@ export function isString(str) {
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
return Object.prototype.toString.call(arg) === '[object Array]';
}
return Array.isArray(arg)
return Array.isArray(arg);
}
// 手机号正则
export function isValidCNPhoneNumber(phone) {
const regex = /^1[3-9]\d{9}$/;
return regex.test(phone);
}
// 身份证号正则
export function isValidCNidCardNumber(idCard) {
const regex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return regex.test(idCard);
}
// 根据身份证号获取性别和年龄
export function getGenderAndAge(idCard) {
// 确保身份证号码是18位
if (idCard.length !== 18) {
return { error: '身份证号码必须是18位' };
}
// 提取出生年月日
const birthDate = idCard.substr(6, 8); // YYYYMMDD
const year = birthDate.substr(0, 4);
const month = birthDate.substr(4, 2);
const day = birthDate.substr(6, 2);
const dateOfBirth = new Date(`${year}-${month}-${day}`);
// 计算年龄
const age = new Date().getFullYear() - dateOfBirth.getFullYear();
const m = new Date().getMonth() - dateOfBirth.getMonth();
if (m < 0 || (m === 0 && new Date().getDate() < dateOfBirth.getDate())) {
age--;
}
// 提取性别
const gender = idCard.charAt(16) % 2 === 0 ? 1 : 0;
return { age, gender };
}

View File

@@ -79,6 +79,9 @@
</el-form-item>
<el-form-item label="属性代码" prop="code">
<el-input v-model="form.code" placeholder="请输入属性代码" />
</el-form-item>
<el-form-item label="体温单类型编码" prop="typeCode">
<el-input v-model="form.typeCode" placeholder="请输入体温单属性编码" />
</el-form-item>
<el-form-item label="岗位顺序" prop="displayOrder">
<el-input-number v-model="form.displayOrder" controls-position="right" :min="0" />
@@ -197,6 +200,7 @@ function reset() {
displayOrder: 0, // 显示顺序
dictName: '', //字典名称
dictType: '', //字典类型
typeCode:'',//体温单类型编码
};
proxy.resetForm('statisticsRef');
}

View File

@@ -32,7 +32,7 @@
<PopoverList @search="handleSearch" :width="800" :modelValue="scope.row.name">
<template #popover-content="{}">
<DeviceList
v-if="scope.row.type == '2' || props.tab == 2 "
v-if="scope.row.type == '2' || props.tab == 2"
@selectRow="(row) => selectRow(row, scope.$index)"
:searchKey="searchKey"
/>
@@ -171,7 +171,7 @@
</el-form>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
import { bind, deleteBind, init } from './api';
@@ -345,6 +345,5 @@ function selectRow(row, index) {
}
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -79,8 +79,12 @@ import { getActivityList, getBindList, getRegistrationfeeList } from './componen
import ConsumablesList from './components/consumablesList.vue';
const activityList = ref([]);
const queryParams = ref({});
const queryParamsRegistration = ref({});
const queryParams = ref({
statusEnum: 2,
});
const queryParamsRegistration = ref({
activeFlag: 1,
});
const bindList = ref([]);
const bindInfo = ref({});
const activeTab = ref(1);
@@ -91,19 +95,18 @@ const { proxy } = getCurrentInstance();
const { method_code } = proxy.useDict('method_code');
getList();
getRegistrationList()
getRegistrationList();
function getList() {
// queryParams.value.typeEnum = activeTab.value;
getActivityList(queryParams.value).then((res) => {
activityList.value = res.data.records;
});
}
function getRegistrationList() {
getRegistrationfeeList(queryParamsRegistration.value).then(res => {
getRegistrationfeeList(queryParamsRegistration.value).then((res) => {
RegistrationfeeList.value = res.data.records;
})
});
}
// 点击诊疗列表 获取绑定的耗材
@@ -119,5 +122,4 @@ function clickRow(row) {
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -5,7 +5,7 @@
<span style="vertical-align: middle">病区</span>
</template>
<div style="width: 100%">
<el-button type="primary" @click="open = true" class="mb8"> 新增 </el-button>
<el-button type="primary" @click="onIncrease" class="mb8"> 新增 </el-button>
<el-button type="success" plain @click="handleEnableBatch('wardRef')" class="mb8">
批量启用
</el-button>
@@ -16,7 +16,7 @@
<el-table
max-height="630"
:data="wardList"
@cell-click="(row) => clickRow(row, 10)"
@cell-click="(row) => clickRow(row, 10, 0)"
highlight-current-row
ref="wardRef"
>
@@ -95,7 +95,7 @@
<el-table
height="280"
:data="houseList"
@cell-click="(row) => clickRow(row, 8)"
@cell-click="(row) => clickRow(row, 8, 0, 1)"
highlight-current-row
v-loading="loading"
ref="hourseRef"
@@ -134,7 +134,7 @@
@click.stop="
() => {
handleUnable(scope.row).then(() => {
clickRow(houseRow, 10);
getHouseList();
});
}
"
@@ -153,7 +153,7 @@
@click.stop="
() => {
handleEnable(scope.row).then(() => {
clickRow(houseRow, 10);
getHouseList();
});
}
"
@@ -206,7 +206,7 @@
@click.stop="
() => {
handleUnable(scope.row, 10).then(() => {
clickRow(bedRow, 8);
getBedList();
});
}
"
@@ -225,7 +225,7 @@
@click.stop="
() => {
handleEnable(scope.row, 10).then(() => {
clickRow(bedRow, 8);
getBedList();
});
}
"
@@ -255,7 +255,7 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="type + '名称'" prop="name">
<el-input v-model="form.name" placeholder="请输入科室名称" />
<el-input v-model="form.name" :placeholder="'请输入' + type + '名称'" />
</el-form-item>
<el-col>
<el-form-item :label="upLabel" prop="busNoParent">
@@ -304,8 +304,9 @@
</el-dialog>
</div>
</template>
<script setup name="Ward">
import { onMounted, ref } from 'vue';
import {
getList,
addLocation,
@@ -315,33 +316,65 @@ import {
unableLocation,
enableLocation,
} from './components/api';
import { ElMessage } from 'element-plus';
const { proxy } = getCurrentInstance();
// 病区参数
const queryParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 4,
formEnum: 4, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病房参数
const queryHouseParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 10, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病床参数
const querybedParams = ref({
pageNum: 1,
pageSize: 50,
formEnum: 8, //4 病区 10 病房 8床位
// locationFormList: [4],
});
// 病区、病房、病床类型
const type = ref('病区');
// 病区列表
const wardList = ref([]);
// 床位列表
const bedList = ref([]);
// 病房列表
const houseList = ref([]);
const wardListOption = ref([]);
const organization = ref([]);
const loading = ref(false);
const isEdit = ref(false);
const open = ref(false);
// 病区row
const wardRow = ref({});
// 床位row
const bedRow = ref({});
// 病房row
const houseRow = ref({});
// 记录点击的是启用
const clickType = ref(0);
const orgRef = ref();
// 新增数据参数
const form = reactive({
formEnum: 4,
busNoParent: '',
organizationId: '',
name: '',
busNo: '',
});
const upLabel = ref('关联科室');
const title = ref('新增');
const rules = ref({
name: [
{ required: true, message: '请输入科室名称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
{ required: true, message: '请输入名称', trigger: 'blur' },
{ required: true, min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
],
busNoParent: [
{
@@ -351,23 +384,76 @@ const rules = ref({
},
],
});
/**
* 病区列表
*/
function getWardList() {
queryParams.value.formEnum = 4;
getList(queryParams.value).then((res) => {
wardList.value = res.data.records;
});
}
// 获取科室下啦树
function init() {
getOrgList().then((res) => {
organization.value = res.data.records;
});
}
/**
* 病区列表
*/
function getWardList() {
houseList.value = [];
bedList.value = [];
getList(queryParams.value).then((res) => {
wardList.value = res.data.records;
});
}
// 获取病房列表
const getHouseList = () => {
// 4病区 10病房 8床位
loading.value = true;
// 病区号
queryHouseParams.value.busNo = wardRow.value.busNo;
bedList.value = [];
getList(queryHouseParams.value).then((res) => {
houseList.value = res.data.records;
loading.value = false;
});
};
// 获取床位列表
const getBedList = () => {
// 4病区 10病房 8床位
loading.value = true;
querybedParams.value.busNo = houseRow.value.busNo;
bedList.value = [];
getList(querybedParams.value).then((res) => {
bedList.value = res.data.records;
loading.value = false;
});
};
// 点击新增按钮
const onIncrease = () => {
open.value = true;
};
// 查询病房和病区的下拉选
const getHomeOrBed = (formEnum) => {
const params = {
formEnum,
pageNum: 1,
pageSize: 50,
};
getList(params).then((res) => {
if (formEnum == 10) {
const datas = res.data?.records || [];
const optionsList = datas.map((item) => {
let obj = {
...item,
};
obj.name = item.parentName + '-' + item.name;
return obj;
});
wardListOption.value = optionsList;
} else {
wardListOption.value = res.data?.records || [];
}
});
};
function handleRadioChange(val) {
let formEnum = 4;
if (val == 4) {
type.value = '病区';
upLabel.value = '关联科室';
@@ -375,38 +461,56 @@ function handleRadioChange(val) {
} else if (val == 10) {
type.value = '病房';
upLabel.value = '所属病区';
queryParams.value.formEnum = 4;
formEnum = 4;
// queryParams.value.formEnum = 4;
} else {
type.value = '床位';
upLabel.value = '所属病房';
queryParams.value.formEnum = 10;
formEnum = 10;
// queryParams.value.formEnum = 10;
}
getList(queryParams.value).then((res) => {
wardListOption.value = res.data.records;
});
form.organizationId = '';
form.busNo = '';
form.busNoParent = '';
form.name = '';
getHomeOrBed(formEnum);
}
/**
* 点击患者列表行 获取处方列表
*/
function clickRow(row, val) {
loading.value = true;
queryParams.value.formEnum = val;
queryParams.value.busNo = row.busNo;
bedList.value = [];
getList(queryParams.value).then((res) => {
if (val == 10) {
houseList.value = res.data.records;
houseRow.value = row;
} else if (val == 8) {
bedRow.value = row;
bedList.value = res.data.records;
}
setTimeout(() => {
queryParams.value.busNo = undefined;
loading.value = false;
}, 100);
});
function clickRow(row, val, type) {
// 1点击了启用
clickType.value = type;
console.log('val=====>', JSON.stringify(row));
console.log('type=====>', JSON.stringify(type));
console.log('val=====>', JSON.stringify(val));
// if (type !== 1) {
// if (val == 10) {
// wardRow.value = row;
// getHouseList();
// } else if (val == 8) {
// houseRow.value = row;
// getBedList();
// }
// } else {
// if (val == 10) {
// houseRow.value = row;
// getHouseList();
// } else if (val == 8) {
// bedRow.value = row;
// getBedList();
// }
// }
if (val == 10) {
wardRow.value = row;
getHouseList();
} else if (val == 8) {
houseRow.value = row;
console.log('houseRow=====>', houseRow.value);
getBedList();
}
}
function checkSelectable(row, index) {
@@ -459,38 +563,113 @@ function handleUnableBatch(tableRef) {
});
}
// 新增病床拆分"busNo": "LOC055.LOC056",
const splitBusNo = (busNo) => {
const busNoArr = busNo.split('.') || [];
let busNoParent = '';
if (busNoArr.length > 1) {
busNoParent = busNoArr[0];
}
return busNoParent;
};
function submitForm() {
if (form.busNoParent) {
if (form.formEnum == 4) {
form.organizationId = form.busNoParent;
} else {
form.busNo = form.busNoParent;
if (!orgRef) return;
const params = {
...form,
};
console.log('form========>', JSON.stringify(form));
console.log('params11========>', JSON.stringify(params));
orgRef.value.validate((valid) => {
if (valid) {
console.log('99999999');
if (form.busNoParent) {
if (form.formEnum == 4) {
params.organizationId = form.busNoParent;
} else {
if (!isEdit.value) {
params.busNo = form.busNoParent;
// params.busNoParent = splitBusNo(form.busNoParent);
}
}
} else {
if (form.formEnum == 4) {
if (!(params.organizationId && params.organizationId.length > 0)) {
ElMessage({
type: 'error',
message: '请选择关联科室!',
});
return;
}
} else if (form.formEnum == 10) {
if (!(params.busNo && params.busNo.length > 0)) {
ElMessage({
type: 'error',
message: '请选择所属病区!',
});
return;
}
} else {
if (!(params.busNo && params.busNo.length > 8)) {
ElMessage({
type: 'error',
message: '请选择所属病房!',
});
return;
}
}
}
// console.log('params========>', JSON.stringify(form));
console.log('params========>', JSON.stringify(params));
// console.log('params========>', isEdit.value);
if (!isEdit.value) {
addLocation(params).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
if (params.formEnum == 4) {
cancel();
getWardList();
} else if (params.formEnum == 10) {
getHouseList();
open.value = false;
} else if (params.formEnum == 8) {
getBedList();
open.value = false;
}
}
});
} else {
editLocation(params).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
if (params.formEnum == 4) {
cancel();
getWardList();
} else if (params.formEnum == 10) {
getHouseList();
open.value = false;
} else if (params.formEnum == 8) {
getBedList();
open.value = false;
}
}
});
}
}
}
console.log(form);
if (!isEdit.value) {
addLocation(form).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
cancel();
getWardList();
}
});
} else {
editLocation(form).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
cancel();
getWardList();
}
});
}
});
}
function handleDelete(row) {
deleteLocation(row.busNo).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
if (row.formEnum == 4) {
getWardList();
} else if (row.formEnum == 10) {
getHouseList();
} else {
getBedList();
}
}
});
}
@@ -501,24 +680,27 @@ function getLastPartOfString(str) {
}
function handleEdit(row, val) {
console.log('editRow=========>', JSON.stringify(row));
form.id = row.id;
form.name = row.name;
form.formEnum = row.formEnum;
form.busNo = row.busNo;
if (row.organizationId) {
form.busNoParent = row.organizationId;
form.organizationId = row.organizationId;
} else {
form.busNoParent = row.busNo.split('.').slice(0, -1).join('.');
}
isEdit.value = true;
title.value = '编辑';
if (val) {
queryParams.value.formEnum = val;
getList(queryParams.value).then((res) => {
wardListOption.value = res.data.records;
});
}
open.value = true;
console.log('editRow1=========>', JSON.stringify(form));
if (val == 4) {
getHomeOrBed(4);
} else if (val == 10) {
getHomeOrBed(10);
}
}
function cancel() {
@@ -532,10 +714,13 @@ function cancel() {
isEdit.value = false;
title.value = '新增';
}
init();
getWardList();
// 页面挂在成功
onMounted(() => {
// 获取所有科室
init();
// 获取病区列表
getWardList();
});
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -217,6 +217,7 @@
:statusFlagOptions="statusFlagOptions"
:exeOrganizations="exeOrganizations"
:typeEnumOptions="typeEnumOptions"
:isEditInfoDisable="isEditInfoDisable"
:title="title"
:item="currentData"
@submit="getList()"

View File

@@ -197,8 +197,15 @@ const emit = defineEmits(['close']);
function submit() {
console.log(props.chargeItemIds);
if (parseFloat(displayAmount.value) < formData.totalAmount) {
console.log(
displayAmount.value,
parseFloat(displayAmount.value),
formData.totalAmount,
formData,
'feeRefund'
);
//比较时,将金额都转换为两位小数的字符串再转换为浮点数,避免浮点数精度问题
if (parseFloat(displayAmount.value) < parseFloat(formData.totalAmount.toFixed(2))) {
proxy.$modal.msgError('请输入正确的金额');
return;
}
@@ -255,7 +262,7 @@ function submit() {
// returnedAmount: parseFloat(returnedAmount.value),
}).then((res) => {
if (res.code == 200) {
// 长春大学自动退耗材
// 长春市朝阳区中医院自动退耗材
emit('close', 'success');
}
@@ -457,4 +464,4 @@ const getFeeTypeText = computed(() => {
.amount-input .el-input__inner {
padding-right: 30px !important;
}
</style>
</style>

View File

@@ -139,16 +139,6 @@
prop="chargeStatus_enumText"
width="100"
/>
<!-- <el-table-column
label="发药/执行状态"
align="center"
prop="dispenseStatus_enumText"
width="130"
>
<template #default="scope">
{{ scope.row.dispenseStatus_enumText || scope.row.serviceStatus_enumText }}
</template>
</el-table-column> -->
<el-table-column label="数量" align="center" width="100">
<template #default="scope">
{{ scope.row.quantityValue + ' ' + scope.row.quantityUnit_dictText }}

View File

@@ -0,0 +1,39 @@
import request from '@/utils/request';
import { dateEquals } from 'element-plus';
// 获取网银支付参数列表
export function getList (data) {
return request ({
url: '/three-part/pay/page',
method: 'get',
params:data,
});
}
export function getPayinfo (data) {
return request ({
url: '/three-part/pay/pay-query?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnfee (data) {
return request ({
url: '/three-part/pay/return-bill?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnFeednext (data) {
return request ({
url: '/three-part/pay/return-goods?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnResult (data) {
return request ({
url: '/three-part/pay/return-query?id='+data.id,
method: 'get',
});
}

View File

@@ -0,0 +1,119 @@
<template>
<div class="app-continer">
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="queryParams.searchKey" placeholder="请输入患者姓名" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="recordList" v-loading="loading" border ref="tableRef">
<el-table-column label="患者姓名" align="center" prop="patientName" :show-overflow-tooltip="true" />
<el-table-column label="支付单号" align="center" prop="paymentBusNo" :show-overflow-tooltip="true" />
<el-table-column label="交易金额(元)" align="right" prop="txnAmt" header-align="center" width="100"
:show-overflow-tooltip="true">
</el-table-column>
<el-table-column label="交易类型" align="center" prop="tranType" :show-overflow-tooltip="true" />
<el-table-column label="支付方式" align="center" prop="payType" :show-overflow-tooltip="true" />
<el-table-column label="交易时间" align="center" prop="txnTime" :show-overflow-tooltip="true">
<template #default="scope">
<span>{{ parseTime(scope.row.txnTime) }}</span>
</template>
</el-table-column>
<el-table-column label="原交易类型" align="center" prop="orgTranType" :show-overflow-tooltip="true" />
<el-table-column label="原交易类型" align="center" prop="orgTranType" :show-overflow-tooltip="true" />
<el-table-column label="第三方优惠说明" align="center" prop="otherMsg" :show-overflow-tooltip="true" />
<el-table-column label="错误信息" align="center" prop="errMsg" :show-overflow-tooltip="true" />
<el-table-column label="查询结果" align="center" prop="queryResult" :show-overflow-tooltip="true" />
<el-table-column label="查询结果说明" align="center" prop="queryResultMsg" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" prop="paymentEnum_enumText" width="340">
<template #default="scope">
<el-button type="primary" link :disabled="!scope.row.paymentId"
@click="getPayInfo(scope.row.paymentId)">支付结果查询</el-button>
<el-button type="warning" link :disabled="!scope.row.paymentId"
@click="returnFee(scope.row.paymentId)">退费</el-button>
<el-button type="danger" link auto-insert-space :disabled="!scope.row.paymentId"
@click="returnFeeNext(scope.row.paymentId)">隔天退费</el-button>
<el-button type="success" link @click="returnFeeResultQuery(scope.row.id)">退费结果查询</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getLists" />
</div>
</template>
<script setup name="ClinicRecord">
const { proxy } = getCurrentInstance();
import { getList, getPayinfo, returnfee, returnFeednext, returnResult } from './components/api.js';
const total = ref(0);
const queryParams = ref({
pageNo: 1,
pageSize: 100,
searchKey: '',
});
const recordList = ref([]);
const loading = ref(false);
const getPayInfo = (paymentId) => {
console
getPayinfo({ paymentId }).then((res) => {
proxy.$message.success('支付结果查询成功');
console.log(res);
});
};
const returnFee = (paymentId) => {
returnfee({ paymentId }).then((res) => {
proxy.$message.success('退费成功');
console.log(res);
});
};
const returnFeeNext = (paymentId) => {
returnFeednext({ paymentId }).then((res) => {
proxy.$message.success('隔天退费成功');
console.log(res);
});
};
const returnFeeResultQuery = (id) => {
returnResult({ id }).then((res) => {
proxy.$message.success(res.data);
console.log(res);
});
};
const handleQuery = () => {
//queryParams.value.pageNum = 1;
getList(queryParams.value);
};
const resetQuery = () => {
proxy.resetForm('queryRef');
handleQuery();
};
const getLists = async () => {
loading.value = true;
getList(queryParams.value).then((res) => {
loading.value = false;
recordList.value = res.data.records;
total.value = res.data.total;
});
};
getLists();
</script>
<style scoped>
.app-continer {
padding: 20px;
}
.el-dialog {
height: 90vh !important;
}
.dialog-footer {
text-align: right;
}
.el-textarea__inner {
resize: none;
}
</style>

View File

@@ -12,6 +12,16 @@
width="100"
/>
<el-table-column label="门诊号" align="center" prop="iptOtpNo" />
<el-table-column label="患者姓名" align="center" prop="patnName" />
<el-table-column label="身份证号" align="center" prop="certno" />
<el-table-column label="诊断名" align="center" prop="conditionName" />
<el-table-column
label="慢性诊断名"
align="center"
prop="specialConditionName"
width="180"
/>
<el-table-column label="请求数量" align="center" prop="quantity" />
<el-table-column label="请求单位" align="center" prop="unitCode" />
<el-table-column label="审核状态" align="center" prop="statusEnum_enumText" />
@@ -38,8 +48,7 @@
<el-table-column label="给药间隔" align="center" prop="dispenseInterval" />
<el-table-column label="单次发药数" align="center" prop="dispensePerQuantity" />
<el-table-column label="每次发药供应天数" align="center" prop="dispensePerDuration" />
<el-table-column label="患者姓名" align="center" prop="patnName" />
<el-table-column label="身份证号" align="center" prop="certno" />
<el-table-column label="开方医生名" align="center" prop="practitionerName" />
<el-table-column label="挂号科室" align="center" prop="mdtrtDeptName" />
<el-table-column label="开单科室" align="center" prop="prscDeptName" />
@@ -53,7 +62,6 @@
{{ formatDate(scope.row.prscTime) }}
</template>
</el-table-column>
<el-table-column label="诊断名" align="center" prop="conditionName" />
</el-table>
<!-- <pagination
v-show="total > 0"

View File

@@ -8,32 +8,73 @@
</div>
</template>
<div style="width: 100%">
<el-input v-model="queryParams.searchKey" placeholder="搜索患者"
style="width: 48%; margin-bottom: 10px; margin-right: 15px" @keyup.enter="getEncounterList">
<el-input
v-model="queryParams.searchKey"
placeholder="搜索患者"
style="width: 48%; margin-bottom: 10px; margin-right: 15px"
@keyup.enter="getEncounterList"
>
<template #append>
<el-button icon="Search" @click="getEncounterList" />
</template>
</el-input>
<el-select v-model="queryParams.refundEnum" style="width: 48%; margin-bottom: 10px" placeholder="收费状态"
@change="getEncounterList">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
<el-select
v-model="queryParams.refundEnum"
style="width: 48%; margin-bottom: 10px"
placeholder="收费状态"
@change="getEncounterList"
>
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker v-model="dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
style="width: 85%; margin-bottom: 10px" value-format="YYYY-MM-DD" placement="bottom"
@change="getEncounterList" />
<el-button type="primary" @click="getEncounterList" style="margin-bottom: 10px; margin-left: 18px">
<el-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 85%; margin-bottom: 10px"
value-format="YYYY-MM-DD"
placement="bottom"
@change="getEncounterList"
/>
<el-button
type="primary"
@click="getEncounterList"
style="margin-bottom: 10px; margin-left: 18px"
>
搜索
</el-button>
</div>
<el-table :data="encounterList" border style="width: 100%" height="calc(100vh - 300px)" highlight-current-row
@cell-click="handleGetReturnDrugList">
<el-table-column prop="patientName" align="center" label="姓名" width="130" show-overflow-tooltip />
<el-table-column prop="genderEnum_enumText" align="center" label="性别" show-overflow-tooltip />
<el-table
:data="encounterList"
border
style="width: 100%"
height="calc(100vh - 300px)"
highlight-current-row
@cell-click="handleGetReturnDrugList"
>
<el-table-column
prop="patientName"
align="center"
label="姓名"
width="130"
show-overflow-tooltip
/>
<el-table-column
prop="genderEnum_enumText"
align="center"
label="性别"
show-overflow-tooltip
/>
<el-table-column align="center" width="140" label="就诊日期" show-overflow-tooltip>
<template #default="scope">
{{
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
}}
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
}}
</template>
</el-table-column>
<!-- <el-table-column label="状态" align="center" prop="refundEnum_enumText" /> -->
@@ -52,16 +93,35 @@
</div>
</template>
<el-button type="primary" :disabled="!selectedMedicines.length" @click="handleReturnDrug(undefined)"
style="margin-bottom: 10px">
<el-button
type="primary"
:disabled="!selectedMedicines.length"
@click="handleReturnDrug(undefined)"
style="margin-bottom: 10px"
>
确认退药
</el-button>
<el-button type="primary" @click="handleScan()" style="margin-bottom: 10px"> 扫码 </el-button>
<el-table ref="returnDrugRef" :data="returDrugList" style="width: 100%" height="calc(100vh - 300px)" border
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table
ref="returnDrugRef"
:data="returDrugList"
style="width: 100%"
height="calc(100vh - 300px)"
border
@select="handleSelection"
@selection-change="handelSelectRows"
:span-method="handelSpanMethod"
class="no-hover-table"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="itemName" label="药品名称" show-overflow-tooltip align="center" />
<el-table-column prop="totalPrice" label="总价" width="100" align="right" header-align="center">
<el-table-column
prop="totalPrice"
label="总价"
width="100"
align="right"
header-align="center"
>
<template #default="scope">
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '-' }}
</template>
@@ -74,29 +134,30 @@
</el-table-column>
<el-table-column prop="reqStatus_enumText" label="退药状态" width="100" align="center">
<template #default="scope">
{{
scope.row.reqStatus_enumText == null
? scope.row.refundEnum_enumText
: scope.row.reqStatus_enumText
}}
{{ scope.row.refundEnum_enumText }}
</template>
</el-table-column>
<el-table-column prop="waitingQuantity" label="退药数量" width="100" align="center">
<template #default="scope">
<span>{{
scope.row.quantity
? Math.abs(scope.row.quantity)
: '0' + ' ' + scope.row.unitCode_dictText
}}</span>
scope.row.quantity
? Math.abs(scope.row.quantity) + ' ' + scope.row.unitCode_dictText
: '0' + ' ' + scope.row.unitCode_dictText
}}</span>
</template>
</el-table-column>
<el-table-column prop="doctorName" label="开单医生" align="center" width="180" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-popconfirm width="150" hide-after="10" title="操作确认" placement="top-start"
@confirm="handleReturnDrug(scope.row)">
<el-popconfirm
width="150"
hide-after="10"
title="操作确认"
placement="top-start"
@confirm="handleReturnDrug(scope.row)"
>
<template #reference>
<el-button type="primary" link :disabled="scope.row.reqStatus != 11">
<el-button type="primary" link :disabled="scope.row.refundEnum != 16">
退药
</el-button>
</template>
@@ -113,13 +174,17 @@
</div>
</div>
</el-card>
<TraceNoDialog :ypName="ypName" :openDialog="openTraceNoDialog" @submit="submit"
@cancel="openTraceNoDialog = false" />
<TraceNoDialog
:ypName="ypName"
:openDialog="openTraceNoDialog"
@submit="submit"
@cancel="openTraceNoDialog = false"
/>
</div>
</template>
<script setup name="ReturnDrug">
import { getCurrentInstance } from 'vue';
import { getCurrentInstance, nextTick } from 'vue';
import { getList, getReturnDrugList, returnDrug, init, itemTraceNo } from './components/api';
import { formatDateStr } from '@/utils/index';
import { debounce } from 'lodash-es';
@@ -128,7 +193,7 @@ import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue';
const queryParams = ref({
pageSize: 50,
pageNum: 1,
refundEnum: 5,
refundEnum: 16,
});
const openTraceNo = ref(false);
const traceNoList = ref([]);
@@ -138,8 +203,10 @@ const encounterId = ref('');
const returDrugList = ref([]);
const selectedMedicines = ref([]);
const statusOptions = ref([]);
const dateRange = ref([formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),]);
const dateRange = ref([
formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),
]);
const traceNoTemp = ref('');
const traceNoTempRef = ref();
const totalAmount = ref(0);
@@ -226,14 +293,16 @@ function handleReturnDrug(row) {
console.log(row);
let saveList = [];
if (row) {
saveList = [
{
requestId: row.requestId,
dispenseId: row.dispenseId,
tableName: row.serviceTable,
traceNo: row.traceNo,
},
];
saveList = returDrugList.value
.filter((item) => item.requestId == row.requestId)
.map((item) => {
return {
requestId: item.requestId,
dispenseId: item.dispenseId,
tableName: item.serviceTable,
traceNo: item.traceNo,
};
});
} else {
saveList = proxy.$refs.returnDrugRef.getSelectionRows().map((item) => {
return {
@@ -248,7 +317,7 @@ function handleReturnDrug(row) {
returnDrug(saveList).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('退药成功');
getEncounterList()
getEncounterList();
getReturnDrugList({
encounterId: encounterId.value,
refundStatus: queryParams.value.refundEnum,
@@ -259,12 +328,79 @@ function handleReturnDrug(row) {
});
}
function handleSelectionChange(selectRows) {
selectedMedicines.value = selectRows;
totalAmount.value = selectRows.reduce((accumulator, currentRow) => {
// 选择框改变时的处理
function handleSelection(selection, row) {
const isSelected = selection.some((item) => item.dispenseId === row.dispenseId);
returDrugList.value
.filter((item) => {
return item.requestId == row.requestId;
})
.forEach((row) => {
proxy.$refs['returnDrugRef'].toggleRowSelection(row, isSelected);
});
nextTick(() => {
selectedMedicines.value = proxy.$refs['returnDrugRef'].getSelectionRows();
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
return accumulator + (currentRow.totalPrice || 0);
}, 0);
});
}
function handelSelectRows(selection) {
selectedMedicines.value = selection;
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
return accumulator + (currentRow.totalPrice || 0);
}, 0);
}
// 根据 requestId 合并相同行的方法(只合并药品名称和总价列)
function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
// 定义需要合并的列索引
// 1: 药品名称列, 2: 总价列
const mergeColumns = [1, 2, 5, 7, 8];
// 检查当前列是否需要合并
if (!mergeColumns.includes(columnIndex)) {
return [1, 1];
}
// 获取当前行的 requestId
const currentRequestId = row.requestId;
// 如果没有 requestId不进行合并
if (!currentRequestId) {
return [1, 1];
}
// 查找具有相同 requestId 的连续行
let rowspan = 1;
let colspan = 1;
// 向上查找相同 requestId 的行
let startIndex = rowIndex;
while (startIndex > 0 && returDrugList.value[startIndex - 1].requestId === currentRequestId) {
startIndex--;
rowspan++;
}
// 如果当前行不是合并组的第一行,则不显示
if (startIndex !== rowIndex) {
return [0, 0];
}
// 向下查找相同 requestId 的行
let endIndex = rowIndex;
while (
endIndex < returDrugList.value.length - 1 &&
returDrugList.value[endIndex + 1].requestId === currentRequestId
) {
endIndex++;
rowspan++;
}
// 只有当 rowspan > 1 时才进行合并
return rowspan > 1 ? [rowspan, colspan] : [1, 1];
}
</script>
<style lang="scss" scoped>
@@ -339,4 +475,8 @@ function handleSelectionChange(selectRows) {
::v-deep.el-textarea .el-textarea__inner {
resize: none !important;
}
:deep(.no-hover-table) .el-table__body tr:hover > td {
background: inherit !important;
}
</style>

View File

@@ -1,34 +1,77 @@
import request from '@/utils/request'
import request from '@/utils/request';
export function listSkinRecord(query) {
/**
* 获取患者列表
*/
export function getList(queryParams) {
return request({
url: '/outpatient-manage/skin-test/outpatient-record-page',
url: '/outpatient-manage/treatment/encounter-list',
method: 'get',
params: query
})
params: queryParams,
});
}
/**
* 获取皮试记录列表
*/
export function listSkinRecord(queryParams) {
return request({
url: '/outpatient-manage/skin-test/record-info',
method: 'get',
params: queryParams,
});
}
/**后台没有对应接口,暂无
* 更新护士签名
*/
export function updateNurseSign(data) {
return request({
url: '/outpatient-manage/skin-test/nurse-sign',
method: 'put',
data: data,
});
}
/**
* 更新皮试记录
*/
export function updateSkinTestRecord(data) {
return request({
url: '/outpatient-manage/skin-test/save-record-info',
method: 'post',
data: data,
});
}
/**
* 初始化选项
*/
export function lists() {
return request({
url: '/outpatient-manage/skin-test/init',
method: 'get',
})
});
}
/**
* 获取护士列表
*/
export function getNurseList(queryParams) {
return request({
url: '/app-common/nurse-list',
method: 'get',
params: queryParams,
});
}
export function updateSkinTestRecord(data) {
return request({
url: '/outpatient-manage/skin-test/outpatient-record-skin-test',
method: 'put',
data: data
})
}
export function updateNurseSign(data) {
return request({
url: '/outpatient-manage/skin-test/outpatient-record-sign-check',
method: 'put',
data: data
})
}
/**
* 新增皮试记录
*/
export function addSkinTestRecord(data) {
return request({
url: '/outpatient-manage/skin-test/save-record-info',
method: 'post',
data: data,
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -101,9 +101,9 @@
>
<el-option
v-for="item in diagnosisListOption"
:key="item.id"
:label="item.name"
:value="item.id"
:key="item.definitionId"
:label="item.name + '--' + item.ybNo"
:value="item.definitionId"
/>
</el-select>
</el-form-item>
@@ -432,7 +432,7 @@ import { computed, onMounted, ref } from 'vue';
import { reactive } from 'vue';
// import { useModal, useDict } from '@/hooks';
import { parseTime, formatNumber } from '@/utils/his';
import { queryYbCatalogue, getDiagnosisList } from './api';
import { queryYbCatalogue, getDiagnosisListEle } from './api';
import { debounce } from 'lodash-es';
import {
@@ -565,7 +565,7 @@ const unitMap = ref({
function getInit(searchKey) {
if(searchKey) {
getDiagnosisList(searchKey).then(res => {
getDiagnosisListEle(searchKey,infoForm.encounterId).then(res => {
diagnosisListOption.value = res.data
})
}

View File

@@ -150,12 +150,12 @@
</el-col>
<el-col :span="12">
<el-form-item label="入院诊断" prop="diagnosisDefinitionId">
<el-select
<!-- <el-select
v-model="submitForm.diagnosisDefinitionId"
placeholder="诊断"
clearable
filterable
remote
remote
:remote-method="getDiagnosisInfo"
style="width: 400px"
>
@@ -166,7 +166,10 @@
:value="item.id"
@click="handleDiagnosisChange(item)"
/>
</el-select>
</el-select> -->
<el-input v-model="props.mainDiagnosis.name" disabled style="width: 400px" />
<!-- 隐藏存储ID的字段 -->
<input type="hidden" v-model="submitForm.diagnosisDefinitionId" />
</el-form-item>
</el-col>
</el-row>
@@ -179,7 +182,7 @@
</template>
</el-dialog>
</template>
<script setup>
import {
getInit,
@@ -206,6 +209,7 @@ const props = defineProps({
type: String,
default: '',
},
mainDiagnosis: { type: Object, default: null },
});
const emit = defineEmits(['close']);
@@ -244,9 +248,17 @@ const rules = reactive({
});
function openDialog() {
console.log('orgId==========>', props.patientInfo.orgId);
getOrgList().then((res) => {
organization.value = res.data.records;
// organization.value = res.data.records;
organization.value = res.data.records[0].children.filter(
(record) => record.typeEnum === 2 && record.classEnum === 2
);
console.log('organization==========>', organization.value);
submitForm.inHospitalOrgId =
organization.value.find((item) => item.id === props.patientInfo.orgId)?.id || '';
});
// wardList().then((res) => {
// wardListOptions.value = res.data;
// });
@@ -256,6 +268,14 @@ function openDialog() {
});
console.log(props.patientInfo, 'patientInfo');
getDiagnosisInfo(undefined);
console.log(props.mainDiagnosis, 'mainDiagnosis');
if (props.mainDiagnosis) {
submitForm.diagnosisDefinitionId = props.mainDiagnosis.definitionId;
diagnosisDefinitionId = props.mainDiagnosis.definitionId;
diagnosisYbNo = props.mainDiagnosis.ybNo || '';
submitForm.medTypeCode = props.mainDiagnosis.medTypeCode;
diagnosisDefinitionList.value = [props.mainDiagnosis];
}
}
function getDiagnosisInfo(value) {
@@ -333,4 +353,4 @@ function close() {
.patInfo-value {
width: 100px;
}
</style>
</style>

View File

@@ -0,0 +1,195 @@
<!-- consumableDialog.vue -->
<template>
<el-dialog
v-model="dialogVisible"
title="皮试检查"
width="700px"
:close-on-click-modal="false"
:before-close="handleClose"
>
<div class="consumable-dialog">
<div class="dialog-header">
<el-alert
title="下列药品需要执行皮试项目,请确认,点击确定将自动添加皮试检查项目到医嘱列表"
type="warning"
show-icon
:closable="false"
/>
</div>
<div class="table-container">
<el-table
ref="consumableTableRef"
:data="consumableList"
row-key="orderDefinitionId"
border
style="width: 100%"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column prop="adviceName" align="center" label="项目名称" />
<el-table-column prop="unitCodeName" label="单位" align="center" width="80">
<template #default="scope">
{{ scope.row.unitCodeName || '-' }}
</template>
</el-table-column>
<el-table-column label="执行次数" align="center" width="120">
<template #default="scope">
{{ '1' }}
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" link size="small" @click="handleDeleteRow(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 下次不再提示复选框 -->
<div class="dont-show-again">
<!-- <el-checkbox v-model="dontShowAgain" @change="handleDontShowAgainChange">
下次不再提示
</el-checkbox> -->
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button ref="submitRef" type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ElMessageBox, ElMessage } from 'element-plus';
import { ref, nextTick, onBeforeUnmount } from 'vue';
import useUserStore from '@/store/modules/user';
const dialogVisible = ref(false);
const consumableList = ref([]);
const consumableTableRef = ref();
const submitRef = ref();
const dontShowAgain = ref(false); // 下次不再提示的状态
const userStore = useUserStore();
// 定义事件
const emit = defineEmits(['submit']);
// 键盘事件处理函数
const handleKeyDown = (event) => {
// 检查是否按下了回车键
if (event.key === 'Enter' && dialogVisible.value) {
event.preventDefault();
handleSubmit();
}
};
// 打开弹窗方法
const open = (data) => {
consumableList.value = data;
dialogVisible.value = true;
// 默认全选
nextTick(() => {
if (consumableTableRef.value) {
consumableList.value.forEach((row) => {
consumableTableRef.value.toggleRowSelection(row, true);
});
}
// 注册键盘事件监听器
document.addEventListener('keydown', handleKeyDown);
});
};
// 关闭弹窗方法
const handleClose = () => {
// 移除键盘事件监听器
document.removeEventListener('keydown', handleKeyDown);
dialogVisible.value = false;
consumableList.value = [];
};
// 页面卸载前清理事件监听器
onBeforeUnmount(() => {
document.removeEventListener('keydown', handleKeyDown);
});
// 删除行处理
const handleDeleteRow = (row) => {
ElMessageBox.confirm(`确定要删除 "${row.orderDefinitionName}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
const index = consumableList.value.findIndex(
(item) => item.orderDefinitionId === row.orderDefinitionId
);
if (index > -1) {
consumableList.value.splice(index, 1);
}
});
};
// 提交处理
const handleSubmit = () => {
const selectedRows = consumableTableRef.value.getSelectionRows();
// 保存到本地存储
// localStorage.setItem('doctor' + userStore.id.toString(), dontShowAgain.value);
if (selectedRows.length === 0) {
ElMessage.warning('请至少选择一项');
return;
}
// 发送事件给父组件
emit('submit', selectedRows);
// 关闭弹窗并清理事件监听器
document.removeEventListener('keydown', handleKeyDown);
dialogVisible.value = false;
consumableList.value = [];
};
// 暴露方法给父组件
defineExpose({
open,
close: handleClose,
});
</script>
<style lang="scss" scoped>
.consumable-dialog {
.dialog-header {
margin-bottom: 20px;
}
.table-container {
margin-bottom: 20px;
}
.dont-show-again {
margin-top: 10px;
text-align: left;
}
.dialog-footer-summary {
text-align: right;
.summary-text {
font-size: 14px;
color: #606266;
.total-amount {
font-size: 16px;
font-weight: bold;
color: #e64545;
}
}
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
align-items: center;
}
</style>

View File

@@ -0,0 +1,243 @@
<template>
<div class="report-container">
<div class="report-section">
<div class="report-title">
<span>检查报告</span>
<el-icon
class="report-refresh-icon"
:class="{ 'is-loading': loadingCheck }"
@click="handleRefreshCheck"
>
<Refresh />
</el-icon>
</div>
<div class="report-table-wrapper">
<el-table
v-loading="loadingCheck"
:data="checkReportList"
border
size="small"
height="100%"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="adviceName" label="报告名称" width="140" />
<el-table-column prop="reportNo" label="报告号" width="140" />
<el-table-column label="链接" min-width="140">
<template #default="scope">
<a
v-if="scope.row.requestUrl"
class="report-link"
:href="scope.row.requestUrl"
target="_blank"
rel="noopener noreferrer"
>
查看报告
</a>
<span v-else class="report-link-disabled">暂无链接</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="report-section">
<div class="report-title">
<span>检验报告</span>
<el-icon
class="report-refresh-icon"
:class="{ 'is-loading': loadingInspection }"
@click="handleRefreshInspection"
>
<Refresh />
</el-icon>
</div>
<div class="report-table-wrapper">
<el-table
v-loading="loadingInspection"
:data="inspectionReportList"
border
size="small"
height="100%"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="adviceName" label="报告名称" width="140" />
<el-table-column prop="reportNo" label="报告号" width="140" />
<el-table-column label="链接" min-width="140">
<template #default="scope">
<a
v-if="scope.row.requestUrl"
class="report-link"
:href="scope.row.requestUrl"
target="_blank"
rel="noopener noreferrer"
>
查看报告
</a>
<span v-else class="report-link-disabled">暂无链接</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</template>
<script setup>
import { getCurrentInstance, ref, watch } from 'vue';
import { Refresh } from '@element-plus/icons-vue';
import { getProofResult, getTestResult } from './api';
const { proxy } = getCurrentInstance();
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
const checkReportList = ref([]);
const inspectionReportList = ref([]);
const loadingCheck = ref(false);
const loadingInspection = ref(false);
const fetchCheckReport = async () => {
if (!props.patientInfo?.encounterId) return;
const res = await getTestResult({ encounterId: props.patientInfo.encounterId });
if (res.code === 200 && res.data) {
const raw = res.data?.records || res.data;
const list = Array.isArray(raw) ? raw : [raw];
checkReportList.value = list.filter(Boolean).map((item) => ({
reportNo: item.busNo,
requestUrl: item.requestUrl,
adviceName: item.adviceName,
_raw: item,
}));
} else {
checkReportList.value = [];
}
};
const fetchInspectionReport = async () => {
if (!props.patientInfo?.encounterId) return;
const res = await getProofResult({ encounterId: props.patientInfo.encounterId });
if (res.code === 200 && res.data) {
const raw = res.data?.records || res.data;
const list = Array.isArray(raw) ? raw : [raw];
inspectionReportList.value = list.filter(Boolean).map((item) => ({
reportNo: item.busNo,
requestUrl: item.requestUrl,
adviceName: item.adviceName,
_raw: item,
}));
} else {
inspectionReportList.value = [];
}
};
const fetchAll = async () => {
if (!props.patientInfo?.encounterId) {
checkReportList.value = [];
inspectionReportList.value = [];
loadingCheck.value = false;
loadingInspection.value = false;
return;
}
loadingCheck.value = true;
loadingInspection.value = true;
try {
await Promise.all([fetchCheckReport(), fetchInspectionReport()]);
} catch (e) {
proxy.$modal?.msgError?.(e.message || '查询报告失败');
} finally {
loadingCheck.value = false;
loadingInspection.value = false;
}
};
const handleRefreshCheck = async () => {
if (loadingCheck.value || !props.patientInfo?.encounterId) return;
loadingCheck.value = true;
try {
await fetchCheckReport();
} finally {
loadingCheck.value = false;
}
};
const handleRefreshInspection = async () => {
if (loadingInspection.value || !props.patientInfo?.encounterId) return;
loadingInspection.value = true;
try {
await fetchInspectionReport();
} finally {
loadingInspection.value = false;
}
};
watch(
() => props.patientInfo?.encounterId,
(val) => {
if (val) {
fetchAll();
} else {
checkReportList.value = [];
inspectionReportList.value = [];
}
},
{ immediate: true }
);
</script>
<style scoped lang="scss">
.report-container {
display: flex;
flex-direction: column;
gap: 12px;
padding: 8px 0;
height: 100%;
}
.report-section {
background: #fff;
flex: 1;
max-height: 55%;
min-height: 0;
display: flex;
flex-direction: column;
}
.report-title {
font-weight: 600;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
.report-table-wrapper {
flex: 1;
min-height: 0;
overflow: auto;
}
.report-refresh-icon {
cursor: pointer;
color: #909399;
transition: color 0.2s;
}
.report-refresh-icon:hover {
color: #409eff;
}
.report-link {
color: #409eff;
cursor: pointer;
text-decoration: underline;
}
.report-link-disabled {
color: #c0c4cc;
}
</style>

View File

@@ -0,0 +1,259 @@
const formData = reactive({
//医院信息
hospitalInfo: {
//组织机构代码
medins_orgcode: '',
//医疗付款方式
medfee_paymtd_code: '',
},
//患者信息
patientInfo: {
// 健康卡号
healthCardNo: '',
// 患者姓名
patient_name: '',
// 患者性别
gend: '',
// 出生日期
brdy: '',
// 年龄
age: '',
// 国籍
ntly: '中国',
// 籍贯
napl: '',
// 民族
naty: '1',
// 身份证号
certno: '',
// 户口住址
resd_addr: '',
// 工作单位地址
empr_addr: '',
// 联系人姓名
coner_name: '',
// 关系
coner_rlts_code: '',
// 联系人地址
coner_addr: '',
// 联系人电话
coner_tel: '',
},
// 住院信息
admission: {
// 第几次住院
patn_ipt_cnt: 1,
// 住院号
ipt_no: '',
// 病案号
medcasno: '',
// 入院途径
adm_way_code: '',
// 入院时间
adm_time: '',
// 入院科室
adm_dept_name: '',
// 病房
adm_ward: '',
// 确诊日期
adm_date: '',
// 出院时间
dscg_date: '',
// 出院科室
dscg_caty: '',
// 病房
dscg_ward: '',
// 实际住院天数
act_ipt_days: '',
},
// 诊断信息
diagnosis: {
// 主要诊断
mainDiagnosis: '',
// 其他诊断
otherDiagnosis: '',
},
// 诊断信息
diagnosisList: [],
// 医疗信息
medicalInfo: {
// 是否输血
bloodTransfusion: '',
// 血型
abo_code: '',
// rh类型
rh_code: '',
// 药物过敏史
die_autp_flag: '',
},
// 医师信息
doctorInfo: {
// 科主任
deptdrt_name: '',
// 副主任
chfdr_name: '',
// 主治医师
chfpdr_name: '',
// 住院医师
ipt_dr_name: '',
// 责任护士
resp_nurs_name: '',
// 住院总医师
chiefResident: '',
// 实习医师
intn_dr_name: '',
// 病案质量
medcas_qlt_code: '',
// 编码员
codr_name: '',
// 控制日期
qltctrl_date: '',
},
// 病案首页2
medicalSecond: {
// 手术方式
surgeryType: '',
// 离院方式
dscg_way: '',
// 31天是否计划出院
dscg_31days_rinp_flag: '',
// 目的
dscg_31days_rinp_pup: '',
//昏迷时间---入院前
brn_damg_bfadm_coma_dura: '',
//昏迷时间---入院后
brn_damg_afadm_coma_dura: '',
// 肿瘤分期
tumorStaging: '',
// T
tumor_T: '',
// N
tumor_N: '',
// M
tumor_M: '',
// 判断依据
judgmentBase: '',
// 分化程度
bkup_deg_code: '',
// 临床路径
enterPath: '',
// 变异
mutation: '',
// 退出路径
outPath: '',
// 特级护理
nursingLevel_spec: '',
// 1级护理
nursingLevel_1: '',
// 2级护理
nursingLevel_2: '',
// 3级护理
nursingLevel_3: '',
// 呼吸机使用
use_vent_flag: '',
// 有创呼吸机使用小时
vent_used_dura: '',
// 手术表
surgery_tableData: [],
},
// 病案首页3
// 住院费用
hospitalization: {
// 总费用
medfee_sumamt: '',
// 自付金额
selfpay_amt: '',
},
// 综合医疗服务类
medicalServices: {
// 一般医疗服务类
ordn_med_servfee: '',
// 一般治疗操作费
ordn_trt_oprt_fee: '',
// 护理费
nurs_fee: '',
// 其他费用
com_med_serv_oth_fee: '',
},
// 诊断类
diagnosisClass: {
// 病理诊断
palg_diag_fee: '',
// 实验室诊断
lab_diag_fee: '',
// 影像学诊断
rdhy_diag_fee: '',
// 临床诊断
clnc_dise_fee: '',
},
// 治疗类
treatmentClass: {
// 非手术治疗项目费
nsrgtrt_item_fee: '',
// 临床物理治疗
clnc_phys_trt_fee: '',
// 手术治疗费
rgtrt_trt_fee: '',
// 麻醉费
anst_fee: '',
// 手术费
rgtrt_fee: '',
},
// 康复类
recoveryClass: {
// 康复费
rhab_fee: '',
},
// 中医类
TCMClass: {
// 中医治疗费
tcm_trt_fee: '',
},
// 西药类
WesternClass: {
// 西药费
wm_fee: '',
// 抗菌药物费
abtl_medn_fee: '',
},
// 中药类
chineseClass: {
//中成药
tcmpat_fee: '',
// 中草药
tcmherb_fee: '',
},
// 血液和血液制品类
bloodClass: {
// 血费
blo_fee: '',
// 蛋白类制品费
albu_fee: '',
// 球蛋白制品费
glon_fee: '',
// 凝血因子制品费
clotfac_fee: '',
// 细胞因子制品费
cyki_fee: '',
},
// 耗材类
consumablesClass: {
// 检查用一次性医用材料费
exam_dspo_matl_fee: '',
// 治疗用一次性医用材料费
trt_dspo_matl_fee: '',
// 手术用一次性医用材料费
oprn_dspo_matl_fee: '',
},
// 其他类
otherClass: {
// 其他费用
oth_fee: '',
},
// 其他诊断及手术附加页
other_tableData: [],
// 手术操作数组
surgery_tableData: [],
});
export default formData;

View File

@@ -0,0 +1,256 @@
<template>
<div class="med-summary-container">
<div style="width: 40%">
<el-card style="height: 80vh">
<template #header>
{{ '汇总单' }}
</template>
<div style="display: flex; justify-content: space-between; margin-bottom: 10px">
<div>
<el-input style="width: 250px" v-model="queryParams.searchKey" placeholder="单据号">
<template #append>
<el-button icon="Search" @click="getSummaryList" />
</template>
</el-input>
<el-select
placeholder="发放状态"
style="width: 250px; margin-left: 10px"
v-model="queryParams.statusEnum"
@change="getSummaryList"
>
<el-option
v-for="item in statusEnumOptions"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</div>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 10px">
<div>
<el-date-picker
v-model="queryParams.applyTime"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 510px"
value-format="YYYY-MM-DD"
:clearable="false"
@change="getSummaryList"
/>
</div>
<div>
<el-button type="primary" plain @click="handleSend">批量发药</el-button>
<el-button type="warning" plain>批量作废</el-button>
</div>
</div>
<el-table
:data="summaryList"
max-height="85vh"
border
ref="summaryTableRef"
highlight-current-row
@row-click="getDetails"
>
<el-table-column type="selection" align="center" width="50" />
<el-table-column prop="busNo" label="单据号" align="center" width="150" />
<el-table-column prop="applicantName" label="申请人" align="center" width="100" />
<el-table-column prop="locationName" label="发药药房" align="center" />
<el-table-column prop="statusEnum_enumText" label="状态" align="center" />
<el-table-column prop="applyTime" label="汇总日期" align="center" width="140">
<template #default="scope">
{{ scope.row.applyTime ? parseTime(scope.row.applyTime, '{y}-{m}-{d}') : '-' }}
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="primary" link @click="handleSend(scope.row)">发药</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<!-- <el-row :gutter="10" justify="end" align="middle" style="margin-bottom: 10px">
<el-button type="danger" plain icon="Refresh" @click="handleSendDrug" :disabled="!busNo">
发药
</el-button>
<el-button type="primary" plain icon="Download" @click="handleExport" :disabled="!busNo">
导出
</el-button>
</el-row> -->
<div style="width: 59%">
<el-card style="height: 80vh">
<template #header>
{{ '汇总单详情' }}
</template>
<el-table
:data="summaryDetailsData"
style="width: 100%"
border
v-loading="loading"
:cell-style="{ textAlign: 'center' }"
>
<el-table-column type="index" label="序号" min-width="50" />
<el-table-column prop="itemName" label="项目名称" min-width="150">
<template #default="scope">
{{ scope.row.itemName || '-' }}
</template>
</el-table-column>
<el-table-column prop="totalVolume" label="规格" min-width="120">
<template #default="scope">
{{ scope.row.totalVolume || '-' }}
</template>
</el-table-column>
<el-table-column prop="lotNumber" label="批次号" min-width="100">
<template #default="scope">
{{ scope.row.lotNumber || '-' }}
</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" min-width="80" align="center">
<template #default="scope">
{{ scope.row.itemQuantity + ' ' + scope.row.minUnitCode_dictText }}
</template>
</el-table-column>
<el-table-column prop="categoryCode_dictText" label="药品类型" min-width="100">
<template #default="scope">
{{ scope.row.categoryCode_dictText || '-' }}
</template>
</el-table-column>
<el-table-column
prop="manufacturerText"
label="生产厂家"
min-width="120"
:show-overflow-tooltip="true"
>
<template #default="scope">
{{ scope.row.manufacturerText || '-' }}
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import {
totalSendDrug,
getFromSummaryList,
getFromSummaryDetails,
getFromSummaryInit,
} from './api.js';
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance();
const statusEnumOptions = ref([]);
const summaryList = ref([]);
const queryParams = ref({
applyTime: [
proxy.formatDateStr(new Date().setMonth(new Date().getMonth() - 1), 'YYYY-MM-DD'),
proxy.formatDateStr(new Date(), 'YYYY-MM-DD'),
],
});
// 定义组件属性
const props = defineProps({
tableData: {
type: Array,
default: () => [],
},
selectedId: {
type: String,
default: '',
},
busNo: {
type: String,
default: '',
},
});
const selectedRows = ref([]);
const summaryDetailsData = ref([]);
// 定义loading状态
const loading = ref(false);
getSummaryList();
// 获取汇总单信息
function getSummaryList() {
queryParams.value.applyTimeSTime = queryParams.value.applyTime[0] + ' 00:00:00';
queryParams.value.applyTimeETime = queryParams.value.applyTime[1] + ' 23:59:59';
getFromSummaryList(queryParams.value).then((res) => {
summaryList.value = res.data.records;
});
}
function getDetails(row) {
loading.value = true;
getFromSummaryDetails({ summaryNo: row.busNo }).then((res) => {
summaryDetailsData.value = res.data;
loading.value = false;
});
}
// 发药
function handleSend(row) {
let sendList = [];
if (row.busNo) {
sendList.push(row.busNo);
} else {
proxy.$refs['summaryTableRef'].getSelectionRows().forEach((item) => {
sendList.push(item.busNo);
});
}
console.log(sendList);
totalSendDrug(sendList).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
getSummaryList();
}
});
}
// 获取发药状态
const getStatusOption = async () => {
try {
const res = await getFromSummaryInit();
statusEnumOptions.value = res.data.dispenseStatusOptions;
} catch (error) {}
};
getStatusOption();
// 定义暴露给父组件的数据和方法
defineExpose({
selectedRows,
});
</script>
<style lang="scss" scoped>
.med-summary-container {
height: 100%;
display: flex;
justify-content: space-between;
}
.medicationTableDetail {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
.buttonGroup {
display: flex;
justify-content: flex-end;
padding: 10px 0;
gap: 10px;
margin-right: 20px;
:deep(.el-button) {
padding: 6px 16px;
font-size: 14px;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More