Merge branch 'merge_1.3' into develop

This commit is contained in:
2025-12-30 09:38:57 +08:00
1345 changed files with 220529 additions and 98778 deletions

View File

@@ -0,0 +1,8 @@
# 页面标题
VITE_APP_TITLE =医院信息管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
# OpenHIS管理系统/开发环境
VITE_APP_BASE_API = '/dev-api'

View File

@@ -21,4 +21,3 @@ selenium-debug.log
package-lock.json
yarn.lock
vite.config.js

View File

@@ -1,116 +0,0 @@
// 表单数据
const formData = reactive({
admission: {
confirmDate: '2023年10月28日',
dischargeTime: '2023年11月13日 08时14分',
hospitalDays: '17'
},
diagnosis: {
mainDiagnosis: '腰椎间盘突出症(L4-5)',
otherDiagnosis: ''
}
});
// 打印表单
const printForm = () => {
// 创建一个新的打印窗口
const printWindow = window.open('', '_blank');
// 构建打印内容
const printContent = `
<!DOCTYPE html>
<html>
<head>
<title>住院病案首页</title>
<style>
/* ... existing code ... */
.form-row {
display: flex;
flex-wrap: wrap;
margin-bottom: 15px;
justify-content: space-between;
}
.form-item {
flex: 1;
min-width: 200px;
margin-right: 15px;
margin-bottom: 10px;
}
.form-item.full-width {
flex: 0 0 100%;
min-width: 100%;
}
.barcode {
width: 240px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="form-header">
<div class="form-title">吉林大学第一医院</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<div style="font-size: 12px;text-align: left;">
<div>组织机构代码:(${formData.hospital.orgCode || ''}</div>
<div>医疗付费方式:(${formData.hospital.paymentMethod || ''}</div>
</div>
<div style="font-size: 20px;font-weight: 700;">
住院病案首页
</div>
<div class="barcode" id="barcode"></div>
</div>
</div>
<!-- 医院信息、患者基本信息、住院信息、诊断信息等部分保持不变 -->
<!-- 住院信息 -->
<div class="section">
<div class="section-title">住院信息</div>
<div class="form-row">
<div class="form-item">
<label>入院时间:</label>
<div class="form-value">${formData.admission.admitTime || ''}</div>
</div>
<div class="form-item">
<label>入科时间:</label>
<div class="form-value">${formData.admission.departmentAdmitTime || ''}</div>
</div>
<div class="form-item">
<label>出院时间:</label>
<div class="form-value">${formData.admission.dischargeTime || ''}</div>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label>实际住院天数:</label>
<div class="form-value">${formData.admission.hospitalDays || ''}</div>
</div>
</div>
</div>
<!-- 其他部分保持不变 -->
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<script>
window.onload = function() {
JsBarcode("#barcode").init();
JsBarcode("#barcode").encode("12979667");
window.print();
window.onafterprint = function() {
window.close();
}
}
</script>
</body>
</html>
`;
// 将内容写入打印窗口并打印
printWindow.document.write(printContent);
printWindow.document.close();
};

View File

@@ -13,7 +13,7 @@
<meta name="author" content="OpenHIS Team" />
<!-- 安全相关 meta 标签 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://at.alicdn.com; style-src 'self' 'unsafe-inline' https://at.alicdn.com; img-src 'self' data: https: https://at.alicdn.com; font-src 'self' 'unsafe-inline' https://at.alicdn.com data:; connect-src 'self' https://at.alicdn.com;">
<meta name="referrer" content="no-referrer-when-downgrade">
<!-- 移动端和 PWA 支持 -->

8958
openhis-ui-vue3/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -21,11 +21,11 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "^11.0.0",
"area-data": "^5.0.6",
"axios": "^1.7.0",
"chart.js": "^4.5.1",
"@vueuse/core": "10.6.1",
"axios": "0.27.2",
"china-division": "^2.7.0",
"d3": "^7.9.0",
"dayjs": "^1.11.19",
"decimal.js": "^10.5.0",
"echarts": "^5.4.3",
"element-china-area-data": "^6.1.0",
@@ -36,6 +36,7 @@
"js-cookie": "^3.0.5",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"moment": "^2.30.1",
"next": "^16.1.0",
"nprogress": "^0.2.0",
@@ -53,14 +54,16 @@
"vue-router": "^4.3.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"@vue/compiler-sfc": "^3.5.25",
"sass": "^1.97.1",
"sass-loader": "^16.0.6",
"unplugin-auto-import": "^0.19.0",
"unplugin-vue-setup-extend-plus": "^1.0.0",
"vite": "^7.3.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-svg-icons": "^2.0.1"
"@types/node": "^25.0.1",
"@vitejs/plugin-vue": "4.5.0",
"@vue/compiler-sfc": "3.3.9",
"sass": "1.69.5",
"typescript": "^5.9.3",
"unplugin-auto-import": "0.17.1",
"unplugin-vue-setup-extend-plus": "1.0.0",
"vite": "5.0.4",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"vue-tsc": "^3.1.8"
}
}

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);
}

View File

@@ -1,73 +1,98 @@
import request from '@/utils/request'
import request from '@/utils/request';
// 获取追溯码
export function searchTraceNo(data) {
return request({
url:'/app-common/search-trace-no',
method: 'get',
params: data,
})
return request({
url: '/app-common/search-trace-no',
method: 'get',
params: data,
});
}
// 获取处方打印数据
export function advicePrint(data) {
return request({
url:'/app-common/advice-print',
method: 'get',
params: data,
})
return request({
url: '/app-common/advice-print',
method: 'get',
params: data,
});
}
// 获取全部科室列表
export function getOrgList(data) {
return request({
url:'/app-common/department-list',
method: 'get',
params: data,
})
return request({
url: '/app-common/department-list',
method: 'get',
params: data,
});
}
// 获取全部病区列表
export function getWardList(data) {
return request({
url:'/app-common/ward-list',
method: 'get',
params: data,
})
return request({
url: '/app-common/ward-list',
method: 'get',
params: data,
});
}
// 获取全部供应商列表
export function getSupplierList(data) {
return request({
url:'/app-common/supplier',
method: 'get',
params: data,
})
return request({
url: '/app-common/supplier',
method: 'get',
params: data,
});
}
/**
* 获取项目定价方式,批次售价/统一售价
*/
export function getAdjustPriceSwitchState(params) {
return request({
url: '/change/price/getAdjustPriceSwitchState',
method: 'get',
params: params,
});
}
/**
* 批次号匹配
*/
export function lotNumberMatch(params) {
return request({
url: '/app-common/lot-number-match',
method: 'get',
params: params,
});
}
import axios from 'axios';
const env = import.meta.env.MODE;
export function invokeYbPlugin(data) {
if(env == 'development'){
return axios.create(
{
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: '/ybplugin',//ybplugin
// 超时
timeout: 60000
}
).post('/api/data/', data);
export function invokeYbPlugin5001(data) {
} else {
return axios.create(
{
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: '',
// 超时
timeout: 60000
}
).post('http://localhost:5000/api/data/', data);
return axios
.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: '',
// 超时
timeout: 60000,
})
.post('http://localhost:5001/api/data/', data);
}
// }
export function invokeYbPlugin5000(data) {
return axios
.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: '',
// 超时
timeout: 60000,
})
.post('http://localhost:5000/api/data/', data);
}

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

@@ -1,32 +1,36 @@
<template>
<div
:class="{ 'has-logo': showLogo }"
:style="{
backgroundColor:
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
}"
class="sidebar-layout"
>
:class="{ 'has-logo': showLogo }"
class="sidebar-wrapper"
:style="{
backgroundColor:
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
}"
>
<!-- <logo v-if="showLogo" :collapse="isCollapse" /> -->
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground
"
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
:unique-opened="true"
:active-text-color="theme"
:collapse-transition="false"
mode="horizontal"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
<div class="menu-container">
<div class="menu-scrollbar">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground
"
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
:unique-opened="true"
:active-text-color="theme"
:collapse-transition="false"
mode="horizontal"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
</div>
</div>
<navbar @setLayout="setLayout" class="navbar-container" />
<settings ref="settingRef" />
</div>
@@ -69,16 +73,61 @@ function setLayout() {
</script>
<style lang="scss" scoped>
/* 移除滚动条样式 */
.sidebar-wrapper {
display: flex;
flex-direction: row;
align-items: center;
height: 50px;
padding: 0;
width: 100%;
overflow: hidden;
position: relative;
}
.menu-container {
flex: 1;
height: 50px;
min-width: 0;
overflow: hidden;
position: relative;
display: flex;
align-items: center;
}
.menu-scrollbar {
height: 50px;
width: 100%;
overflow-x: auto;
overflow-y: hidden;
display: flex;
align-items: center;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar:vertical {
display: none;
}
&::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
}
.el-menu--horizontal {
display: flex !important;
align-items: center !important;
border-bottom: none !important;
background-color: transparent !important;
min-width: auto;
flex-wrap: nowrap;
overflow: hidden !important;
white-space: nowrap;
height: 50px;
& > .el-menu-item,
& > .el-sub-menu {
@@ -87,8 +136,11 @@ function setLayout() {
color: #fff;
padding: 0 15px !important;
font-size: 14px;
min-width: auto !important;
min-width: 120px !important;
flex-shrink: 0;
white-space: nowrap;
display: flex;
align-items: center;
}
:deep(.svg-icon) {
@@ -98,7 +150,9 @@ function setLayout() {
:deep(.el-sub-menu__title) {
padding-right: 25px !important;
white-space: nowrap;
display: flex;
align-items: center;
height: 50px;
}
:deep(.el-sub-menu__icon-arrow) {
@@ -110,28 +164,37 @@ function setLayout() {
}
}
/* 水平布局,并与 Navbar 正确配合 */
.sidebar-layout {
display: flex;
flex-direction: row;
align-items: center;
.navbar-container {
flex-shrink: 0;
height: 50px;
padding: 0;
width: 100%;
z-index: 1002;
}
& > .el-menu {
/* 响应式处理 */
@media (max-width: 768px) {
.menu-container {
flex: 1;
height: 50px;
min-width: 0;
}
&.has-logo {
display: flex;
flex-direction: row;
align-items: center;
height: 50px;
padding: 0;
width: 100%;
.el-menu--horizontal {
& > .el-menu-item,
& > .el-sub-menu {
padding: 0 12px !important;
min-width: 80px !important;
font-size: 12px;
}
}
}
@media (max-width: 480px) {
.el-menu--horizontal {
& > .el-menu-item,
& > .el-sub-menu {
padding: 0 8px !important;
min-width: 60px !important;
font-size: 11px;
}
}
}
</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

@@ -1,123 +1,112 @@
import { createApp } from 'vue'
import { createApp } from 'vue';
// 修复 util._extend 已弃用警告(仅在 Node.js 环境中需要)
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
try {
import('util').then(util => {
if (!util._extend) {
util._extend = function(destination, source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = source[key];
}
}
return destination;
};
}
});
} catch (e) {
console.error('util._extend 补丁加载失败:', e);
}
}
import Cookies from 'js-cookie';
import Cookies from 'js-cookie'
// 导入 hiprint 并挂载到全局 window 对象
import { hiprint } from 'vue-plugin-hiprint';
window.hiprint = hiprint;
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import 'element-plus/dist/index.css';
import locale from 'element-plus/es/locale/lang/zh-cn';
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/index.scss'; // global css
import App from './App'
import store from './store'
import router from './router'
import directive from './directive' // directive
import App from './App';
import store from './store';
import router from './router';
import directive from './directive'; // directive
// 注册指令
import plugins from './plugins' // plugins
import { download, downloadGet } from '@/utils/request'
import plugins from './plugins'; // plugins
import { download, downloadGet } from '@/utils/request';
// svg图标
import 'virtual:svg-icons-register'
import SvgIcon from '@/components/SvgIcon'
import elementIcons from '@/components/SvgIcon/svgicon'
import 'virtual:svg-icons-register';
import SvgIcon from '@/components/SvgIcon';
import elementIcons from '@/components/SvgIcon/svgicon';
import './permission' // permission control
import './permission'; // permission control
import { useDict } from '@/utils/dict'
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels} from '@/utils/openhis'
import { useDict } from '@/utils/dict';
import {
parseTime,
resetForm,
addDateRange,
handleTree,
selectDictLabel,
selectDictLabels,
} from '@/utils/openhis';
import { formatDateStr } from '@/utils/index';
// 分页组件
import Pagination from '@/components/Pagination'
import Pagination from '@/components/Pagination';
// 自定义表格工具组件
import RightToolbar from '@/components/RightToolbar'
import RightToolbar from '@/components/RightToolbar';
// 富文本组件
import Editor from "@/components/Editor"
import Editor from '@/components/Editor';
// 文件上传组件
import FileUpload from "@/components/FileUpload"
import FileUpload from '@/components/FileUpload';
// 图片上传组件
import ImageUpload from "@/components/ImageUpload"
import ImageUpload from '@/components/ImageUpload';
// 图片预览组件
import ImagePreview from "@/components/ImagePreview"
import ImagePreview from '@/components/ImagePreview';
// 自定义树选择组件
import TreeSelect from '@/components/TreeSelect'
import TreeSelect from '@/components/TreeSelect';
// 字典标签组件
import DictTag from '@/components/DictTag'
import DictTag from '@/components/DictTag';
// 导入请求工具
import request from './utils/request'
import { ElDialog, ElMessage } from 'element-plus';
import {registerComponents} from './template';
import { registerComponents } from './template';
const app = createApp(App);
if(chrome.webview !== undefined) {
if (chrome.webview !== undefined) {
// 如果是webview环境挂载CSharpAccessor对象到vue实例上
const csAccessor = chrome.webview.hostObjects.CSharpAccessor;
app.config.globalProperties.csAccessor = csAccessor;
}
// 全局方法挂载
app.config.globalProperties.useDict = useDict
app.config.globalProperties.download = download
app.config.globalProperties.downloadGet = downloadGet
app.config.globalProperties.parseTime = parseTime
app.config.globalProperties.resetForm = resetForm
app.config.globalProperties.handleTree = handleTree
app.config.globalProperties.addDateRange = addDateRange
app.config.globalProperties.selectDictLabel = selectDictLabel
app.config.globalProperties.selectDictLabels = selectDictLabels
app.config.globalProperties.formatDateStr = formatDateStr
// 全局挂载请求实例
app.config.globalProperties.$http = request
app.config.globalProperties.useDict = useDict;
app.config.globalProperties.download = download;
app.config.globalProperties.downloadGet = downloadGet;
app.config.globalProperties.parseTime = parseTime;
app.config.globalProperties.resetForm = resetForm;
app.config.globalProperties.handleTree = handleTree;
app.config.globalProperties.addDateRange = addDateRange;
app.config.globalProperties.selectDictLabel = selectDictLabel;
app.config.globalProperties.selectDictLabels = selectDictLabels;
app.config.globalProperties.formatDateStr = formatDateStr;
// 全局组件挂载
app.component('DictTag', DictTag)
app.component('Pagination', Pagination)
app.component('TreeSelect', TreeSelect)
app.component('FileUpload', FileUpload)
app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
app.component('DictTag', DictTag);
app.component('Pagination', Pagination);
app.component('TreeSelect', TreeSelect);
app.component('FileUpload', FileUpload);
app.component('ImageUpload', ImageUpload);
app.component('ImagePreview', ImagePreview);
app.component('RightToolbar', RightToolbar);
app.component('Editor', Editor);
app.use(registerComponents);
app.use(ElMessage);
app.use(router);
app.use(store);
app.use(plugins);
app.use(elementIcons);
app.component('svg-icon', SvgIcon);
directive(app);
// 全局禁止点击遮罩层关闭弹窗
ElDialog.props.closeOnClickModal.default = false;
// 使用element-plus 并且设置全局的大小
app.use(ElementPlus, {
locale: zhCn,
// 支持 large、default、small
size: Cookies.get('size') || 'default'
})
app.use(ElMessage)
app.use(registerComponents)
app.use(router)
app.use(store)
app.use(plugins)
app.use(elementIcons)
app.component('svg-icon', SvgIcon)
directive(app)
// 全局禁止点击遮罩层关闭弹窗
ElDialog.props.closeOnClickModal.default = false;
size: Cookies.get('size') || 'default',
});
app.mount('#app')
app.mount('#app');

View File

@@ -1,6 +1,7 @@
import { createWebHistory, createRouter } from 'vue-router'
import { createWebHistory, createRouter } from 'vue-router';
/* Layout */
import Layout from '@/layout'
import Layout from '@/layout';
import { path } from 'd3';
/**
* Note: 路由配置项说明
@@ -34,27 +35,31 @@ export const constantRoutes = [
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
component: () => import('@/views/redirect/index.vue'),
},
],
},
// 登录路由
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
hidden: true,
},
// 注册路由
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
hidden: true,
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404'),
hidden: true,
},
// 401权限不足路由
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
hidden: true,
},
// 首页路由
{
@@ -66,11 +71,10 @@ export const constantRoutes = [
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
meta: { title: '首页', icon: 'dashboard', affix: true },
},
],
},
// 个人中心路由
{
path: '/user',
component: Layout,
@@ -81,38 +85,15 @@ export const constantRoutes = [
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
meta: { title: '个人中心', icon: 'user' },
},
],
},
// 套餐管理相关路由 - 添加到公共路由确保始终可用
{
path: '/maintainSystem/Inspection/PackageManagement',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
name: 'DirectPackageManagement',
meta: { title: '套餐管理' }
}
]
path: '/tpr',
component: () => import('@/views/inpatientNurse/tprsheet/index.vue'),
},
// {
// path: '/reservationRecord2/appoinmentmanage',
// component: Layout,
// hidden: true,
// children: [
// {
// path: '',
// component: () => import('@/views/appoinmentmanage/clinicRoom/index.vue'),
// name: 'DirectClinicRoom',
// meta: { title: '门诊出诊医生诊室设置' }
// }
// ]
// }
]
];
// 动态路由 - 基于用户权限动态加载的路由
export const dynamicRoutes = [
@@ -257,9 +238,9 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setUser'),
name: 'SetUser',
meta: { title: '所属用户', activeMenu: '/system/tenant' }
}
]
meta: { title: '所属用户', activeMenu: '/system/basicmanage/tenant' },
},
],
},
//租户合同管理路由
{
@@ -272,130 +253,81 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setContract'),
name: 'SetContract',
meta: { title: '合同管理', activeMenu: '/system/tenant' }
}
]
meta: { title: '合同管理', activeMenu: '/system/basicmanage/tenant' },
},
],
},
// 用户角色分配路由
// {
// path: '/system/user-auth',
// component: Layout,
// hidden: true,
// permissions: ['system:user:edit'],
// children: [
// {
// path: 'role/:userId(\\d+)',
// component: () => import('@/views/system/user/authRole'),
// name: 'AuthRole',
// meta: { title: '分配角色', activeMenu: '/system/user' }
// }
// ]
// },
// // 角色用户分配路由
// {
// path: '/system/role-auth',
// component: Layout,
// hidden: true,
// permissions: ['system:role:edit'],
// children: [
// {
// path: 'user/:roleId(\\d+)',
// component: () => import('@/views/system/role/authUser'),
// name: 'AuthUser',
// meta: { title: '分配用户', activeMenu: '/system/role' }
// }
// ]
// },
// // 字典数据路由
// {
// path: '/system/dict-data',
// component: Layout,
// hidden: true,
// permissions: ['system:dict:list'],
// children: [
// {
// path: 'index/:dictId(\\d+)',
// component: () => import('@/views/system/dict/data'),
// name: 'Data',
// meta: { title: '字典数据', activeMenu: '/system/dict' }
// }
// ]
// },
// 系统监控路由
// {
// path: '/monitor',
// component: Layout,
// redirect: '/monitor/operlog',
// name: 'Monitor',
// meta: { title: '系统监控', icon: 'monitor' },
// children: [
// {
// path: 'operlog', // 操作日志路由
// component: () => import('@/views/monitor/operlog/index.vue'),
// name: 'Operlog',
// meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
// },
// {
// path: 'logininfor', // 登录日志路由
// component: () => import('@/views/monitor/logininfor/index.vue'),
// name: 'Logininfor',
// meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
// },
// {
// path: 'job', // 定时任务路由
// component: () => import('@/views/monitor/job/index.vue'),
// name: 'Job',
// meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
// }
// ]
// },
// 系统工具路由
// {
// path: '/tool',
// component: Layout,
// redirect: '/tool/gen',
// name: 'Tool',
// meta: { title: '系统工具', icon: 'tool' },
// children: [
// {
// path: 'gen', // 代码生成路由
// component: () => import('@/views/tool/gen/index.vue'),
// name: 'Gen',
// meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
// }
// ]
// },
// 定时任务日志路由
// {
// path: '/monitor/job-log',
// component: Layout,
// hidden: true,
// permissions: ['monitor:job:list'],
// children: [
// {
// path: 'index/:jobId(\\d+)',
// component: () => import('@/views/monitor/job/log'),
// name: 'JobLog',
// meta: { title: '调度日志', activeMenu: '/monitor/job' }
// }
// ]
// },
// 代码生成编辑路由
// {
// path: '/tool/gen-edit',
// component: Layout,
// hidden: true,
// permissions: ['tool:gen:edit'],
// children: [
// {
// path: 'index/:tableId(\\d+)',
// component: () => import('@/views/tool/gen/editTable'),
// name: 'GenEdit',
// meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
// }
// ]
// }
]
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' },
},
],
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' },
},
],
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' },
},
],
},
{
path: '/monitor/job-log',
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' },
},
],
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' },
},
],
},
];
// 合并常量路由和动态路由,确保所有路由都能被访问
const allRoutes = [...constantRoutes, ...dynamicRoutes];
@@ -414,9 +346,9 @@ const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 页面滚动行为:如果有保存的位置则恢复,否则滚动到顶部
if (savedPosition) {
return savedPosition
return savedPosition;
} else {
return { top: 0 }
return { top: 0 };
}
},
});

View File

@@ -19,8 +19,7 @@ const useUserStore = defineStore(
roles: [],
permissions: [],
tenantId: '',
hospitalName:'',
status: '' // 用户状态0-启用(管理员), 1-禁用(普通人员)
hospitalName:''
}),
actions: {
// 登录
@@ -65,7 +64,6 @@ const useUserStore = defineStore(
this.avatar = avatar
this.hospitalName = res.optionJson.hospitalName
this.status = user.status // 保存用户状态
resolve(res)
}).catch(error => {
reject(error)

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

@@ -1,24 +1,17 @@
<template>
<!-- 跌倒/坠床评估护理记录单主容器 -->
<div>
<div class="business">
<!-- 已有记录展示表格 -->
<el-table
<!-- <el-table
:data="tableDataSource"
border
stripe
fit
:header-cell-style="{ background: '#f2f2f2', color: 'black' }"
>
<!-- 记录时间列 -->
<el-table-column prop="content.recordTime" label="记录时间" />
<!-- 评估分数列 -->
<el-table-column prop="content.totalScore" label="评估分数" />
<!-- 护理措施列 -->
<el-table-column prop="content.patientCareSessionsTableList" label="护理措施" />
<!-- 责任护士列 -->
<el-table-column prop="content.nurseSignature" label="责任护士" />
<!-- 操作列编辑和删除按钮 -->
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -37,18 +30,15 @@
@click="handleDelete(scope.row)"
:disabled="admissionDataForm !== undefined"
>
删除
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-table> -->
<!-- 跌倒/坠床评估护理记录单表单区域 -->
<div name="跌倒/坠床评估护理记录单" class="changeMajor" style="width: 99.9%">
<div>
<!-- 表单主体 -->
<el-form ref="formRef" :model="form" style="width: 99.9%">
<!-- 标题行 -->
<el-form-item style="text-align: center">
<div
style="
@@ -64,12 +54,10 @@
</div>
</el-form-item>
<!-- 日期时间选择器 -->
<el-form-item label="日期:" class="changeMajorFromItem" style="width: 100%">
<el-row :span="20">
<el-col :span="8" style="padding-left: 0px !important">
<el-form-item>
<!-- 日期时间选择器 -->
<el-date-picker
v-model="form.ZKDATE"
type="datetime"
@@ -79,7 +67,6 @@
style="width: 800px"
:disabled="admissionDataForm !== undefined"
/>
<!-- 时间选择器被注释掉 -->
<!-- <span style="margin-left: 5px">时间</span>
<el-time-picker
v-model="form.ZKTIME"
@@ -90,7 +77,6 @@
/> -->
</el-form-item>
</el-col>
<!-- 新增/保存按钮 -->
<el-col :span="5">
<el-button
v-if="!updateFlag"
@@ -114,7 +100,6 @@
</el-row>
</el-form-item>
<!-- 危险因素评估表格 -->
<el-form-item style="padding-top: 10px; margin: 0px !important">
<el-table
:data="dangerData"
@@ -122,7 +107,6 @@
:span-method="handleSpan"
style="text-align: center"
>
<!-- 动态生成表格列 -->
<el-table-column
v-for="column in dangerColumns"
:key="column.key"
@@ -131,7 +115,6 @@
:label="column.title"
align="center"
/>
<!-- 选择列复选框 -->
<el-table-column prop="id" label="选择" width="80" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.checked" @change="handleDangerChange(row)" />
@@ -140,7 +123,6 @@
</el-table>
</el-form-item>
<!-- 总分显示 -->
<el-form-item
style="text-align: center; margin-bottom: 0px; padding: 0px"
class="changeMajorFromItem"
@@ -153,7 +135,6 @@
</el-row>
</el-form-item>
<!-- 护理措施表格 -->
<el-form-item style="padding-top: 10px">
<el-table
:data="nursingData"
@@ -161,7 +142,6 @@
:span-method="arraySpanMethod"
style="width: 100%"
>
<!-- 动态生成表格列 -->
<el-table-column
v-for="column in nursingColumns"
:key="column.key"
@@ -170,7 +150,6 @@
:label="column.title"
align="center"
/>
<!-- 选择列复选框 -->
<el-table-column prop="id" label="选择" width="80" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.checked" @change="handleNursingChange(row)" />
@@ -179,7 +158,6 @@
</el-table>
</el-form-item>
<!-- 护士签字输入框 -->
<el-form-item
style="text-align: center; margin-bottom: 0px; padding: 0px"
class="changeMajorFromItem"
@@ -199,7 +177,6 @@
</el-row>
</el-form-item>
<!-- 备注信息 -->
<el-form-item>
<el-row :span="20">
<el-col :span="5">
@@ -220,24 +197,30 @@
</template>
<script setup>
// 组件选项定义
defineOptions({
name: '跌倒/坠床评估护理记录单',
name: 'FallBedFallAssessment',
});
// 导入所需模块
import { ref, reactive, computed, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { useRoute, useRouter } from 'vue-router';
import { patientInfo } from '../views/doctorstation/components/store/patient';
// 使用路由相关功能
// 定义props和emits
const props = defineProps({
patientInfo: {
type: Object,
default: () => ({}),
},
});
const emits = defineEmits(['submitOk']);
// 响应式数据
const route = useRoute();
const router = useRouter();
// 定义引用变量
const queryRef = ref();
const formRef = ref();
// 基本数据变量
const wardCode = ref('');
const patientId = ref('');
const visitId = ref('');
@@ -251,18 +234,17 @@ const totalScore = ref(0);
const lastSubmit = ref('');
const admissionDataForm = ref(route.params.admissionData);
// 表单数据模型
const form = reactive({
ZKDATE: '', // 日期
ZKTIME: '', // 时间
recordTime: '', // 记录时间
totalScore: 0, // 总分
bedFallRiskAssessmentList: [], // 跌倒风险评估列表
nurseSignature: '', // 护士签名
patientCareSessionsCheckedList: [], // 护理措施选中列表
ZKDATE: '',
ZKTIME: '',
recordTime: '',
totalScore: 0,
bedFallRiskAssessmentList: [],
nurseSignature: '',
patientCareSessionsCheckedList: [],
});
// 危险因素表格列配置
// 危险因素表格列
const dangerColumns = [
{
key: 'content',
@@ -327,7 +309,7 @@ const dangerData = ref([
{ id: '27', evalContent: '麻醉止痛剂', score: 2, checked: false },
]);
// 护理措施表格列配置
// 护理措施表格列
const nursingColumns = [
{
key: 'content',
@@ -364,21 +346,21 @@ const instructions = [
'3.评分≥5高度风险每周至少评估一次需采取适宜的预防措施同时填写《预防患者跌倒/坠床知情告知书》',
];
// 计算属性:计算选中的危险因素总分
// 计算属性
const calculate = computed(() => {
return dangerData.value
.filter((option) => option.checked)
.reduce((total, option) => total + option.score, 0);
});
// 计算属性:判断表单是否为空
const isFormEmpty = computed(() => {
return (
form.ZKDATE === '' && form.ZKTIME === '' && form.recordTime === '' && form.nurseSignature === ''
);
});
// 危险因素选择变化处理函数
// 方法 - 不再需要handleData方法通过表单输入和按钮加载数据
const handleDangerChange = (row) => {
totalScore.value = calculate.value;
form.bedFallRiskAssessmentList = dangerData.value
@@ -386,14 +368,12 @@ const handleDangerChange = (row) => {
.map((item) => item.id);
};
// 护理措施选择变化处理函数
const handleNursingChange = (row) => {
form.patientCareSessionsCheckedList = nursingData.value
.filter((item) => item.checked)
.map((item) => item.id);
};
// 初始化函数:加载模拟数据
const init = async () => {
// 使用模拟数据不再调用后端API
try {
@@ -455,7 +435,6 @@ const init = async () => {
}
};
// 危险因素表格合并单元格处理函数
const handleSpan = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex === 0) {
if (rowIndex === 0) {
@@ -502,7 +481,6 @@ const handleSpan = ({ row, column, rowIndex, columnIndex }) => {
return [1, 1];
};
// 护理措施表格合并单元格处理函数
const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
// 护理措施
if (columnIndex === 0) {
@@ -519,9 +497,8 @@ const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
return [1, 1];
};
// 提交表单处理函数
const onSubmit = async () => {
// 检查上次提交时间,防止重复提交
// 检查上次提交时间
if (lastSubmit.value && new Date() - lastSubmit.value < 2000) {
ElMessage.error('禁止重复提交!');
return;
@@ -546,29 +523,48 @@ const onSubmit = async () => {
if (isFormEmpty.value) {
ElMessage.error('请填写跌倒/坠床评估护理记录单后再进行操作');
} else {
form.totalScore = totalScore.value;
return;
}
// 模拟保存数据不再调用后端API
console.log('保存的数据:', form);
// 表单验证通过,收集数据
form.totalScore = totalScore.value;
try {
// 准备保存的数据
const saveData = {
...form,
// 添加患者相关信息
patientId: patientId.value,
visitId: visitId.value,
wardCode: wardCode.value,
// 确保选中项数组存在
bedFallRiskAssessmentList: form.bedFallRiskAssessmentList || [],
patientCareSessionsCheckedList: form.patientCareSessionsCheckedList || [],
};
// 提交表单数据
console.log('提交保存的数据:', saveData);
emits('submitOk', saveData);
// 更新本地数据
if (updateFlag.value) {
// 模拟更新操作
// 更新操作
const updatedIndex = tableDataSource.value.findIndex((item) => item.id === updateId.value);
if (updatedIndex !== -1) {
tableDataSource.value[updatedIndex].content = { ...form };
}
ElMessage.success('模拟更新成功');
ElMessage.success('更新成功');
} else {
// 模拟新增操作
// 新增操作
const newRecord = {
id: Date.now().toString(),
content: { ...form },
};
tableDataSource.value.unshift(newRecord);
ElMessage.success('模拟新增成功');
ElMessage.success('保存成功');
}
// 处理返回逻辑
if (admissionDataForm.value !== undefined) {
const admissionDataBack = JSON.parse(admissionDataForm.value);
admissionDataBack.project2 = totalScore.value;
@@ -589,11 +585,14 @@ const onSubmit = async () => {
}
}
// 重置表单
reset();
} catch (error) {
console.error('保存失败:', error);
ElMessage.error('保存失败,请重试');
}
};
// 编辑记录处理函数
const handleUpdate = (row) => {
const loginUser = JSON.parse(window.localStorage.getItem('loginUser'));
@@ -623,7 +622,6 @@ const handleUpdate = (row) => {
}
};
// 重置表单函数
const reset = () => {
Object.assign(form, {
ZKDATE: '',
@@ -656,7 +654,6 @@ const reset = () => {
}
};
// 删除记录处理函数
const handleDelete = (row) => {
const loginUser = JSON.parse(window.localStorage.getItem('loginUser'));
@@ -680,7 +677,6 @@ const handleDelete = (row) => {
}
};
// 打印预览函数(暂未实现)
const dc_ajax_preview = () => {
var args = {
report: urlAddRandomNo('./grf/NurseRecord_Pressure_208.grf'),
@@ -691,7 +687,6 @@ const dc_ajax_preview = () => {
webapp_ws_ajax_run(args);
};
// 数据转换函数,用于报表打印
const transformData = () => {
const jsonDate = [...tableDataSource.value];
@@ -838,7 +833,7 @@ const transformData = () => {
return transformedData;
};
// 组件挂载后执行的生命周期函数
// 生命周期钩子
onMounted(() => {
try {
// 安全获取用户信息
@@ -851,7 +846,13 @@ onMounted(() => {
wardCode.value = window.localStorage.getItem('wardInfo') || '';
admissionDataForm.value = route.params.admissionData;
// 自动初始化表格数据不再依赖患者ID参数
// 获取患者信息
if (patientInfo.value) {
patientId.value = patientInfo.value.patientId || '';
visitId.value = patientInfo.value.visitId || '';
}
// 自动初始化表格数据
// 延迟执行,确保所有数据都已初始化
setTimeout(() => {
init();
@@ -864,15 +865,17 @@ onMounted(() => {
}, 100);
}
});
// 暴露接口
defineExpose({ form, submit: onSubmit, reset });
</script>
<style scoped>
/* 页面样式定义 */
.business {
background: white;
border-radius: 5px;
padding: 10px 16px;
height: calc(100vh - var(--barHeight) * 1px - 50px);
height: calc(100vh - 250px);
overflow: auto;
display: grid;
grid-row-gap: 16px;
@@ -931,10 +934,4 @@ onMounted(() => {
margin-bottom: 0px !important;
}
}
/* 备注信息列表样式 */
.instructions-list {
list-style-type: none;
padding-left: 0;
}
</style>
</style>

View File

@@ -2,20 +2,34 @@
<div class="hospital-record-form">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="病案首页(一)" name="first">
<medicalRecordFirst :formData="formData"></medicalRecordFirst>
<medicalRecordFirst
ref="firstRef"
:formData="formData"
@onCaseFirst="updateCaseFirstDatas"
></medicalRecordFirst>
</el-tab-pane>
<el-tab-pane label="病案首页(二)" name="second">
<medicalRecordSecond :formData="formData"></medicalRecordSecond>
<medicalRecordSecond
:formData="formData"
@onCaseSecond="updateCaseFirstDatas"
></medicalRecordSecond>
</el-tab-pane>
<el-tab-pane label="病案附页()" name="third">
<medicalRecordThird :formData="formData"></medicalRecordThird>
<el-tab-pane label="病案附页()" name="third">
<medicalRecordThird
:formData="formData"
@onCaseThird="updateCaseFirstDatas"
></medicalRecordThird>
</el-tab-pane>
</el-tabs>
<div class="form-footer">
<button @click="printForm" class="print-btn">打印表单</button>
<!-- <button @click="printForm" class="print-btn">打印表单</button> -->
<button @click="resetForm" class="reset-btn">重置表单</button>
</div>
<medicalRecordPrint v-if="isShowprintDom" ref="recordPrintRef"></medicalRecordPrint>
<!-- <el-drawer v-model="drawer" size="100%">
<medicalRecordPrint ref="recordPrintRef"></medicalRecordPrint>
</el-drawer> -->
</div>
</template>
@@ -23,7 +37,8 @@
defineOptions({
name: 'HospitalRecordForm',
});
import { ref, reactive } from 'vue';
import { ref, reactive, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
// import medicalRecordFirst from './components/medicalRecordFirst.vue';
import medicalRecordFirst from '@/views/hospitalRecord/components/medicalRecordFirst.vue';
import medicalRecordSecond from '@/views/hospitalRecord/components/medicalRecordSecond.vue';
@@ -31,119 +46,615 @@ import medicalRecordThird from '@/views/hospitalRecord/components/medicalRecordT
import medicalRecordFirstPrint from '@/views/hospitalRecord/components/medicalRecordFirstPrint.json';
import medicalRecordSecondPrint from '@/views/hospitalRecord/components/medicalRecordSecondPrint.json';
import medicalRecordThirdPrint from '@/views/hospitalRecord/components/medicalRecordThirdPrint.json';
import formDataJs from '../views/doctorstation/components/store/medicalpage';
import medicalRecordPrint from '../views/hospitalRecord/components/medicalRecordPrint.vue';
import { previewPrint } from '../utils/printUtils';
import {
getEncounterDiagnosis,
getTcmDiagnosis,
} from '../views/inpatientDoctor/home/components/api';
import { cloneDeep } from 'lodash';
const firstRef = ref();
const commpoentType = 'medicalRecord';
const emit = defineEmits(['submitOk']);
const drawer = ref(false);
// 表单数据
const formData = reactive({
hospital: {
orgCode: '41275054-7',
paymentMethod: '城乡居民基本医疗保险'
//医院信息
hospitalInfo: {
//组织机构代码
orgCode: '41275054-700000',
//医疗付款方式
medicalPaymentCode: '',
},
patient: {
//患者信息
patientInfo: {
// 健康卡号
healthCardNo: '',
name: '',
// 患者姓名
patientName: '',
// 患者性别
gender: '',
// 出生日期
birthDate: '',
// 年龄
age: '',
// 国籍
nationality: '中国',
// 籍贯
nativePlace: '',
// 民族
ethnicity: '汉族',
// 身份证号
idCardNo: '',
// 户口住址
householdAddress: '',
workUnit: '',
// 工作单位地址
workUnitAddress: '',
// 联系人姓名
contactName: '',
// 关系
contactRelation: '',
// 联系人地址
contactAddress: '',
contactPhone: ''
// 联系人电话
contactPhone: '',
},
// 住院信息
admission: {
// 第几次住院
times: 1,
// 住院号
hospitalNo: '',
recordNo: '',
channel: '',
// 病案号
medicalRecordNo: '',
// 入院途径
admissionRoute: '',
// 入院时间
admitTime: '',
// 入院科室
department: '',
// 病房
ward: '',
// 确诊日期
confirmDate: '',
// 出院时间
dischargeTime: '',
// 出院科室
dischargeDepartment: '',
// 病房
dischargeWard: '',
hospitalDays: ''
// 实际住院天数
hospitalDays: '',
},
// 诊断信息
diagnosis: {
// 主要诊断
mainDiagnosis: '',
otherDiagnosis: ''
// 其他诊断
otherDiagnosis: '',
},
// 医疗信息
medicalInfo: {
// 是否输血
bloodTransfusion: '2',
// 血型
bloodType: '',
// rh类型
rhType: '',
drugAllergy: '1'
// 药物过敏史
drugAllergy: '1',
},
// 医师信息
doctorInfo: {
// 科主任
departmentDirector: '',
// 副主任
chiefPhysician: '',
// 主治医师
attendingPhysician: '',
// 住院医师
residentPhysician: '',
// 责任护士
chargeNurse: '',
// 住院总医师
chiefResident: '',
// 实习医师
intern: '',
// 病案质量
recordQuality: '1',
// 编码员
coder: '',
qualityControlDate: ''
}
// 控制日期
qualityControlDate: '',
},
// 病案首页2
medicalSecond: {
// 手术方式
surgeryType: '',
// 离院方式
leaveType: '',
// 是否计划出院
isPlan: '',
// 目的
purpose: '',
//昏迷时间---入院前
comaDurationTime_before: '',
//昏迷时间---入院后
comaDurationTime_after: '',
// 肿瘤分期
tumorStaging: '',
// T
tumor_T: '',
// N
tumor_N: '',
// M
tumor_M: '',
// 判断依据
judgmentBase: '',
// 分化程度
degreeDifferentiation: '',
// 临床路径
enterPath: '',
// 变异
mutation: '',
// 退出路径
outPath: '',
// 特级护理
nursingLevel_spec: '',
// 1级护理
nursingLevel_1: '',
// 2级护理
nursingLevel_2: '',
// 3级护理
nursingLevel_3: '',
// 呼吸机使用
ventilatorUse: '',
// 有创呼吸机使用小时
ventilatorUseTime: '',
// 手术表
tableData_top: [],
},
// 病案首页3
// 住院费用
hospitalization: {
// 总费用
hosCharges: '',
// 自付金额
hosCharges_self: '',
},
// 综合医疗服务类
medicalServices: {
// 一般医疗服务类
medicalServices_1: '',
// 一般治疗操作费
medicalServices_2: '',
// 护理费
medicalServices_3: '',
// 其他费用
medicalServices_4: '',
},
// 诊断类
diagnosisClass: {
// 病理诊断
diagnosis_5: '',
// 实验室诊断
diagnosis_6: '',
// 影像学诊断
diagnosis_7: '',
// 临床诊断
diagnosis_8: '',
},
// 治疗类
treatmentClass: {
// 非手术治疗项目费
treatment_9: '',
// 临床物理治疗
treatment_9_1: '',
// 手术治疗费
treatment_10: '',
// 麻醉费
treatment_10_1: '',
// 手术费
treatment_10_2: '',
},
// 康复类
recoveryClass: {
// 康复费
recovery_11: '',
},
// 中医类
TCMClass: {
// 中医治疗费
TCM_12: '',
},
// 西药类
WesternClass: {
// 西药费
Western_13: '',
// 抗菌药物费
Western_13_1: '',
},
// 中药类
chineseClass: {
//中成药
chinese_14: '',
// 中草药
chinese_15: '',
},
// 血液和血液制品类
bloodClass: {
// 血费
blood_16: '',
// 蛋白类制品费
blood_17: '',
// 球蛋白制品费
blood_18: '',
// 凝血因子制品费
blood_19: '',
// 细胞因子制品费
blood_20: '',
},
// 耗材类
consumablesClass: {
// 检查用一次性医用材料费
consumables_21: '',
// 治疗用一次性医用材料费
consumables_22: '',
// 手术用一次性医用材料费
consumables_23: '',
},
// 其他类
otherClass: {
// 其他费用
other_24: '',
},
// 其他诊断及手术附加页
tableData_sub: [],
// 手术操作数组
tableData_top: [],
});
// Props与事件
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
const activeName = ref('first');
const recordPrintRef = ref();
const isShowprintDom = ref(false);
// 打印表单
const printForm = () => {
// 创建一个新的打印窗口
const printWindow = window.open('', '_blank');
let printContent
// 获取模板字符串并替换转义的插值标记
if(activeName.value == 'first') {
printContent = medicalRecordFirstPrint.printContent;
}else if(activeName.value == 'second') {
printContent = medicalRecordSecondPrint.printContent;
}else {
printContent = medicalRecordThirdPrint.printContent;
}
// 这里可以进行实际的数据替换操作
printContent = printContent.replace(/\$\{([^}]+)\}/g, (match, expr) => {
// 简单示例实际应根据expr内容进行数据提取
return eval(expr); // 注意实际使用中应避免eval这里仅为示例
});
// 将内容写入打印窗口并打印
printWindow.document.write(printContent);
printWindow.document.close();
}
drawer.value = true;
// // 创建一个新的打印窗口
// const printWindow = window.open('', '_blank');
// let printContent;
// // 获取模板字符串并替换转义的插值标记
// if (activeName.value == 'first') {
// printContent = medicalRecordFirstPrint.printContent;
// } else if (activeName.value == 'second') {
// printContent = medicalRecordSecondPrint.printContent;
// } else {
// printContent = medicalRecordThirdPrint.printContent;
// }
// // 这里可以进行实际的数据替换操作
// printContent = printContent.replace(/\$\{([^}]+)\}/g, (match, expr) => {
// // 简单示例实际应根据expr内容进行数据提取
// return eval(expr); // 注意实际使用中应避免eval这里仅为示例
// });
// // 将内容写入打印窗口并打印
// printWindow.document.write(printContent);
// printWindow.document.close();
};
function handleClick() {
console.log('112313413');
}
// 重置表单
const resetForm = () => {
Object.keys(formData).forEach(key => {
if (typeof formData[key] === 'object') {
Object.keys(formData[key]).forEach(subKey => {
formData[key][subKey] = '';
});
const resetFun = (data) => {
Object.keys(data).forEach((key) => {
if (data[key] instanceof Array) {
data[key].length = 0;
} else {
formData[key] = '';
data[key] = '';
}
});
// 重置默认值
formData.hospital.orgCode = '41275054-7';
formData.hospital.paymentMethod = '城乡居民基本医疗保险';
formData.patient.nationality = '中国';
formData.patient.ethnicity = '汉族';
formData.admission.times = 1;
formData.medicalInfo.bloodTransfusion = '2';
formData.medicalInfo.drugAllergy = '1';
formData.doctorInfo.recordQuality = '1';
};
// 重置表单
const resetForm = () => {
if (activeName.value == 'first') {
// resetFun(firstRef.value.formData.hospitalInfo);
// resetFun(firstRef.value.formData.patientInfo);
firstRef.value.formData.patientInfo.napl = ''; //籍贯
firstRef.value.formData.patientInfo.certno = ''; //身份证号
firstRef.value.formData.patientInfo.resd_addr = ''; //户口地址
firstRef.value.formData.patientInfo.empr_addr = ''; //工作单位
firstRef.value.formData.patientInfo.coner_name = ''; //联系人
firstRef.value.formData.patientInfo.coner_rlts_code = ''; //关系
firstRef.value.formData.patientInfo.coner_addr = ''; //联系人地址
firstRef.value.formData.patientInfo.coner_tel = ''; //联系人电话
resetFun(firstRef.value.formData.admission);
resetFun(firstRef.value.formData.diagnosis);
resetFun(firstRef.value.formData.medicalInfo);
resetFun(firstRef.value.formData.doctorInfo);
} else if (activeName.value == 'second') {
resetFun(firstRef.value.formData.medicalSecond);
} else {
resetFun(firstRef.value.formData.hospitalization);
resetFun(firstRef.value.formData.medicalServices);
resetFun(firstRef.value.formData.diagnosisClass);
resetFun(firstRef.value.formData.treatmentClass);
resetFun(firstRef.value.formData.recoveryClass);
resetFun(firstRef.value.formData.TCMClass);
resetFun(firstRef.value.formData.WesternClass);
resetFun(firstRef.value.formData.chineseClass);
resetFun(firstRef.value.formData.bloodClass);
resetFun(firstRef.value.formData.consumablesClass);
resetFun(firstRef.value.formData.otherClass);
firstRef.value.formData.tableData_sub.length = 0;
firstRef.value.formData.tableData_top.length = 0;
}
};
//自定义事件更新主数据
const updateCaseFirstDatas = (newDatas) => {
Object.assign(formData, newDatas);
};
const getList = () => {
getEncounterDiagnosis(props.patientInfo.encounterId).then((res) => {
if (res.code == 200) {
console.log('getEncounterDiagnosis=======>', JSON.stringify(res.data));
formDataJs.diagnosisList = res.data;
}
});
getTcmDiagnosis({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.code == 200) {
// if (res.data.illness.length > 0) {
// diagnosisNetDatas.value = res.data.illness;
// res.data.illness.forEach((item, index) => {
// form.value.diagnosisList.push({
// name: item.name + '-' + res.data.symptom[index].name,
// ybNo: item.ybNo,
// medTypeCode: item.medTypeCode,
// });
// });
// }
}
});
};
// 点击历史数据回传布局
const setFormData = (data) => {
if (Object.keys(data).length > 0) {
Object.assign(firstRef.value.formData, data);
} else {
resetForm();
}
getList();
};
//保存数据方法
const submit = () => {
/*
{
"encounterId": "1987758365116919809",
"statusEnum": 5,
"busNo": "ZY202511100001",
"inHospitalTime": "2025-11-10 13:47:08",
"outHospitalTime": null,
"patientId": "1981311362744872962",
"patientName": "王海明",
"genderEnum": 0,
"genderEnum_enumText": "男性",
"birthDate": "1999-06-18 00:00:00",
"age": "26岁",
"wardName": "内科病区1",
"houseName": "内科病房2",
"bedName": "002",
"inOrgTime": "2025-11-10 19:36:28",
"inHospitalDays": 2,
"inHospitalOrgId": "1950367917287616513",
"inHospitalOrgName": "住院内科",
"contractNo": "0000",
"contractName": "自费",
"regDiagnosisName": "感冒",
"accountId": "1987759000528809985"
}
*/
// const isOk = verifyMethod(formDataJs);
if (1) {
const cloneParams = cloneDeep(formDataJs);
// 病例二表
const medicalSecondTable = cloneParams.medicalSecond.surgery_tableData.filter((obj) => {
return obj.isChoose;
});
// 病例三表第一张表
const other_tableData = cloneParams.other_tableData.filter((obj) => {
return obj.isChoose;
});
// 病例三表第二张表
const surgery_tableData = cloneParams.surgery_tableData.filter((obj) => {
return obj.isChoose;
});
// 数据整理
let params = {
...cloneParams,
other_tableData,
surgery_tableData,
};
params.medicalSecond.surgery_tableData = medicalSecondTable;
console.log('cloneParams========>', JSON.stringify(params));
emit('submitOk', params);
}
};
// 公共校验方法
const verifyMethod = (data) => {
let message = '';
if (!data) return false;
if (!data.hospitalInfo.orgCode) {
message = '请填写组织机构代码';
} else if (!data.hospitalInfo.medicalPaymentCode) {
message = '请选择医疗付费方式';
} else if (!data.patientInfo.healthCardNo) {
message = '请填写健康卡号';
} else if (!data.patientInfo.patientName) {
message = '请填患者姓名';
} else if (!data.patientInfo.gender) {
message = '请选择患者性别';
} else if (!data.patientInfo.birthDate) {
message = '请选择出生日期';
} else if (!data.patientInfo.age) {
message = '请填写患者年龄';
} else if (!data.patientInfo.nationality) {
message = '请填写国籍';
} else if (!data.patientInfo.nativePlace) {
message = '请填写籍贯';
} else if (!data.patientInfo.ethnicity) {
message = '请填写民族';
} else if (!data.patientInfo.idCardNo) {
message = '请填写身份证号';
} else if (!data.patientInfo.householdAddress) {
message = '请填写户口地址';
} else if (!data.patientInfo.workUnitAddress) {
message = '请填写工作单位及地址';
} else if (!data.patientInfo.contactName) {
message = '请填写联系人姓名';
} else if (!data.patientInfo.contactRelation) {
message = '请填写与联系人关系';
} else if (!data.patientInfo.contactAddress) {
message = '请填写地址';
} else if (!data.patientInfo.contactPhone) {
message = '请填写电话';
} else if (!data.admission.times) {
message = '请填写第几次住院';
} else if (!data.admission.hospitalNo) {
message = '请填写住院号';
} else if (!data.admission.medicalRecordNo) {
message = '请填写病案号';
} else if (!data.admission.admissionRoute) {
message = '请填写入院途径';
} else if (!data.admission.admitTime) {
message = '请填写入院时间';
} else if (!data.admission.department) {
message = '请填写入院科室';
} else if (!data.admission.ward) {
message = '请填写病房';
} else if (!data.admission.confirmDate) {
message = '请填写确诊日期';
} else if (!data.admission.dischargeTime) {
message = '请填写出院时间';
} else if (!data.admission.dischargeDepartment) {
message = '请填写出院科室';
} else if (!data.admission.dischargeWard) {
message = '请填写病房';
} else if (!data.admission.hospitalDays) {
message = '请填写实际住院天数';
} else if (!data.diagnosis.mainDiagnosis) {
message = '请填写主要诊断';
} else if (!data.diagnosis.otherDiagnosis) {
message = '请填写其他诊断';
} else if (!data.medicalInfo.bloodTransfusion) {
message = '请选择是否输血';
} else if (!data.medicalInfo.bloodType) {
message = '请选择血型';
} else if (!data.medicalInfo.rhType) {
message = '请选择rh类型';
} else if (!data.medicalInfo.drugAllergy) {
message = '请选择药物过敏史';
} else if (!data.doctorInfo.departmentDirector) {
message = '请填写科主任';
} else if (!data.doctorInfo.chiefPhysician) {
message = '请填写主任(副主任)医师';
} else if (!data.doctorInfo.attendingPhysician) {
message = '请填写主治医师';
} else if (!data.doctorInfo.residentPhysician) {
message = '请填写住院医师';
} else if (!data.doctorInfo.chargeNurse) {
message = '请填写责任护士';
} else if (!data.doctorInfo.chiefResident) {
message = '请填写住院总医师';
} else if (!data.doctorInfo.intern) {
message = '请填写实习医师';
} else if (!data.doctorInfo.recordQuality) {
message = '请填写病案质量';
} else if (!data.doctorInfo.coder) {
message = '请填写编码员';
} else if (!data.doctorInfo.qualityControlDate) {
message = '请选择质控日期';
} else if (!data.medicalSecond.tableData_top) {
message = '请添加手术操作表';
} else if (!data.medicalSecond.surgeryType) {
message = '请填写手术方式';
} else if (!data.medicalSecond.leaveType) {
message = '请选择离院方式';
} else if (!data.medicalSecond.isPlan) {
message = '请选择是否有出院31天内再住院计划';
} else if (!data.medicalSecond.purpose) {
message = '请填写目的';
} else if (!data.medicalSecond.comaDurationTime_before) {
message = '请选择颅脑损伤患者昏迷时间-入院前';
} else if (!data.medicalSecond.comaDurationTime_after) {
message = '请选择颅脑损伤患者昏迷时间-入院后';
} else if (!data.medicalSecond.tumorStaging) {
message = '请填写肿瘤分期';
} else if (!data.medicalSecond.tumor_T) {
message = '请填写T';
} else if (!data.medicalSecond.tumor_N) {
message = '请填写N';
} else if (!data.medicalSecond.tumor_M) {
message = '请填写M';
} else if (!data.medicalSecond.judgmentBase) {
message = '请填写判断依据';
} else if (!data.medicalSecond.degreeDifferentiation) {
message = '请选择分化程度';
} else if (!data.medicalSecond.enterPath) {
message = '请填写临床路径-进入路径';
} else if (!data.medicalSecond.mutation) {
message = '请选择是否变异';
} else if (!data.medicalSecond.outPath) {
message = '请选择退出路径';
} else if (!data.medicalSecond.nursingLevel_spec) {
message = '请填写特级护理';
} else if (!data.medicalSecond.nursingLevel_1) {
message = '请填写1级护理';
} else if (!data.medicalSecond.nursingLevel_2) {
message = '请填写2级护理';
} else if (!data.medicalSecond.nursingLevel_3) {
message = '请填写3级护理';
} else if (!data.medicalSecond.ventilatorUse) {
message = '请选择是否使用呼吸机使用';
} else if (!data.medicalSecond.ventilatorUseTime) {
message = '请填写有创呼吸机使用时间(小时)';
}
if (message.length > 0) {
ElMessage({
message,
type: 'error',
});
return false;
}
return true;
};
// 打印方法
const printFun = () => {
isShowprintDom.value = true;
nextTick(() => {
recordPrintRef?.value.setData();
previewPrint(recordPrintRef?.value.getDom());
isShowprintDom.value = false;
});
};
defineExpose({
submit,
commpoentType,
setFormData,
printFun,
});
</script>
<style scoped>
@@ -213,7 +724,9 @@ label {
font-size: 14px;
}
input, select, textarea {
input,
select,
textarea {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
@@ -234,7 +747,8 @@ textarea {
margin-top: 30px;
}
.print-btn, .reset-btn {
.print-btn,
.reset-btn {
padding: 10px 20px;
margin: 0 10px;
border: none;
@@ -244,7 +758,7 @@ textarea {
}
.print-btn {
background-color: #4CAF50;
background-color: #4caf50;
color: white;
}
@@ -275,4 +789,4 @@ textarea {
margin: 2cm;
}
}
</style>
</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

@@ -1,7 +1,9 @@
<template>
<div class="medical-form">
<h2 style="text-align: center;">{{ userStore.hospitalName || '长春市朝阳区中医院' }} -入院记录</h2>
<h2 style="text-align: center">
{{ userStore.hospitalName || '长春市朝阳区中医院' }} -入院记录
</h2>
<!-- 滚动内容区域 -->
<div class="form-scroll-container">
<el-form
@@ -16,90 +18,67 @@
<h4 class="section-title">基础信息</h4>
<div class="adaptive-grid form-section">
<el-form-item label="姓名" prop="patientName" class="grid-item required">
<el-input
v-model="formData.patientName"
placeholder="请输入姓名"
clearable
/>
<el-input v-model="formData.patientName" placeholder="请输入姓名" clearable />
</el-form-item>
<el-form-item label="住院号" prop="hospitalNo" class="grid-item required">
<el-input
v-model="formData.hospitalNo"
placeholder="请输入住院号"
clearable
/>
<el-input v-model="formData.hospitalNo" placeholder="请输入住院号" clearable />
</el-form-item>
<el-form-item label="性别" prop="gender" class="grid-item required">
<el-select v-model="formData.gender" placeholder="请选择" style="width: 100%;">
<el-select v-model="formData.gender" placeholder="请选择" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
<el-form-item label="年龄" prop="age" class="grid-item required">
<div class="input-with-unit">
<el-input
v-model.number="formData.age"
placeholder="请输入年龄"
clearable
/>
<el-input v-model.number="formData.age" placeholder="请输入年龄" clearable />
<span class="unit"></span>
</div>
</el-form-item>
<el-form-item label="民族" prop="nation" class="grid-item">
<el-input
v-model="formData.nation"
placeholder="请输入民族"
clearable
/>
<el-input v-model="formData.nation" placeholder="请输入民族" clearable />
</el-form-item>
<el-form-item label="职业" prop="occupation" class="grid-item">
<el-input
v-model="formData.occupation"
placeholder="请输入职业"
clearable
/>
<el-input v-model="formData.occupation" placeholder="请输入职业" clearable />
</el-form-item>
<el-form-item label="婚姻状况" prop="marriage" class="grid-item">
<el-select v-model="formData.marriage" placeholder="请选择" clearable style="width: 100%;">
<el-select
v-model="formData.marriage"
placeholder="请选择"
clearable
style="width: 100%"
>
<el-option label="已婚" value="已婚"></el-option>
<el-option label="未婚" value="未婚"></el-option>
<el-option label="离异" value="离异"></el-option>
</el-select>
</el-form-item>
<el-form-item label="出生地" prop="birthplace" class="grid-item">
<el-input
v-model="formData.birthplace"
placeholder="请输入出生地"
clearable
/>
<el-input v-model="formData.birthplace" placeholder="请输入出生地" clearable />
</el-form-item>
<el-form-item label="入院时间" prop="admissionTime" class="grid-item required">
<el-date-picker
v-model="formData.admissionTime"
type="datetime"
placeholder="选择入院时间"
<el-date-picker
v-model="formData.admissionTime"
type="datetime"
placeholder="选择入院时间"
value-format="YYYY-MM-DD HH:mm"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="记录时间" prop="recordTime" class="grid-item required">
<el-date-picker
v-model="formData.recordTime"
type="datetime"
placeholder="选择记录时间"
<el-date-picker
v-model="formData.recordTime"
type="datetime"
placeholder="选择记录时间"
value-format="YYYY-MM-DD HH:mm"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="病史陈述者" prop="historyReporter" class="grid-item">
<el-input
v-model="formData.historyReporter"
placeholder="请输入陈述者"
clearable
/>
<el-input v-model="formData.historyReporter" placeholder="请输入陈述者" clearable />
</el-form-item>
<el-form-item label="可靠程度" prop="reliability" class="grid-item">
<el-select v-model="formData.reliability" placeholder="请选择" style="width: 100%;">
<el-select v-model="formData.reliability" placeholder="请选择" style="width: 100%">
<el-option label="可靠" value="可靠"></el-option>
<el-option label="基本可靠" value="基本可靠"></el-option>
<el-option label="不可靠" value="不可靠"></el-option>
@@ -111,76 +90,76 @@
<h4 class="section-title">病史信息</h4>
<div class="form-section">
<el-form-item label="主诉" prop="complaint" class="history-item required">
<el-input
v-model="formData.complaint"
type="textarea"
placeholder="请输入主诉"
<el-input
v-model="formData.complaint"
type="textarea"
placeholder="请输入主诉"
autosize
maxlength="200"
show-word-limit
/>
</el-form-item>
<el-form-item label="现病史" prop="presentIllness" class="history-item">
<el-input
v-model="formData.presentIllness"
type="textarea"
placeholder="请详细描述现病史"
<el-input
v-model="formData.presentIllness"
type="textarea"
placeholder="请详细描述现病史"
autosize
maxlength="1000"
show-word-limit
/>
</el-form-item>
<el-form-item label="既往史" prop="pastHistory" class="history-item">
<el-input
v-model="formData.pastHistory"
type="textarea"
placeholder="请输入既往史"
<el-input
v-model="formData.pastHistory"
type="textarea"
placeholder="请输入既往史"
autosize
maxlength="800"
show-word-limit
/>
</el-form-item>
<el-form-item label="个人史" prop="personalHistory" class="history-item">
<el-input
v-model="formData.personalHistory"
type="textarea"
placeholder="请输入个人史"
<el-input
v-model="formData.personalHistory"
type="textarea"
placeholder="请输入个人史"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="婚育史" prop="maritalHistory" class="history-item">
<el-input
v-model="formData.maritalHistory"
type="textarea"
placeholder="请输入婚育史"
<el-input
v-model="formData.maritalHistory"
type="textarea"
placeholder="请输入婚育史"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="月经史" prop="menstrualHistory" class="history-item">
<el-input
v-model="formData.menstrualHistory"
type="textarea"
placeholder="请输入月经史"
<el-input
v-model="formData.menstrualHistory"
type="textarea"
placeholder="请输入月经史"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="家族史" prop="familyHistory" class="history-item">
<el-input
v-model="formData.familyHistory"
type="textarea"
placeholder="请输入家族史"
<el-input
v-model="formData.familyHistory"
type="textarea"
placeholder="请输入家族史"
autosize
maxlength="500"
show-word-limit
@@ -192,10 +171,10 @@
<h4 class="section-title">中医望闻问切</h4>
<div class="form-section">
<el-form-item label="望闻问切" prop="tcmInfo" class="history-item">
<el-input
v-model="formData.tcmInfo"
type="textarea"
placeholder="请输入中医望闻问切结果"
<el-input
v-model="formData.tcmInfo"
type="textarea"
placeholder="请输入中医望闻问切结果"
autosize
maxlength="600"
show-word-limit
@@ -209,138 +188,134 @@
<div class="adaptive-grid">
<el-form-item label="体温" prop="temp" class="grid-item">
<div class="input-with-unit">
<el-input
v-model.number="formData.temp"
type="number"
step="0.1"
placeholder="如36.0"
<el-input
v-model.number="formData.temp"
type="number"
step="0.1"
placeholder="如36.0"
clearable
/>
<span class="unit"></span>
</div>
</el-form-item>
<el-form-item label="脉搏" prop="pulse" class="grid-item">
<div class="input-with-unit">
<el-input
v-model.number="formData.pulse"
type="number"
placeholder="如76"
<el-input
v-model.number="formData.pulse"
type="number"
placeholder="如76"
clearable
/>
<span class="unit">/</span>
</div>
</el-form-item>
<el-form-item label="呼吸" prop="respiration" class="grid-item">
<div class="input-with-unit">
<el-input
v-model.number="formData.respiration"
type="number"
placeholder="如16"
<el-input
v-model.number="formData.respiration"
type="number"
placeholder="如16"
clearable
/>
<span class="unit">/</span>
</div>
</el-form-item>
<el-form-item label="血压" prop="bp" class="grid-item">
<div class="input-with-unit">
<el-input
v-model="formData.bp"
placeholder="如188/94"
<el-input
v-model="formData.bp"
placeholder="如188/94"
clearable
@blur="validateBloodPressure"
/>
<span class="unit">mmHg</span>
</div>
</el-form-item>
<el-form-item label="身高" prop="height" class="grid-item">
<div class="input-with-unit">
<el-input
v-model.number="formData.height"
type="number"
placeholder="如165"
<el-input
v-model.number="formData.height"
type="number"
placeholder="如165"
clearable
/>
<span class="unit">cm</span>
</div>
</el-form-item>
<el-form-item label="体重" prop="weight" class="grid-item">
<div class="input-with-unit">
<el-input
v-model.number="formData.weight"
type="number"
placeholder="如79"
<el-input
v-model.number="formData.weight"
type="number"
placeholder="如79"
clearable
/>
<span class="unit">kg</span>
</div>
</el-form-item>
<el-form-item label="BMI" prop="bmi" class="grid-item">
<div class="input-with-unit">
<el-input
v-model="formData.bmi"
placeholder="如29.02"
readonly
/>
<el-input v-model="formData.bmi" placeholder="如29.02" readonly />
<span class="unit">kg/</span>
</div>
</el-form-item>
</div>
<el-form-item label="一般情况" prop="general" class="history-item">
<el-input
v-model="formData.general"
type="textarea"
placeholder="请输入一般情况"
<el-input
v-model="formData.general"
type="textarea"
placeholder="请输入一般情况"
autosize
maxlength="300"
show-word-limit
/>
</el-form-item>
<el-form-item label="皮肤粘膜" prop="skin" class="history-item">
<el-input
v-model="formData.skin"
type="textarea"
placeholder="请输入皮肤粘膜情况"
<el-input
v-model="formData.skin"
type="textarea"
placeholder="请输入皮肤粘膜情况"
autosize
maxlength="300"
show-word-limit
/>
</el-form-item>
<el-form-item label="胸部(心、肺)" prop="chest" class="history-item">
<el-input
v-model="formData.chest"
type="textarea"
placeholder="请输入胸部检查结果"
<el-input
v-model="formData.chest"
type="textarea"
placeholder="请输入胸部检查结果"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="腹部" prop="abdomen" class="history-item">
<el-input
v-model="formData.abdomen"
type="textarea"
placeholder="请输入腹部检查结果"
<el-input
v-model="formData.abdomen"
type="textarea"
placeholder="请输入腹部检查结果"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="四肢/神经系统" prop="limbsNervous" class="history-item">
<el-input
v-model="formData.limbsNervous"
type="textarea"
placeholder="请输入四肢及神经系统检查结果"
<el-input
v-model="formData.limbsNervous"
type="textarea"
placeholder="请输入四肢及神经系统检查结果"
autosize
maxlength="500"
show-word-limit
@@ -352,10 +327,10 @@
<h4 class="section-title">辅助检查</h4>
<div class="form-section">
<el-form-item label="检查结果" prop="auxExam" class="history-item">
<el-input
v-model="formData.auxExam"
type="textarea"
placeholder="请输入辅助检查结果"
<el-input
v-model="formData.auxExam"
type="textarea"
placeholder="请输入辅助检查结果"
autosize
maxlength="1000"
show-word-limit
@@ -367,21 +342,21 @@
<h4 class="section-title">初步诊断</h4>
<div class="form-section">
<el-form-item label="中医诊断" prop="tcmDiagnosis" class="history-item">
<el-input
v-model="formData.tcmDiagnosis"
type="textarea"
placeholder="如:胸痹心痛(气阴两虚证)"
<el-input
v-model="formData.tcmDiagnosis"
type="textarea"
placeholder="如:胸痹心痛(气阴两虚证)"
autosize
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="西医诊断" prop="westernDiagnosis" class="history-item">
<el-input
v-model="formData.westernDiagnosis"
type="textarea"
placeholder="如1.冠状动脉粥样硬化性心脏病"
<el-input
v-model="formData.westernDiagnosis"
type="textarea"
placeholder="如1.冠状动脉粥样硬化性心脏病"
autosize
maxlength="800"
show-word-limit
@@ -391,30 +366,22 @@
<!-- 7. 签名信息三列布局 -->
<h4 class="section-title">签名信息</h4>
<div class="adaptive-grid form-section" style="grid-template-columns: repeat(3, 1fr);">
<div class="adaptive-grid form-section" style="grid-template-columns: repeat(3, 1fr)">
<el-form-item label="医师签名" prop="doctorSign" class="grid-item">
<el-input
v-model="formData.doctorSign"
placeholder="请签名"
clearable
/>
<el-input v-model="formData.doctorSign" placeholder="请签名" clearable />
</el-form-item>
<el-form-item label="上级医师签名" prop="superiorSign" class="grid-item">
<el-input
v-model="formData.superiorSign"
placeholder="请签名"
clearable
/>
<el-input v-model="formData.superiorSign" placeholder="请签名" clearable />
</el-form-item>
<el-form-item label="记录日期" prop="signDate" class="grid-item">
<el-date-picker
v-model="formData.signDate"
type="datetime"
placeholder="选择日期"
<el-date-picker
v-model="formData.signDate"
type="datetime"
placeholder="选择日期"
value-format="YYYY-MM-DD HH:mm"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
</div>
@@ -426,16 +393,30 @@
</el-form>
</div>
</div>
<admissionRecord v-if="isShowprintDom" ref="recordPrintRef"></admissionRecord>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { ElInput, ElSelect, ElOption, ElDatePicker, ElButton, ElMessage, ElMessageBox, ElForm, ElFormItem } from 'element-plus';
import { previewPrint } from '../utils/printUtils';
import admissionRecord from '../views/hospitalRecord/components/admissionRecord.vue';
import {
ElInput,
ElSelect,
ElOption,
ElDatePicker,
ElButton,
ElMessage,
ElMessageBox,
ElForm,
ElFormItem,
} from 'element-plus';
import useUserStore from '../store/modules/user';
const isShowprintDom = ref(false);
const recordPrintRef = ref();
defineOptions({
name: 'InHospitalRecord',
components: { ElInput, ElSelect, ElOption, ElDatePicker, ElButton, ElForm, ElFormItem }
components: { ElInput, ElSelect, ElOption, ElDatePicker, ElButton, ElForm, ElFormItem },
});
// Props与事件
@@ -461,26 +442,26 @@ const formData = reactive({
gender: patient?.genderEnum_enumText || '',
age: patient?.age || '',
nation: '',
occupation: '',// 职业
marriage: '',// 婚姻状况
birthplace: '',// 出生地
admissionTime: '',// 入院时间
recordTime: '',// 记录时间
historyReporter: '',// 病史陈述者
reliability: '可靠',// 可靠程度
occupation: '', // 职业
marriage: '', // 婚姻状况
birthplace: '', // 出生地
admissionTime: '', // 入院时间
recordTime: '', // 记录时间
historyReporter: '', // 病史陈述者
reliability: '可靠', // 可靠程度
// 病史信息
complaint: '',// 主诉
presentIllness: '',// 现病史
pastIllness: '',// 既往史
personalHistory: '',// 个人史
allergyHistory: '',// 过敏史
pastHistory: '',// 既往史
familyHistory: '',// 家族史
maritalHistory: '',// 婚姻史
menstrualHistory: '',// 月经史
complaint: '', // 主诉
presentIllness: '', // 现病史
pastIllness: '', // 既往史
personalHistory: '', // 个人史
allergyHistory: '', // 过敏史
pastHistory: '', // 既往史
familyHistory: '', // 家族史
maritalHistory: '', // 婚姻史
menstrualHistory: '', // 月经史
// 中医信息
tcmInfo: '',
// 体格检查
temp: '',
pulse: '',
@@ -494,44 +475,38 @@ const formData = reactive({
chest: '',
abdomen: '',
limbsNervous: '',
// 辅助检查
auxExam: '',
// 诊断信息
tcmDiagnosis: '',
westernDiagnosis: '',
// 签名信息
doctorSign: '',
superiorSign: '',
signDate: ''
signDate: '',
});
// 表单校验规则
const rules = reactive({
name: [
{ required: true, message: '请填写姓名', trigger: ['blur', 'submit'] }
],
hospitalNo: [
{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }
],
gender: [
{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }
],
name: [{ required: true, message: '请填写姓名', trigger: ['blur', 'submit'] }],
hospitalNo: [{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }],
gender: [{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }],
age: [
{ required: true, message: '请填写年龄', trigger: ['blur', 'submit'] },
{ type: 'number', min: 1, max: 120, message: '年龄需在1-120岁之间', trigger: ['blur', 'submit'] }
{
type: 'number',
min: 1,
max: 120,
message: '年龄需在1-120岁之间',
trigger: ['blur', 'submit'],
},
],
admissionTime: [
{ required: true, message: '请选择入院时间', trigger: ['change', 'submit'] }
],
recordTime: [
{ required: true, message: '请选择记录时间', trigger: ['change', 'submit'] }
],
chiefComplaint: [
{ required: true, message: '请填写主诉', trigger: ['blur', 'submit'] }
]
admissionTime: [{ required: true, message: '请选择入院时间', trigger: ['change', 'submit'] }],
recordTime: [{ required: true, message: '请选择记录时间', trigger: ['change', 'submit'] }],
chiefComplaint: [{ required: true, message: '请填写主诉', trigger: ['blur', 'submit'] }],
});
// 生命周期
@@ -548,11 +523,11 @@ onMounted(() => {
}
if (!formData.patientName) {
formData.patientName = patient?.patientName || '';
}
if (!formData.gender) {
}
if (!formData.gender) {
formData.gender = patient?.genderEnum_enumText || '';
}
if (!formData.age) {
}
if (!formData.age) {
formData.age = patient?.age || '';
}
if (!formData.hospitalNo) {
@@ -571,21 +546,22 @@ watch([() => formData.height, () => formData.weight], ([newHeight, newWeight]) =
});
// 入院时间变化处理
watch(() => formData.admissionTime, (val) => {
if (val && !formData.recordTime) {
ElMessageBox.confirm(
'是否将记录时间同步为入院时间?',
'时间同步提示',
{
watch(
() => formData.admissionTime,
(val) => {
if (val && !formData.recordTime) {
ElMessageBox.confirm('是否将记录时间同步为入院时间?', '时间同步提示', {
confirmButtonText: '同步',
cancelButtonText: '手动设置',
type: 'info'
}
).then(() => {
formData.recordTime = val;
}).catch(() => {});
type: 'info',
})
.then(() => {
formData.recordTime = val;
})
.catch(() => {});
}
}
});
);
// 血压格式校验
const validateBloodPressure = () => {
@@ -607,7 +583,6 @@ const submit = () => {
validateBloodPressure();
if (!formData.bp) return; // 格式错误时终止提交
}
emits('submitOk', formData);
ElMessage.success('记录保存成功!');
}
@@ -616,35 +591,33 @@ const submit = () => {
// 新增:重置表单方法(带确认提示)
const handleReset = () => {
ElMessageBox.confirm(
'确定要重置表单吗?所有已填写内容将被清空,且不可恢复',
'重置确认',
{
confirmButtonText: '确认重置',
cancelButtonText: '取消',
type: 'warning',
center: true
}
).then(() => {
// 执行表单重置
formRef.value.resetFields();
// 保留患者基础信息和默认值(避免清空关键基础数据)
formData.patientName = patient?.name || '';
formData.hospitalNo = patient?.busNo || '';
formData.gender = patient?.genderEnum_enumText || '';
formData.age = patient?.age || '';
formData.reliability = '可靠';
// 重置时间为当前时间
const today = new Date();
formData.admissionTime = formatDateTime(today);
formData.recordTime = formatDateTime(today);
formData.signDate = formatDateTime(today);
// 重置成功提示
ElMessage.success('表单已成功重置');
}).catch(() => {
// 取消重置提示
ElMessage.info('已取消表单重置');
});
ElMessageBox.confirm('确定要重置表单吗?所有已填写内容将被清空,且不可恢复', '重置确认', {
confirmButtonText: '确认重置',
cancelButtonText: '取消',
type: 'warning',
center: true,
})
.then(() => {
// 执行表单重置
formRef.value.resetFields();
// 保留患者基础信息和默认值(避免清空关键基础数据)
formData.patientName = patient?.name || '';
formData.hospitalNo = patient?.busNo || '';
formData.gender = patient?.genderEnum_enumText || '';
formData.age = patient?.age || '';
formData.reliability = '可靠';
// 重置时间为当前时间
const today = new Date();
formData.admissionTime = formatDateTime(today);
formData.recordTime = formatDateTime(today);
formData.signDate = formatDateTime(today);
// 重置成功提示
ElMessage.success('表单已成功重置');
})
.catch(() => {
// 取消重置提示
ElMessage.info('已取消表单重置');
});
};
// 表单数据赋值
@@ -664,8 +637,21 @@ const formatDateTime = (date) => {
return `${year}-${month}-${day} ${hour}:${minute}`;
};
// 打印方法
const printFun = () => {
console.log('入院记录打印');
isShowprintDom.value = true;
nextTick(() => {
recordPrintRef?.value.setData(formData);
nextTick(() => {
previewPrint(recordPrintRef?.value.getDom());
isShowprintDom.value = false;
});
});
};
// 暴露接口
defineExpose({ formData, submit, setFormData, handleReset });
defineExpose({ formData, submit, setFormData, handleReset, printFun });
</script>
<style scoped>
@@ -673,8 +659,7 @@ defineExpose({ formData, submit, setFormData, handleReset });
.medical-form {
max-width: 1200px;
width: 100%;
height: 700px;
margin: 15px auto;
height: 28000px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
@@ -810,7 +795,8 @@ defineExpose({ formData, submit, setFormData, handleReset });
.adaptive-grid {
grid-template-columns: 1fr; /* 小屏幕下单列显示 */
}
.grid-item, .history-item {
.grid-item,
.history-item {
margin-bottom: 10px;
}
/* 小屏幕按钮居中 */
@@ -825,4 +811,4 @@ defineExpose({ formData, submit, setFormData, handleReset });
grid-template-columns: repeat(2, 1fr); /* 中等屏幕下两列显示 */
}
}
</style>
</style>

View File

@@ -14,7 +14,7 @@
label-width="120px"
label-align="left"
class="doc-content"
style="height: 60vh; overflow: scroll;"
style="height: 60vh; overflow: scroll"
>
<!-- 患者与手术基础信息 -->
<section class="doc-section">
@@ -23,29 +23,28 @@
<el-form-item label="患者姓名" prop="patientName" class="grid-item required">
<el-input v-model="formData.patientName" placeholder="请输入患者姓名" clearable />
</el-form-item>
<el-form-item label="性别" prop="gender" class="grid-item required">
<el-select v-model="formData.gender" placeholder="请选择性别">
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
<el-form-item label="年龄" prop="age" class="grid-item required">
<div class="input-with-unit">
<el-input v-model.number="formData.age" type="number" placeholder="请输入年龄" />
<el-input v-model.number="formData.age" placeholder="请输入年龄" />
<span class="unit"></span>
</div>
</el-form-item>
<el-form-item label="科室" prop="department" class="grid-item required">
<el-input v-model="formData.department" placeholder="如:普外科" clearable />
</el-form-item>
<el-form-item label="病房/床号" prop="bedNo" class="grid-item required">
<el-input v-model="formData.bedNo" placeholder="如502-03" clearable />
</el-form-item>
<el-form-item label="手术日期/时间" prop="operationDateTime" class="grid-item required">
<el-date-picker
v-model="formData.operationDateTime"
@@ -64,23 +63,23 @@
<el-form-item label="手术者" prop="surgeon" class="grid-item required">
<el-input v-model="formData.surgeon" placeholder="主刀医师姓名" clearable />
</el-form-item>
<el-form-item label="第一助手" prop="firstAssistant" class="grid-item required">
<el-input v-model="formData.firstAssistant" placeholder="第一助手姓名" clearable />
</el-form-item>
<el-form-item label="第二助手" prop="secondAssistant" class="grid-item">
<el-input v-model="formData.secondAssistant" placeholder="第二助手姓名" clearable />
</el-form-item>
<el-form-item label="麻醉医师" prop="anesthesiologist" class="grid-item required">
<el-input v-model="formData.anesthesiologist" placeholder="麻醉医师姓名" clearable />
</el-form-item>
<el-form-item label="巡回护士" prop="circulatingNurse" class="grid-item required">
<el-input v-model="formData.circulatingNurse" placeholder="巡回护士姓名" clearable />
</el-form-item>
<el-form-item label="器械护士" prop="scrubNurse" class="grid-item required">
<el-input v-model="formData.scrubNurse" placeholder="器械护士姓名" clearable />
</el-form-item>
@@ -90,11 +89,15 @@
<!-- 手术详情 -->
<section class="doc-section">
<h2 class="section-title">手术详情</h2>
<el-form-item label="手术名称" prop="operationName" class="full-width-item required">
<el-input v-model="formData.operationName" placeholder="规范手术名称(如:腹腔镜下胆囊切除术)" clearable />
<el-input
v-model="formData.operationName"
placeholder="规范手术名称(如:腹腔镜下胆囊切除术)"
clearable
/>
</el-form-item>
<el-form-item label="手术方式" prop="operationMethod" class="full-width-item required">
<el-select v-model="formData.operationMethod" placeholder="选择手术方式">
<el-option label="开放手术" value="开放手术" />
@@ -102,12 +105,20 @@
<el-option label="介入手术" value="介入手术" />
</el-select>
</el-form-item>
<el-form-item label="手术入路" prop="surgicalApproach" class="full-width-item required">
<el-input v-model="formData.surgicalApproach" placeholder="如:右上腹经腹直肌切口" clearable />
<el-input
v-model="formData.surgicalApproach"
placeholder="如:右上腹经腹直肌切口"
clearable
/>
</el-form-item>
<el-form-item label="术中发现" prop="intraoperativeFindings" class="full-width-item required">
<el-form-item
label="术中发现"
prop="intraoperativeFindings"
class="full-width-item required"
>
<el-input
v-model="formData.intraoperativeFindings"
type="textarea"
@@ -117,7 +128,6 @@
show-word-limit
/>
</el-form-item>
<el-form-item label="手术过程" prop="operationProcess" class="full-width-item required">
<el-input
v-model="formData.operationProcess"
@@ -136,26 +146,34 @@
<div class="adaptive-grid">
<el-form-item label="术中出血量" prop="bloodLoss" class="grid-item required">
<div class="input-with-unit">
<el-input v-model.number="formData.bloodLoss" type="number" placeholder="请输入出血量" />
<el-input
v-model.number="formData.bloodLoss"
type="number"
placeholder="请输入出血量"
/>
<span class="unit">ml</span>
</div>
</el-form-item>
<el-form-item label="输血情况" prop="bloodTransfusion" class="grid-item">
<el-select v-model="formData.bloodTransfusion" placeholder="是否输血">
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
<el-form-item label="引流管放置" prop="drainageTube" class="grid-item">
<el-input v-model="formData.drainageTube" placeholder="如腹腔引流管1根" clearable />
</el-form-item>
<el-form-item label="标本处理" prop="specimenDisposal" class="grid-item required">
<el-input v-model="formData.specimenDisposal" placeholder="如:胆囊标本送病理检查" clearable />
<el-input
v-model="formData.specimenDisposal"
placeholder="如:胆囊标本送病理检查"
clearable
/>
</el-form-item>
<el-form-item label="手术结束时间" prop="operationEndTime" class="grid-item required">
<el-date-picker
v-model="formData.operationEndTime"
@@ -164,7 +182,6 @@
value-format="YYYY-MM-DD HH:mm"
/>
</el-form-item>
<el-form-item label="患者去向" prop="patientDestination" class="grid-item required">
<el-select v-model="formData.patientDestination" placeholder="选择去向">
<el-option label="ICU" value="ICU" />
@@ -177,24 +194,25 @@
<!-- 签署区域 -->
<section class="doc-section">
<h2 class="section-title">签署确认</h2>
<div class="adaptive-grid signature-area" style="grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));">
<div
class="adaptive-grid signature-area"
style="grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))"
>
<el-form-item label="手术者签名" prop="surgeonSignature" class="grid-item required">
<el-input v-model="formData.surgeonSignature" placeholder="主刀医师签字" clearable />
<div class="signature-tip">请手术者亲笔签名</div>
</el-form-item>
<el-form-item label="记录者签名" prop="recorderSignature" class="grid-item required">
<el-input v-model="formData.recorderSignature" placeholder="记录者签字" clearable />
<div class="signature-tip">请记录者如第一助手签字</div>
</el-form-item>
<el-form-item label="记录日期" prop="recordDate" class="grid-item required">
<el-date-picker
v-model="formData.recordDate"
type="date"
placeholder="选择记录日期"
value-format="YYYY-MM-DD"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
</div>
@@ -208,16 +226,30 @@
<el-button type="warning" @click="handleReset">重置表单</el-button>
</div>
</div>
<intOperRecordSheet v-if="isShowprintDom" ref="recordPrintRef"></intOperRecordSheet>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElDatePicker, ElButton } from 'element-plus';
import intOperRecordSheet from '../views/hospitalRecord/components/intOperRecordSheet.vue';
import {
ElMessage,
ElMessageBox,
ElForm,
ElFormItem,
ElInput,
ElSelect,
ElOption,
ElDatePicker,
ElButton,
} from 'element-plus';
import { previewPrint } from '../utils/printUtils';
const isShowprintDom = ref(false);
const recordPrintRef = ref();
// 医院名称
const hospitalName = '长春市朝阳区中医院';
defineOptions({
name: 'iInHospitalSurgicalRecord'
name: 'iInHospitalSurgicalRecord',
});
// 表单引用
const formRef = ref(null);
@@ -231,113 +263,93 @@ const formData = reactive({
age: '',
department: '',
bedNo: '',
operationDateTime: '',// 手术日期时间
operationDateTime: '', // 手术日期时间
// 手术团队信息
surgeon: '',// 主刀医师
firstAssistant: '',// 第一助手
secondAssistant: '',// 第二助手
anesthesiologist: '',// 麻醉医师
circulatingNurse: '',// 巡回护士
scrubNurse: '',// 器械护士
surgeon: '', // 主刀医师
firstAssistant: '', // 第一助手
secondAssistant: '', // 第二助手
anesthesiologist: '', // 麻醉医师
circulatingNurse: '', // 巡回护士
scrubNurse: '', // 器械护士
// 手术详情
operationName: '',// 规范手术名称
operationMethod: '',// 手术方式
surgicalApproach: '',// 手术入路
intraoperativeFindings: '',// 术中发现
operationProcess: '',// 手术过程
operationName: '', // 规范手术名称
operationMethod: '', // 手术方式
surgicalApproach: '', // 手术入路
intraoperativeFindings: '', // 术中发现
operationProcess: '', // 手术过程
// 术后情况
bloodLoss: '',// 术中出血量
bloodTransfusion: '',// 输血情况
drainageTube: '',// 引流管放置
specimenDisposal: '',// 标本处理
operationEndTime: '',// 手术结束时间
patientDestination: '',// 患者去向
bloodLoss: '', // 术中出血量
bloodTransfusion: '', // 输血情况
drainageTube: '', // 引流管放置
specimenDisposal: '', // 标本处理
operationEndTime: '', // 手术结束时间
patientDestination: '', // 患者去向
// 签署信息
surgeonSignature: '',// 手术者签名
recorderSignature: '',// 记录者签名
recordDate: ''// 记录日期
surgeonSignature: '', // 手术者签名
recorderSignature: '', // 记录者签名
recordDate: '', // 记录日期
});
// Props与事件
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
const patient = props.patientInfo;
// 表单验证规则
const rules = reactive({
busNo: [
{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }
],
patientName: [
{ required: true, message: '请填写患者姓名', trigger: ['blur', 'submit'] }
],
gender: [
{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }
],
busNo: [{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }],
patientName: [{ required: true, message: '请填写患者姓名', trigger: ['blur', 'submit'] }],
gender: [{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }],
age: [
{ required: true, message: '请填写年龄', trigger: ['blur', 'submit'] },
{ type: 'number', min: 0, max: 150, message: '年龄需在0-150之间', trigger: ['blur', 'submit'] }
],
department: [
{ required: true, message: '请填写科室', trigger: ['blur', 'submit'] }
],
bedNo: [
{ required: true, message: '请填写病房/床号', trigger: ['blur', 'submit'] }
{ type: 'number', min: 0, max: 150, message: '年龄需在0-150之间', trigger: ['blur', 'submit'] },
],
department: [{ required: true, message: '请填写科室', trigger: ['blur', 'submit'] }],
bedNo: [{ required: true, message: '请填写病房/床号', trigger: ['blur', 'submit'] }],
operationDateTime: [
{ required: true, message: '请选择手术日期时间', trigger: ['change', 'submit'] }
],
surgeon: [
{ required: true, message: '请填写手术者姓名', trigger: ['blur', 'submit'] }
],
firstAssistant: [
{ required: true, message: '请填写第一助手姓名', trigger: ['blur', 'submit'] }
{ required: true, message: '请选择手术日期时间', trigger: ['change', 'submit'] },
],
surgeon: [{ required: true, message: '请填写手术者姓名', trigger: ['blur', 'submit'] }],
firstAssistant: [{ required: true, message: '请填写第一助手姓名', trigger: ['blur', 'submit'] }],
anesthesiologist: [
{ required: true, message: '请填写麻醉医师姓名', trigger: ['blur', 'submit'] }
{ required: true, message: '请填写麻醉医师姓名', trigger: ['blur', 'submit'] },
],
circulatingNurse: [
{ required: true, message: '请填写巡回护士姓名', trigger: ['blur', 'submit'] }
],
scrubNurse: [
{ required: true, message: '请填写器械护士姓名', trigger: ['blur', 'submit'] }
],
operationName: [
{ required: true, message: '请填写手术名称', trigger: ['blur', 'submit'] }
],
operationMethod: [
{ required: true, message: '请选择手术方式', trigger: ['change', 'submit'] }
],
surgicalApproach: [
{ required: true, message: '请填写手术入路', trigger: ['blur', 'submit'] }
{ required: true, message: '请填写巡回护士姓名', trigger: ['blur', 'submit'] },
],
scrubNurse: [{ required: true, message: '请填写器械护士姓名', trigger: ['blur', 'submit'] }],
operationName: [{ required: true, message: '请填写手术名称', trigger: ['blur', 'submit'] }],
operationMethod: [{ required: true, message: '请选择手术方式', trigger: ['change', 'submit'] }],
surgicalApproach: [{ required: true, message: '请填写手术入路', trigger: ['blur', 'submit'] }],
intraoperativeFindings: [
{ required: true, message: '请描述术中发现', trigger: ['blur', 'submit'] }
],
operationProcess: [
{ required: true, message: '请描述手术过程', trigger: ['blur', 'submit'] }
{ required: true, message: '请描述术中发现', trigger: ['blur', 'submit'] },
],
operationProcess: [{ required: true, message: '请描述手术过程', trigger: ['blur', 'submit'] }],
bloodLoss: [
{ required: true, message: '请填写术中出血量', trigger: ['blur', 'submit'] },
{ type: 'number', min: 0, message: '出血量不能为负数', trigger: ['blur', 'submit'] }
{ type: 'number', min: 0, message: '出血量不能为负数', trigger: ['blur', 'submit'] },
],
specimenDisposal: [
{ required: true, message: '请填写标本处理方式', trigger: ['blur', 'submit'] }
{ required: true, message: '请填写标本处理方式', trigger: ['blur', 'submit'] },
],
operationEndTime: [
{ required: true, message: '请选择手术结束时间', trigger: ['change', 'submit'] }
{ required: true, message: '请选择手术结束时间', trigger: ['change', 'submit'] },
],
patientDestination: [
{ required: true, message: '请选择患者去向', trigger: ['change', 'submit'] }
{ required: true, message: '请选择患者去向', trigger: ['change', 'submit'] },
],
surgeonSignature: [
{ required: true, message: '请手术者签名', trigger: ['blur', 'submit'] }
],
recorderSignature: [
{ required: true, message: '请记录者签名', trigger: ['blur', 'submit'] }
],
recordDate: [
{ required: true, message: '请选择记录日期', trigger: ['change', 'submit'] }
]
surgeonSignature: [{ required: true, message: '请手术者签名', trigger: ['blur', 'submit'] }],
recorderSignature: [{ required: true, message: '请记录者签名', trigger: ['blur', 'submit'] }],
recordDate: [{ required: true, message: '请选择记录日期', trigger: ['change', 'submit'] }],
});
// 生命周期
@@ -347,18 +359,49 @@ onMounted(() => {
formData.operationDateTime = formatDateTime(today);
formData.operationEndTime = formatDateTime(today);
formData.recordDate = formatDate(today);
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 || '';
}
});
const emits = defineEmits(['submitOk']);
// 提交表单
const submit = () => {
formRef.value.validate((valid) => {
if (valid) {
ElMessage.success('手术记录保存成功');
console.log('手术记录数据:', formData);
emits('submitOk', formData);
}
});
};
// 表单数据赋值
const setFormData = (data) => {
if (data) {
Object.assign(formData, data);
if (!formData.busNo) {
formData.busNo = patient?.busNo || '';
}
}
};
// 打印功能
const handlePrint = () => {
formRef.value.validate((valid) => {
@@ -372,15 +415,11 @@ const handlePrint = () => {
// 重置表单
const handleReset = () => {
ElMessageBox.confirm(
'确定要重置表单吗?所有已填写内容将被清空',
'确认重置',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
ElMessageBox.confirm('确定要重置表单吗?所有已填写内容将被清空', '确认重置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
formRef.value.resetFields();
const today = new Date();
formData.operationDateTime = formatDateTime(today);
@@ -406,17 +445,31 @@ const formatDateTime = (date) => {
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
};
const printFun = () => {
console.log('入院记录打印');
isShowprintDom.value = true;
nextTick(() => {
recordPrintRef?.value.setData(formData);
nextTick(() => {
previewPrint(recordPrintRef?.value.getDom());
isShowprintDom.value = false;
});
});
};
defineExpose({ submit, setFormData, printFun });
</script>
<style scoped>
/* 样式与原代码保持一致,无需修改 */
/* 核心容器PC端限制合理最大宽度避免超宽屏内容过散 */
.medical-document {
max-width: 1200px;
max-width: 1440px; /* PC端最优宽度兼顾大屏和常规屏 */
width: 98%; /* 占满父容器98%,保留少量边距 */
margin: 20px auto;
padding: 30px;
background: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
font-family: 'SimSun', '宋体', serif;
box-sizing: border-box; /* 确保内边距不撑大容器 */
}
.doc-header {
@@ -455,11 +508,14 @@ const formatDateTime = (date) => {
font-weight: bold;
}
/* 自适应网格PC端优先展示多列优化列宽比例 */
.adaptive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
/* PC端最小列宽220px保证每列内容不拥挤自动适配列数 */
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 15px 20px;
margin-bottom: 15px;
width: 100%;
}
.grid-item {
@@ -513,43 +569,58 @@ const formatDateTime = (date) => {
margin-right: 4px;
}
/* 仅针对小屏设备做基础适配优先保证PC端体验 */
@media (max-width: 768px) {
.medical-document {
max-width: 100%;
padding: 15px;
}
.adaptive-grid {
grid-template-columns: 1fr;
grid-template-columns: 1fr; /* 移动端强制单列 */
}
.doc-title {
font-size: 18px;
}
.section-title {
font-size: 16px;
}
}
/* 超宽屏≥1920px优化适度增大间距提升视觉体验 */
@media (min-width: 1920px) {
.medical-document {
max-width: 1600px;
padding: 40px;
}
.adaptive-grid {
gap: 20px 25px;
}
}
/* 打印样式保留 */
@media print {
.btn-group {
display: none;
}
.medical-document {
box-shadow: none;
margin: 0;
padding: 0;
max-width: 100%;
}
.el-input__inner, .el-select__input, .el-textarea__inner {
.el-input__inner,
.el-select__input,
.el-textarea__inner {
border: none !important;
box-shadow: none !important;
background: transparent !important;
}
.el-form-item__label {
font-weight: bold !important;
}
}
</style>
</style>

View File

@@ -22,29 +22,28 @@
<el-form-item label="姓名" prop="patientName" class="grid-item required">
<el-input v-model="formData.patientName" placeholder="请输入患者姓名" clearable />
</el-form-item>
<el-form-item label="性别" prop="gender" class="grid-item required">
<el-select v-model="formData.gender" placeholder="请选择性别">
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
<el-form-item label="年龄" prop="age" class="grid-item required">
<div class="input-with-unit">
<el-input v-model.number="formData.age" type="number" placeholder="请输入年龄" />
<el-input v-model.number="formData.age" placeholder="请输入年龄" clearable />
<span class="unit"></span>
</div>
</el-form-item>
<el-form-item label="科室/病区" prop="department" class="grid-item required">
<el-input v-model="formData.department" placeholder="如:内科疗区" clearable />
</el-form-item>
<el-form-item label="病房/床号" prop="bedNo" class="grid-item required">
<el-input v-model="formData.bedNo" placeholder="如307-12" clearable />
</el-form-item>
<el-form-item label="入院日期" prop="admissionDate" class="grid-item required">
<el-date-picker
v-model="formData.admissionDate"
@@ -63,11 +62,11 @@
<el-form-item label="经治医师" prop="treatingDoctor" class="grid-item required">
<el-input v-model="formData.treatingDoctor" placeholder="请输入医师姓名" clearable />
</el-form-item>
<el-form-item label="主治医师" prop="attendingDoctor" class="grid-item required">
<el-input v-model="formData.attendingDoctor" placeholder="请输入医师姓名" clearable />
</el-form-item>
<el-form-item label="科主任" prop="departmentHead" class="grid-item required">
<el-input v-model="formData.departmentHead" placeholder="请输入主任姓名" clearable />
</el-form-item>
@@ -77,7 +76,6 @@
<!-- 病情与诊断 -->
<section class="doc-section">
<h2 class="section-title">病情与诊断</h2>
<el-form-item label="病情状况" prop="condition" class="full-width-item required">
<el-input
v-model="formData.condition"
@@ -88,7 +86,6 @@
show-word-limit
/>
</el-form-item>
<div class="diagnosis-container">
<el-form-item label="中医诊断" prop="tcmDiagnosis" class="diagnosis-item">
<el-input
@@ -100,7 +97,6 @@
show-word-limit
/>
</el-form-item>
<el-form-item label="西医诊断" prop="westernDiagnosis" class="diagnosis-item">
<el-input
v-model="formData.westernDiagnosis"
@@ -117,7 +113,6 @@
<!-- 治疗与检查计划 -->
<section class="doc-section">
<h2 class="section-title">治疗与检查计划</h2>
<el-form-item label="治疗方案" prop="treatmentPlan" class="full-width-item required">
<el-input
v-model="formData.treatmentPlan"
@@ -128,8 +123,12 @@
show-word-limit
/>
</el-form-item>
<el-form-item label="进一步检查项目" prop="examinationItems" class="full-width-item required">
<el-form-item
label="进一步检查项目"
prop="examinationItems"
class="full-width-item required"
>
<el-input
v-model="formData.examinationItems"
type="textarea"
@@ -159,38 +158,44 @@
<!-- 签署区域优化后三列自适应+细节样式 -->
<section class="doc-section">
<h2 class="section-title">签署确认</h2>
<div class="adaptive-grid signature-area" style="grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));">
<div
class="adaptive-grid signature-area"
style="grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))"
>
<el-form-item label="患者或家属签字" prop="patientSignature" class="grid-item required">
<el-input v-model="formData.patientSignature" placeholder="请签字" clearable />
<div class="signature-tip">请填写患者或家属签字</div>
</el-form-item>
<el-form-item label="与患者关系" prop="relationship" class="grid-item">
<el-input v-model="formData.relationship" placeholder="如:本人、配偶、子女" clearable />
<el-input
v-model="formData.relationship"
placeholder="如:本人、配偶、子女"
clearable
/>
</el-form-item>
<el-form-item label="签字日期" prop="signatureDate" class="grid-item required">
<el-date-picker
v-model="formData.signatureDate"
type="date"
placeholder="选择签字日期"
value-format="YYYY-MM-DD"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="沟通医师签字" prop="doctorSignature" class="grid-item required">
<el-input v-model="formData.doctorSignature" placeholder="请签字" clearable />
<div class="signature-tip">请填写沟通医师签字</div>
</el-form-item>
<el-form-item label="沟通日期" prop="communicationDate" class="grid-item required">
<el-date-picker
v-model="formData.communicationDate"
type="datetime"
placeholder="选择沟通日期时间"
value-format="YYYY-MM-DD HH:mm"
style="width: 100%;"
style="width: 100%"
/>
</el-form-item>
</div>
@@ -208,13 +213,53 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElDatePicker, ElButton } from 'element-plus';
import {
ElMessage,
ElMessageBox,
ElForm,
ElFormItem,
ElInput,
ElSelect,
ElOption,
ElDatePicker,
ElButton,
} from 'element-plus';
// 医院名称
const hospitalName = '长春市朝阳区中医院';
defineOptions({
name: 'InHospitalCommunicate'
name: 'InHospitalCommunicate',
});
// Props与事件
const props = defineProps({
patientInfo: {
type: Object,
required: true,
},
});
// 生命周期
onMounted(() => {
if (!formData.patientName) {
formData.patientName = patient?.patientName || '';
}
if (!formData.gender) {
formData.gender = patient?.genderEnum_enumText || '';
}
if (!formData.age) {
formData.age = patient?.age || '';
}
if (!formData.hospitalNo) {
formData.hospitalNo = patient?.busNo || '';
}
if (!formData.department) {
formData.department = patient?.inHospitalOrgName || '';
}
if (!formData.bedNo) {
formData.bedNo = patient?.houseName + '-' + patient?.bedName;
}
});
const patient = props.patientInfo;
console.log('patient1111=======>', JSON.stringify(patient));
// 表单引用
const formRef = ref(null);
@@ -228,89 +273,57 @@ const formData = reactive({
department: '',
bedNo: '',
admissionDate: '',
// 医疗团队
treatingDoctor: '',
attendingDoctor: '',
departmentHead: '',
// 病情诊断
condition: '',
tcmDiagnosis: '',
westernDiagnosis: '',
// 治疗检查
treatmentPlan: '',
examinationItems: '',
// 风险告知
riskNotification: '',
// 签署信息
patientSignature: '',
relationship: '',
signatureDate: '',
doctorSignature: '',
communicationDate: ''
communicationDate: '',
});
// 表单验证规则
const rules = reactive({
hospitalNo: [
{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }
],
patientName: [
{ required: true, message: '请填写患者姓名', trigger: ['blur', 'submit'] }
],
gender: [
{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }
],
hospitalNo: [{ required: true, message: '请填写住院号', trigger: ['blur', 'submit'] }],
patientName: [{ required: true, message: '请填写患者姓名', trigger: ['blur', 'submit'] }],
gender: [{ required: true, message: '请选择性别', trigger: ['change', 'submit'] }],
age: [
{ required: true, message: '请填写年龄', trigger: ['blur', 'submit'] },
{ type: 'number', min: 0, max: 150, message: '年龄需在0-150之间', trigger: ['blur', 'submit'] }
],
department: [
{ required: true, message: '请填写科室/病区', trigger: ['blur', 'submit'] }
],
bedNo: [
{ required: true, message: '请填写病房/床号', trigger: ['blur', 'submit'] }
],
admissionDate: [
{ required: true, message: '请选择入院日期', trigger: ['change', 'submit'] }
],
treatingDoctor: [
{ required: true, message: '请填写经治医师', trigger: ['blur', 'submit'] }
],
attendingDoctor: [
{ required: true, message: '请填写主治医师', trigger: ['blur', 'submit'] }
],
departmentHead: [
{ required: true, message: '请填写科主任', trigger: ['blur', 'submit'] }
],
condition: [
{ required: true, message: '请描述病情状况', trigger: ['blur', 'submit'] }
],
treatmentPlan: [
{ required: true, message: '请填写治疗方案', trigger: ['blur', 'submit'] }
],
examinationItems: [
{ required: true, message: '请填写检查项目', trigger: ['blur', 'submit'] }
{ type: 'number', min: 0, max: 150, message: '年龄需在0-150之间', trigger: ['blur', 'submit'] },
],
department: [{ required: true, message: '请填写科室/病区', trigger: ['blur', 'submit'] }],
bedNo: [{ required: true, message: '请填写病房/床号', trigger: ['blur', 'submit'] }],
admissionDate: [{ required: true, message: '请选择入院日期', trigger: ['change', 'submit'] }],
treatingDoctor: [{ required: true, message: '请填写经治医师', trigger: ['blur', 'submit'] }],
attendingDoctor: [{ required: true, message: '请填写主治医师', trigger: ['blur', 'submit'] }],
departmentHead: [{ required: true, message: '请填写科主任', trigger: ['blur', 'submit'] }],
condition: [{ required: true, message: '请描述病情状况', trigger: ['blur', 'submit'] }],
treatmentPlan: [{ required: true, message: '请填写治疗方案', trigger: ['blur', 'submit'] }],
examinationItems: [{ required: true, message: '请填写检查项目', trigger: ['blur', 'submit'] }],
riskNotification: [
{ required: true, message: '请填写风险告知内容', trigger: ['blur', 'submit'] }
{ required: true, message: '请填写风险告知内容', trigger: ['blur', 'submit'] },
],
patientSignature: [
{ required: true, message: '请填写患者或家属签字', trigger: ['blur', 'submit'] }
{ required: true, message: '请填写患者或家属签字', trigger: ['blur', 'submit'] },
],
signatureDate: [
{ required: true, message: '请选择签字日期', trigger: ['change', 'submit'] }
],
doctorSignature: [
{ required: true, message: '请填写医师签字', trigger: ['blur', 'submit'] }
],
communicationDate: [
{ required: true, message: '请选择沟通日期', trigger: ['change', 'submit'] }
]
signatureDate: [{ required: true, message: '请选择签字日期', trigger: ['change', 'submit'] }],
doctorSignature: [{ required: true, message: '请填写医师签字', trigger: ['blur', 'submit'] }],
communicationDate: [{ required: true, message: '请选择沟通日期', trigger: ['change', 'submit'] }],
});
// 生命周期
@@ -322,16 +335,26 @@ onMounted(() => {
formData.communicationDate = formatDateTime(today);
});
const emits = defineEmits(['submitOk']);
// 提交表单
const submit = () => {
formRef.value.validate((valid) => {
if (valid) {
ElMessage.success('记录保存成功');
console.log('表单数据:', formData);
emits('submitOk', formData);
}
});
};
// 表单数据赋值
const setFormData = (data) => {
if (data) {
Object.assign(formData, data);
}
};
// 打印功能
const handlePrint = () => {
formRef.value.validate((valid) => {
@@ -345,15 +368,11 @@ const handlePrint = () => {
// 重置表单
const handleReset = () => {
ElMessageBox.confirm(
'确定要重置表单吗?所有已填写内容将被清空',
'确认重置',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
ElMessageBox.confirm('确定要重置表单吗?所有已填写内容将被清空', '确认重置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
formRef.value.resetFields();
const today = new Date();
formData.admissionDate = formatDate(today);
@@ -379,6 +398,8 @@ const formatDateTime = (date) => {
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
};
defineExpose({ submit, setFormData });
</script>
<style scoped>
@@ -510,19 +531,19 @@ const formatDateTime = (date) => {
.medical-document {
padding: 15px;
}
.diagnosis-container {
grid-template-columns: 1fr;
}
.adaptive-grid {
grid-template-columns: 1fr;
}
.doc-title {
font-size: 18px;
}
.section-title {
font-size: 16px;
}
@@ -533,21 +554,21 @@ const formatDateTime = (date) => {
.btn-group {
display: none;
}
.medical-document {
box-shadow: none;
margin: 0;
padding: 0;
}
.el-input__inner, .el-select__input, .el-textarea__inner {
.el-input__inner,
.el-select__input,
.el-textarea__inner {
border: none !important;
box-shadow: none !important;
background: transparent !important;
}
.el-form-item__label {
font-weight: bold !important;
}
}
</style>
</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

@@ -1,19 +1,15 @@
<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">
<!-- Element Plus表单组件 -->
<el-form
ref="formRef"
:model="formData"
@@ -22,40 +18,34 @@
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="height" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.height" type="text" placeholder="请输入" />
<span class="unit">cm</span>
</div>
</el-form-item>
<!-- 体重输入项 -->
<el-form-item label="体重" prop="weight" class="row-item">
<div class="input-with-unit">
<el-input v-model="formData.weight" type="text" placeholder="请输入" />
<span class="unit">kg</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"
@@ -68,13 +58,10 @@
</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"
@@ -83,11 +70,9 @@
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"
@@ -96,7 +81,6 @@
autosize
/>
</el-form-item>
<!-- 过敏史输入项 -->
<el-form-item label="过敏史" prop="allergyHistory" class="row-item history-item">
<el-input
v-model="formData.allergyHistory"
@@ -105,7 +89,6 @@
autosize
/>
</el-form-item>
<!-- 家族史输入项 -->
<el-form-item label="家族史" prop="familyHistory" class="row-item history-item">
<el-input
v-model="formData.familyHistory"
@@ -116,8 +99,6 @@
</el-form-item>
</div>
</el-form-item>
<!-- 主诉查体(治疗)处置辅助检查区域标题 -->
<h4 class="section-title">主诉查体(治疗)处置辅助检查</h4>
<!-- 3. 主诉必填 -->
<el-form-item label="主诉" prop="complaint" class="required form-item-single">
@@ -130,7 +111,6 @@
/>
</el-form-item>
<!-- 4. 查体处理辅助检查 -->
<!-- 查体(治疗)输入项 -->
<el-form-item label="查体(治疗)" prop="physicalExam" class="form-item-single">
<el-input
v-model="formData.physicalExam"
@@ -141,7 +121,6 @@
/>
</el-form-item>
<!-- 处置输入项 -->
<el-form-item label="处置" prop="treatment" class="form-item-single">
<el-input
v-model="formData.treatment"
@@ -152,7 +131,6 @@
/>
</el-form-item>
<!-- 辅助检查输入项 -->
<el-form-item label="辅助检查" prop="auxiliaryExam" class="form-item-single">
<el-input
v-model="formData.auxiliaryExam"
@@ -168,62 +146,51 @@
</template>
<script setup>
// 导入Vue相关功能和组件
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: 'OutpatientMedicalRecord',
components: { ElInput, ElMessage, ElForm, ElFormItem },
});
// // Props与事件,去掉props.patientInfo改为直接从store获取
// // Props与事件, 去掉props.patientInfo改为直接从store获取
// const props = defineProps({
// patientInfo: {
// type: Object,
// required: true,
// },
// });
// 定义组件接收的属性(目前为空)
const props = defineProps({});
// 定义组件触发的事件
const emits = defineEmits(['submitOk']);
// 数据初始化
// 获取用户store实例用于获取医院名称等全局信息
const userStore = useUserStore();
// 患者信息引用,存储当前就诊患者的基本信息
const patient = ref(null);
// 表单引用,用于访问表单实例进行验证等操作
const formRef = ref(null);
// 表单数据(全部字符类型)
// 存储门诊病历表单的所有字段数据
const formData = reactive({
height: '', // 身高(cm)
weight: '', // 体重(kg)
temperature: '', // 体温(℃)
pulse: '', // 脉搏(次/分)
height: '', // 身高
weight: '', // 体重
temperature: '', // 体温
pulse: '', // 脉搏
onsetDate: '', // 发病日期
complaint: '', // 主诉(必填
complaint: '', // 主诉(必填)
presentIllness: '', // 现病史
pastIllness: '', // 既往史
personalHistory: '', // 个人史
allergyHistory: '', // 过敏史
physicalExam: '', // 查体结果
treatment: '', // 处理方案
auxiliaryExam: '', // 辅助检查结果
physicalExam: '', // 查体
treatment: '', // 处理
auxiliaryExam: '', // 辅助检查
familyHistory: '', // 家族史
});
// 表单校验规则
// 定义表单字段的验证规则,目前仅主诉为必填项
const rules = reactive({
complaint: [
{
@@ -235,21 +202,14 @@ const rules = reactive({
});
// 提交函数
// 用于触发表单验证并提交数据到父组件
const submit = () => {
// 表单验证
formRef.value.validate((isValid) => {
if (isValid) {
// 触发submitOk事件传递表单数据
emits('submitOk', formData);
// 显示成功消息
ElMessage.success('提交成功');
}
});
};
// 日期格式化工具函数
// 将Date对象格式化为 YYYY-MM-DD HH:mm 格式的字符串
// 日期格式化工具
const formatDateTime = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
@@ -258,23 +218,17 @@ const formatDateTime = (date) => {
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);
// 从store获取患者信息
patient.value = patientInfo.value;
// 初始化发病日期为当前时间
if (!formData.onsetDate) {
@@ -283,7 +237,6 @@ onMounted(() => {
});
// 监听患者信息变化,实现联动显示
// 当patientInfo发生变化时更新本地patient引用
watch(
() => patientInfo.value,
(newPatientInfo) => {
@@ -292,8 +245,7 @@ watch(
{ deep: true }
);
// 暴露接口供父组件调用
// 将formData、submit方法和setFormData方法暴露给父组件使用
// 暴露接口
defineExpose({ formData, submit, setFormData });
</script>
@@ -303,14 +255,15 @@ defineExpose({ formData, submit, setFormData });
max-width: 1200px;
width: 100%;
min-height: 800px;
height: 900px;
height: auto;
margin: 15px auto;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
font-family: Arial, sans-serif;
box-sizing: border-box;
overflow: visible;
overflow: hidden; /* 防止内部内容溢出 */
position: relative;
}
/* 顶部姓名样式 */
@@ -325,11 +278,12 @@ defineExpose({ formData, submit, setFormData });
/* 滚动内容容器 */
.form-scroll-container {
width: 100%;
max-height: 80vh;
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;
@@ -345,6 +299,8 @@ defineExpose({ formData, submit, setFormData });
/* 完整表单容器 */
.medical-full-form {
width: 100%;
min-width: 0; /* 防止内容强制拉伸容器 */
box-sizing: border-box;
}
/* 区域通用样式 */
@@ -366,6 +322,8 @@ defineExpose({ formData, submit, setFormData });
flex-wrap: wrap; /* 自动换行 */
align-items: flex-start; /* 顶部对齐,适配文本域高度 */
gap: 15px; /* 统一元素间距 */
width: 100%;
box-sizing: border-box;
}
.row-item {
margin-bottom: 0; /* 取消底部间距,避免换行重叠 */
@@ -436,9 +394,11 @@ defineExpose({ formData, submit, setFormData });
.medical-form {
height: 80vh;
padding: 10px;
overflow: hidden;
}
.form-scroll-container {
height: calc(100% - 35px);
max-height: none;
}
.el-form {
label-width: 70px !important;
@@ -457,4 +417,4 @@ defineExpose({ formData, submit, setFormData });
--el-input-textarea-min-height: 80px;
}
}
</style>
</style>

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

@@ -501,4 +501,33 @@ export function getGroupMarkers(tableData) {
}
});
return tableData;
}
/**
* 格式化库存数量显示(大单位情况) 示例 1盒5片
* @param quantity 小单位库存数量
* @param partPercent 拆零比
* @param unitCode 大单位label
* @param minUnitCode 小单位label
*/
export function formatInventory(quantity, partPercent, unitCode, minUnitCode) {
// 处理负数情况
const isNegative = quantity < 0;
const absQuantity = Math.abs(quantity);
if (absQuantity % partPercent !== 0) {
const integerPart = Math.floor(absQuantity / partPercent);
const decimalPart = absQuantity % partPercent;
let result = integerPart.toString() + ' ' + unitCode;
if (decimalPart > 0) {
result += decimalPart.toString() + ' ' + minUnitCode;
}
return isNegative ? '-' + result : result;
}
// 整除情况
const result = absQuantity / partPercent;
return isNegative ? '-' + result : result + ' ' + unitCode;
}

View File

@@ -3,7 +3,7 @@
* 集中管理所有打印相关功能
*/
// 打印模板映射表
// 打印模板映射表 .
const TEMPLATE_MAP = {
// CLINIC_CHARGE: () => import('@/views/charge/cliniccharge/components/template.json'),
// DISPOSAL: () => import('@/views/clinicmanagement/disposal/components/disposalTemplate.json'),
@@ -21,6 +21,25 @@ const TEMPLATE_MAP = {
OUTPATIENT_REGISTRATION: () => import('@/components/Print/OutpatientRegistration.json'),
//门诊收费
OUTPATIENT_CHARGE: () => import('@/components/Print/OutpatientBilling.json'),
//门诊病历
OUTPATIENT_MEDICAL_RECORD: () => import('@/components/Print/OutpatientMedicalRecord.json'),
//门诊输液贴
OUTPATIENT_INFUSION: () => import('@/components/Print/OutpatientInfusion.json'),
//手术记录
OPERATIVE_RECORD: () => import('@/components/Print/OperativeRecord.json'),
//红旗门诊病历
HQOUTPATIENT_MEDICAL_RECORD: () => import('@/components/Print/HQOutpatientMedicalRecord.json'),
//预交金
ADVANCE_PAYMENT: () => import('@/components/Print/AdvancePayment.json'),
//中药处方单
CHINESE_MEDICINE_PRESCRIPTION: () =>
import('@/components/Print/ChineseMedicinePrescription.json'),
//药房处方单
PHARMACY_PRESCRIPTION: () => import('@/components/Print/Pharmacy.json'),
//中药医生处方单
DOC_CHINESE_MEDICINE_PRESCRIPTION: () =>
import('@/components/Print/DocChineseMedicinePrescription.json'),
};
/**
@@ -108,6 +127,22 @@ export const PRINT_TEMPLATE = {
PRESCRIPTION: 'PRESCRIPTION',
//处置单
DISPOSAL: 'DISPOSAL',
//门诊病历
OUTPATIENT_MEDICAL_RECORD: 'OUTPATIENT_MEDICAL_RECORD',
//门诊输液贴
OUTPATIENT_INFUSION: 'OUTPATIENT_INFUSION',
//手术记录
OPERATIVE_RECORD: 'OPERATIVE_RECORD',
//红旗门诊病历
HQOUTPATIENT_MEDICAL_RECORD: 'HQOUTPATIENT_MEDICAL_RECORD',
//预交金
ADVANCE_PAYMENT: 'ADVANCE_PAYMENT',
//中药处方单
CHINESE_MEDICINE_PRESCRIPTION: 'CHINESE_MEDICINE_PRESCRIPTION',
//药房处方单
PHARMACY_PRESCRIPTION: 'PHARMACY_PRESCRIPTION',
//中药医生处方单
DOC_CHINESE_MEDICINE_PRESCRIPTION: 'DOC_CHINESE_MEDICINE_PRESCRIPTION',
};
/**
@@ -128,6 +163,7 @@ export function getPrinterList() {
}
import useUserStore from '@/store/modules/user';
import { ElMessage } from 'element-plus';
/**
* 获取当前登录用户ID
@@ -280,6 +316,22 @@ export async function selectPrinterAndPrint(
}
}
// 预览打印
export function previewPrint(elementDom) {
if (elementDom) {
//初始化
window.hiprint.init();
const hiprintTemplate = new window.hiprint.PrintTemplate();
// printByHtml为预览打印
hiprintTemplate.printByHtml(elementDom, {});
} else {
ElMessage({
type: 'error',
message: '加载模版失败',
});
}
}
// 默认导出简化的打印方法
export default {
print: simplePrint,

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}`);
// 计算年龄
let 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

@@ -0,0 +1,39 @@
import request from '@/utils/request'
/**
* 添加医生排班
* @param {Object} data - 排班数据
* @returns {Promise}
*/
export function addDoctorSchedule(data) {
return request({
url: '/doctor-schedule/add',
method: 'post',
data: data
})
}
/**
* 删除医生排班
* @param {String|Number} id - 排班记录ID
* @returns {Promise}
*/
export function deleteDoctorSchedule(id) {
return request({
url: '/doctor-schedule/delete/' + id,
method: 'delete'
})
}
/**
* 批量保存医生排班
* @param {Array} data - 排班数据数组
* @returns {Promise}
*/
export function batchSaveDoctorSchedule(data) {
return request({
url: '/doctor-schedule/batch-save',
method: 'post',
data: data
})
}

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');
}

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