2026-01-16 16:53:36 +08:00

777 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<div ref="compactForm">
<el-row :gutter="5" class="compact-form" v-show="showSearch">
<el-form :model="queryParams" ref="queryRef" label-width="100px" :rules="queryRules" style="width: 100%;">
<el-row>
<el-col :span="6">
<el-form-item label="标本日期:" prop="times">
<el-date-picker v-model="queryParams.times" type="daterange" range-separator="-"
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="handletime" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="打印状态:" prop="dybz">
<el-select v-model="queryParams.dybz" placeholder="请选择" clearable @change="handleQuery">
<el-option label="已打印" value="1" />
<el-option label="未打印" value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="科室:" prop="ksdh">
<SelectTable v-model:data="queryParams.ksdh" :fields="ksdhFields" :tableData="dictData.DP || []"
label="label" value="value" objKey="value" placeholder="请选择科室" @getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="病人类型:" prop="brlyname">
<SelectTable v-model:data="queryParams.brlyname" :fields="brlxFields" :tableData="dictData.PT || []"
label="label" value="label" objKey="label" placeholder="请选择" @getDataValue="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item label-width="50px">
<el-checkbox v-model="queryParams.alarmflag" label="仅危急值" @change="alarmHandle" size="large" />
<el-checkbox v-model="queryParams.jzbz" label="仅急诊" @change="jzbzHandle" size="large" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="姓名:" prop="brxm">
<el-input v-model="queryParams.brxm" clearable placeholder="请输入姓名" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="病人号:" prop="brdh">
<el-input v-model="queryParams.brdh" clearable placeholder="请输入病人号" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="床号:" prop="ch">
<el-input v-model="queryParams.ch" clearable placeholder="请输入床号" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label-width="50px">
<el-button icon="search" type="primary" @click="handleQuery">
搜索
</el-button>
<!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> -->
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-row>
</div>
<div class="mb8 mt10">
<el-button type="primary" plain icon="Edit" :size="aotuSize" @click="selectStatusRows">勾选所有未打印</el-button>
<el-button type="primary" plain icon="Upload" :size="aotuSize" @click="printPdf">打印报告</el-button>
<el-button type="primary" plain icon="edit" :size="aotuSize" @click="editHandle">编辑</el-button>
</div>
<el-row :gutter="15">
<el-col :span="16" class="right-table">
<CustomVxeTable :table-data="tableData" :loading="loading" :columns="columns" @current-change="rowHandle"
size="small" :scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" class="mytable-style"
ref="tableRefs" :cell-style="cellStyleHd" @selection-change="selectionChange" :config="tableConfig"
:enable-column-drag="true" @column-drag-end="handleColumnDragEnd">
<template #dybz="{ row }">
<svg-icon v-if="row.dybz == 1" icon-class="lvgou" />
</template>
<template #jgbz="{ row }">
<el-checkbox :model-value="row.jgbz == '2'" readonly />
</template>
<template #alarmflag="{ row }">
{{ row.alarmflag == 1 ? '危' : '' }}
</template>
<template #jzbz="{ row }">
{{ row.jzbz == 1 ? '急' : '' }}
</template>
</CustomVxeTable>
</el-col>
<el-col :span="8" class="right-table">
<div>
<CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig"
@row-click="xmrowHandle" ref="rightTableRef">
<template #jgbz="{ row }">
{{ formatJgbz(row.jgbz) }}
</template>
</CustomTable>
</div>
<div class="mt10" v-if="bottomTableData.length > 0"></div>
<div>
<CustomTable v-if="bottomTableData.length > 0" :data="bottomTableData" :columns="bottomColumns"
:config="bottomTableConfig">
</CustomTable>
</div>
<div v-if="bottomTableData.length > 0" class="expert-comment" :title="expertComment">
专家评语:{{ expertComment }}
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, toRaw, computed, watch, nextTick, onMounted } from 'vue';
import CustomTable from '@/components/elTable/index.vue'
import CustomVxeTable from '@/components/vxeTable/index.vue'
import { reportList, reportPrintPdf, reportResult, reportMedList, reportFsresult } from '@/api/checkCode/index'
import dayjs from 'dayjs';
import SelectTable from '@/components/SelectTable/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { classCom } from '@/utils/classCom'
import { HiprintPrinter } from '@/utils/hiprintPrinter';
import * as echarts from 'echarts';
//@ts-ignore
import { comDict } from '@/utils/dict'
import { usePrintStore } from '@/store/modules/printStore'
//@ts-ignore
import useUserStore from '@/store/modules/user'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter();
const aotuSize = classCom.useAutoSize();
const tableRefs = ref()
interface QueryParams {
times: string[]; // 明确类型为字符串元组
alarmflag: boolean,
jzbz: boolean,
ksdh: string,
brlyname: string,
brxm: string,
brdh: string,
ch: string,
dybz: string,
sqh: string,
userid: string,
yljg: string,
}
// 提交表单数据
const queryParams = reactive<QueryParams>({
times: ['2025-08-19', '2025-08-19'],
ksdh: '0210',
brlyname: '',
sqh: '25080109476',
alarmflag: false,
jzbz: false,
brxm: '',
brdh: '',
ch: '',
dybz: '',
userid: '',
yljg: '1'
});
const single = ref(true);
const multiple = ref(true);
const alarmflag = ref()
const jzbz = ref()
const showSearch = ref(true);
const queryRef = ref()
// 项目点击数据
const Info = ref<any>({})
// 定义校验规则
const queryRules = ref({
})
const handletime = (val: Array<string>) => {
// console.log('时间', val);
// 校验日期格式
const dayDiff = getDayDiff(val[0], val[1]);
if (dayDiff === -1) return ElMessage.warning('请选择有效的日期范围');
//校验是否超过7天
if (dayDiff > 7) return ElMessage.warning('查询时间范围不能超过7天,请重新选择');
}
const handleQuery = async () => {
const valid = await queryRef.value.validate().catch(() => { });
if (!valid) return false;
getList()
}
// 重置
const resetQuery = () => {
queryRef.value?.resetFields();
getList()
}
const loading = ref(false);
// 勾选框数据
const rows = ref<any[]>([])
// 数据源
const tableData = ref([])
// 配置项
const columns = ref([
{ type: 'selection', field: '', title: '', resizable: true, align: 'center', width: 40 },
{ field: 'dybz', title: '印', align: 'center', resizable: true, slotName: 'dybz', width: 40 },
{ field: 'jgbz', title: '审', align: 'center', resizable: true, slotName: 'jgbz', width: 40 },
{ field: 'yljg', title: '医疗机构', align: 'center', resizable: true, width: 130 },
{ field: 'yqdl', title: '检验仪器', align: 'center', resizable: true, width: 80 },
{ field: 'jyrqname', title: '标本日期', align: 'center', resizable: true, width: 80 },
{ field: 'ybh', title: '标本号', align: 'center', resizable: true, width: 60 },
// { field: 'jzbz', title: '急', align: 'center', resizable: true, slotName: 'jzbz', width: 40 },
// { field: 'alarmflag', title: '危', align: 'center', resizable: true, slotName: 'alarmflag', width: 40 },
{ field: 'brxm', title: '姓名', align: 'center', resizable: true, width: 50 },
{ field: 'brdh', title: '住院号', align: 'center', resizable: true, sortable: true, width: 100 },
{ field: 'sqh', title: '申请号', align: 'center', resizable: true, width: 100 },
{ field: 'brlyname', title: '病人类型', align: 'center', resizable: true, width: 60 },
{ field: 'brxbname', title: '性别', align: 'center', resizable: true, width: 40 },
{ field: 'nl', title: '年龄', align: 'center', resizable: true, width: 40 },
{ field: 'ksname', title: '科室', align: 'center', resizable: true, width: 90 },
{ field: 'ch', title: '床号', align: 'center', resizable: true, width: 50 },
{ field: 'sjysname', title: '送检医生', align: 'center', resizable: true, width: 80 },
{ field: 'bbsjsj', title: '送检时间', align: 'center', resizable: true, width: 80 },
{ field: 'cysj', title: '采样时间', align: 'center', resizable: true, width: 80 },
{ field: 'jysh', title: '检验医生', align: 'center', resizable: true, width: 80 },
{ field: 'dysj', title: '报告日期', align: 'center', resizable: true, width: 80 },
{ field: 'yblxname', title: '标本', align: 'center', resizable: true, width: 50 },
{ field: 'fb', title: '费别', align: 'center', resizable: true, width: 50 },
{ field: 'hdys', title: '核对医生', align: 'center', resizable: true, width: 60 },
{ field: 'zd', title: '临床诊断', align: 'center', resizable: true, width: 100 },
{ field: 'bz', title: '备注', align: 'center', resizable: true, width: 100 },
{ field: 'sjysname', title: '审核人', align: 'center', resizable: true, width: 60 },
{ field: 'confirmtime', title: '审核时间', align: 'center', resizable: true, width: 80 },
{ field: 'zh', title: '最后修改人', align: 'center', resizable: true, width: 100 },
{ field: 'zhtime', title: '最后修改时间', align: 'center', resizable: true, width: 100 },
{ field: 'sfz', title: '身份证', align: 'center', resizable: true, width: 100 },
{ field: 'phone', title: '病人联系方式', align: 'center', resizable: true, width: 100 },
{ field: 'jsphone', title: '家属联系方式', align: 'center', resizable: true, width: 100 },
])
const selectionChange = (selection: any) => {
rows.value = selection
single.value = selection.length != 1;
multiple.value = !selection.length;
}
const rightTableRef = ref()
const rowHandle = (row: any) => {
if (row.yqdl == '细菌仪') {
rightColumns.value[1].visible = false
rightColumns.value[2].visible = true
rightColumns.value[4].visible = false
rightColumns.value[5].visible = false
rightColumns.value[6].visible = true
} else {
rightColumns.value[1].visible = true
rightColumns.value[2].visible = false
rightColumns.value[4].visible = true
rightColumns.value[5].visible = true
rightColumns.value[6].visible = false
}
Info.value = row
const data = {
jyrq: row.jyrq,
yq: row.yq,
ybh: row.ybh,
}
reportResult(data).then((res: any) => {
if (res.code == 0) {
rightTableData.value = res.data
if (res.data.length) {
nextTick(() => {
rightTableRef.value.setCurrentRow(res.data[0])
})
xmrowHandle(res.data[0])
} else {
bottomTableData.value = []
}
}
})
}
const handleColumnDragEnd = (newColumns: any) => {
columns.value = [];
// 使用nextTick确保DOM更新后再设置新值
nextTick(() => {
columns.value = [...newColumns.columns];
});
}
// 打印单张Pdf
const printPdf = () => {
const printStore = usePrintStore();
const selections = tableRefs.value.allSelection()
if (selections.length <= 0) return ElMessage.warning('请选择要打印的数据!')
const arr = [...new Set(selections.map((item: any) => { return item.brdh }))]
if (arr.length > 1) return ElMessage.warning('只能选中一个病人的报告单打印!')
const merge = selections.map((item: any) => { return `${item.jyrqname.replace(/-/g, '')}_${item.yq.trim()}_${item.ybh}` })
reportPrintPdf(merge).then((res: any) => {
if (res.code == 0) {
if (!res.data) return ElMessage.warning('未找到打印文件')
classCom.printBase64PDF(res.data)
// if (printStore.isConnected) {
// HiprintPrinter.silentPrint([res.data])
// } else {
// classCom.printBase64PDF(res.data)
// }
} else {
ElMessage.warning(res.msg)
}
})
}
const editHandle = () => {
if (!Info.value.ybh) return
if (Info.value.yqdl == '细菌仪') {
router.push({
name: 'Micro', // 必须用 name 跳转,不能用 path
state: { // 核心:参数存在 state 中
yq: Info.value.yq,
jyrq: Info.value.jyrq,
lisgroup: Info.value.lisgroup,
ybh: Info.value.ybh,
}
});
} else {
router.push({
name: 'Work',
state: {
yq: Info.value.yq,
jyrq: Info.value.jyrq,
lisgroup: Info.value.lisgroup,
ybh: Info.value.ybh,
}
});
}
}
// 单元格样式
const cellStyleHd = ({ row, column, rowIndex, columnIndex }: {
row: any;
column: any;
rowIndex: number;
columnIndex: number;
}) => {
// console.log(row, column, rowIndex, columnIndex);
if (column.title == "急" && row.jzbz == "1") {
return { color: '#f00' };
}
if (column.title == "危" && row.alarmflag == "1") {
return { color: '#f00' };
}
};
// 表格配置
const tableConfig = ref({
height: '71vh', // 高度
// 复选框配置
checkboxConfig: {
// 可选配置项
checkField: 'checked', // 绑定数据中的字段(默认:checked)
range: false, // 是否支持范围选择(按住 Shift 连续选择)
disabled: false, // 是否禁用全部复选框
reserve: false, // 是否保留勾选状态(分页/筛选时)
highlight: false // 是否高亮选中行
}
})
// 勾选未打印
const selectStatusRows = () => {
tableData.value.forEach((item: any) => {
if (item.dybz != 1) {
tableRefs.value.toggleRowSelection(item, true)
} else {
tableRefs.value.toggleRowSelection(item, false)
}
})
}
const alarmHandle = (val: boolean) => {
if (val) {
queryParams.jzbz = false
alarmflag.value = '1'
jzbz.value = null
} else {
alarmflag.value = null
}
getList()
}
const jzbzHandle = (val: boolean) => {
if (val) {
queryParams.alarmflag = false
alarmflag.value = null
jzbz.value = '1'
} else {
jzbz.value = null
}
getList()
}
const ksdhFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', enablePinyinSearch: true },
])
const brlxFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', enablePinyinSearch: true },
])
// 右侧数据源
const rightTableData = ref([])
// 配置项
const rightColumns = ref([
{ prop: 'xmdh', label: '项目代号', align: 'center', visible: true, },
{ prop: 'xmmc', label: '项目名称', align: 'center', visible: true, },
{ prop: 'xmmc', label: '培养结果', align: 'center', visible: true, width: 150 },
{ prop: 'csjg', label: '检验结果', align: 'center', visible: true, },
{ prop: 'refs', label: '参考值', align: 'center', visible: true, },
{ prop: 'dw', label: '单位', align: 'center', visible: true, },
{ prop: 'od', label: '菌落计数', align: 'center', visible: true, },
// { prop: 'jgbz', label: '结果标志', align: 'center', visible: true, slot: 'jgbz' },
])
const rightCellStyleHd = ({ row, column, rowIndex, columnIndex }: {
row: any;
column: any;
rowIndex: number;
columnIndex: number;
}) => {
if (column.label == "检验结果" && (row.alarm_flag ?? "").trim().length > 0) {
return { background: '#f00 !important', color: '#FFF' };
}
if (column.label == "检验结果" && row.jgbz == "H") {
return { background: '#ffc0c0 !important', color: '#606266' };
}
if (column.label == "检验结果" && row.jgbz == "L") {
return { background: '#8080ff !important', color: '#fff' };
}
if (column.label == "检验结果" && row.jgbz == "P") {
return { background: '#ffc0c0 !important', color: '#606266' };
}
if (column.label == "检验结果" && row.jgbz == "Q") {
return { background: '#ffff80 !important', color: '#606266' };
}
};
// 表格配置
const rightTableConfig = ref(
{
// stripe: true, // 斑马纹
border: true, // 边框
height: '68vh', // 高度
highlightCurrentRow: true, // 高亮当前行
cellStyle: rightCellStyleHd
}
)
const formatJgbz = (v: string) => {
const jgbzMap = {
'H': '高',
'L': '低',
'N': '阴性',
'P': '阳性',
'Q': '弱阳性',
'M': '正常'
};
return jgbzMap[v as keyof typeof jgbzMap] ?? '';
}
const expertComment = ref('')
const xmRow = ref<any>({})
// 查询抗生素
const xmrowHandle = (row: any) => {
xmRow.value = row
expertComment.value = row?.textresult || ''
const data = {
jyrq: Info.value.jyrq,
yq: Info.value.yq,
ybh: Info.value.ybh,
xmdh: row.xmdh
}
reportMedList(data).then((res: any) => {
if (res.code == 200) {
bottomTableData.value = res.rows
if (res.rows.length > 0) {
nextTick(() => {
rightTableConfig.value.height = `25vh`
rightTableRef.value?.doLayout && rightTableRef.value.doLayout();
});
} else {
nextTick(() => {
rightTableConfig.value.height = `68vh`
rightTableRef.value?.doLayout && rightTableRef.value.doLayout();
});
}
}
})
}
const periods = ref<string[]>([])
const compareTableData = ref([])
// 右侧数据源
const bottomTableData = ref([])
// 配置项
const bottomColumns = ref([
{ prop: 'ywdh', label: '抗生素代码', align: 'center', visible: true, },
{ prop: 'ywmc', label: '抗生素名称', align: 'center', width: 200, visible: true, },
{ prop: 'mic', label: 'MIC', align: 'center', visible: true },
{ prop: 'rad', label: 'KB', align: 'center', visible: true },
{ prop: 'csjg', label: '药敏结果', align: 'center', visible: true },
])
const botCellStyleHd = ({ row, column, rowIndex, columnIndex }: {
row: any;
column: any;
rowIndex: number;
columnIndex: number;
}) => {
if (column.label == '药敏结果' && row.jgbz == 'R') {
return { background: '#ffc0c0 !important', color: '#606266' };
}
if (column.label == '药敏结果' && row.jgbz.trim() == "") {
return { background: '#ffff80 !important', color: '#606266' };
}
}
// 表格配置
const bottomTableConfig = ref(
{
// stripe: true, // 斑马纹
border: true, // 边框
height: '37vh', // 高度
highlightCurrentRow: true, // 高亮当前行
cellStyle: botCellStyleHd
}
)
interface DictData {
DP?: Array<any>;
PT?: Array<any>;
[key: string]: any[] | undefined; // 添加索引签名以支持动态访问
}
// 字典数据存储
const dictData = ref<DictData>({});
onMounted(async () => {
// 获取当前日期(当天)
const today = dayjs();
// 计算前6天的日期(当天 + 前6天 = 7天)
const startOfWeek = today.subtract(6, 'day');
const startDate = route.query.startdate ? String(route.query.startdate) : startOfWeek.format('YYYY-MM-DD');
const endDate = route.query.endate ? String(route.query.endate) : today.format('YYYY-MM-DD');
// queryParams.times = [startDate, endDate];
getList()
// 加载病人来源字典
const dictRefs = await comDict('PT', 'DP');
// 从 ref 中获取实际数据
dictData.value = {
PT: toRaw(dictRefs.PT.value) || [],
DP: toRaw(dictRefs.DP.value) || [],
};
});
const formatDict = (v: string, dictType: string) => {
return dictData.value[dictType]?.find((item: any) => item.value == v)?.label || v;
};
// 计算两个日期的天数差(支持带时分秒)
const getDayDiff = (start: string, end: string): number => {
const startTime = new Date(start).getTime();
const endTime = new Date(end).getTime();
// 校验日期有效性
if (isNaN(startTime) || isNaN(endTime)) {
return -1; // 无效日期
}
// 计算毫秒差 → 转换为天数(向上取整,避免因时分秒导致的误差)
return Math.ceil((endTime - startTime) / (24 * 60 * 60 * 1000));
};
const getList = () => {
const data = {
alarmflag: alarmflag.value,
jzbz: jzbz.value,
dybz: queryParams.dybz,
ksdh: queryParams.ksdh,
brlyname: queryParams.brlyname,
brxm: queryParams.brxm,
brdh: queryParams.brdh,
ch: queryParams.ch,
userid: queryParams.userid,
st: '',
et: '',
yljg: queryParams.yljg,
}
if (!data.ksdh) return ElMessage.error('科室代号不能为空');
if (!data.yljg) return ElMessage.error('医疗机构yljg不能为空');
// 校验日期格式
const dayDiff = getDayDiff(queryParams.times[0], queryParams.times[1]);
if (dayDiff === -1) return ElMessage.warning('请选择有效的日期范围');
//校验是否超过7天
if (dayDiff > 7) return ElMessage.warning('查询时间范围不能超过7天,请重新选择');
if (queryParams.times && queryParams.times.length) {
data.st = queryParams.times[0]
data.et = queryParams.times[1]
} else {
data.st = ''
data.et = ''
}
loading.value = true
reportList(data).then((res: any) => {
if (res.code == 0) {
loading.value = false
tableData.value = res.data
if (res.data.length > 0) {
tableRefs.value.setCurrentRow(res.data[0])
rowHandle(res.data[0])
} else {
rightTableData.value = []
bottomTableData.value = []
}
}
})
}
</script>
<style scoped lang="scss">
.mytable-style {
height: 68vh;
}
.tips {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px;
color: #409eff;
font-weight: 600;
margin-bottom: 5px;
}
.compact-form {
border-bottom: 1px solid #E1E9F7;
.el-form-item {
margin-bottom: .625rem;
}
}
.title {
border-bottom: 1px solid #E1E9F7;
margin-bottom: .625rem;
padding: .625rem;
font-weight: 700;
}
.time-line-box {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: .625rem;
background-color: #f0f3f8;
padding: .3rem 2.5rem;
}
.time-line-item {
display: flex;
align-items: center;
justify-content: space-around;
width: 16.6%;
// padding: 0 30px;
.timeline-content {
display: flex;
flex-direction: column;
align-items: center;
.index {
width: 1.875rem;
height: 1.875rem;
line-height: 1.875rem;
background: #E1E9F7;
border-radius: 50%;
text-align: center;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 1.125rem;
color: #4767A3;
}
.active {
background-color: #4096ff;
box-shadow: 0 0 0 .25rem rgba(64, 150, 255, 0.2);
color: #FFF;
}
.timeline-title {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 1rem;
color: #4767A3;
}
.timeline-time {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: .875rem;
color: #4767A3;
}
}
.timg {
width: 3.125rem;
}
}
:deep(.el-form-item--default) {
margin-bottom: 1.125rem;
}
.expert-comment {
/* 限制最大显示行数为2行 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* 超出部分隐藏 */
overflow: hidden;
/* 可选:设置行高和最大宽度,优化显示效果 */
line-height: 1.3;
/* 行高,根据字体大小调整 */
max-width: 100%;
pointer-events: auto;
/* 允许鼠标交互 */
cursor: default;
}
</style>