This commit is contained in:
wyuu 2026-01-22 17:39:59 +08:00
parent 030a068b66
commit 4be4bf155f
11 changed files with 966 additions and 158 deletions

View File

@ -163,3 +163,19 @@ export function copyQcSample(data?: Object) {
params: data params: data
}) })
} }
//删除靶值
export function delQcSample(data?: Object) {
return request({
url: '/qc/delQcSample',
method: 'delete',
params: data
})
}
//查询质控室内数据(月统计)
export function getmonthst(data?: Object) {
return request({
url: '/qcstat/getmonthst',
method: 'get',
params: data
})
}

View File

@ -17,10 +17,20 @@ export const usePrintStore = defineStore('printStore', {
}, },
setPrinterList(list: any[]) { setPrinterList(list: any[]) {
this.printerList = list this.printerList = list
this.defaultPrinter = list.find((p) => p.isDefault)?.name || ''
}, },
setDefaultPrinter(printerName: string) { setDefaultPrinter(printerName: string) {
this.defaultPrinter = printerName this.defaultPrinter = printerName
} }
},
// 持久化配置
persist: {
// 指定要持久化的状态字段
pick: ['defaultPrinter',],
// 可选配置:自定义存储键名(默认是store的id)
// key: 'common_store',
// 可选配置:指定存储位置(默认是localStorage 也可以用sessionStorage)
storage: localStorage,
} }
}) })

View File

@ -21,7 +21,8 @@ const useUserStore = defineStore(
roleName: '', roleName: '',
avatar: '', avatar: '',
roles: [], roles: [],
permissions: [] permissions: [],
sysLoginParam: {}, // 登录参数
}), }),
actions: { actions: {
// 单点登录 // 单点登录
@ -48,9 +49,9 @@ const useUserStore = defineStore(
const password = userInfo.password const password = userInfo.password
const code = userInfo.code const code = userInfo.code
const uuid = userInfo.uuid const uuid = userInfo.uuid
const loginParam=userInfo.loginParam const loginParam = userInfo.loginParam
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login(username, password, code, uuid,loginParam).then(res => { login(username, password, code, uuid, loginParam).then(res => {
setToken(res.token) setToken(res.token)
this.token = res.token this.token = res.token
resolve() resolve()
@ -65,8 +66,9 @@ const useUserStore = defineStore(
getInfo().then(res => { getInfo().then(res => {
const user = res.user const user = res.user
const rolelist = user.roles const rolelist = user.roles
this.sysLoginParam = user.sysLoginParam
if (rolelist && rolelist.length > 0) { if (rolelist && rolelist.length > 0) {
this.roleName=rolelist[0].roleName this.roleName = rolelist[0].roleName
} }
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar; const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
@ -78,7 +80,7 @@ const useUserStore = defineStore(
} }
this.id = user.userId this.id = user.userId
this.name = user.userName this.name = user.userName
this.nickName= user.nickName this.nickName = user.nickName
this.avatar = avatar this.avatar = avatar
resolve(res) resolve(res)
}).catch(error => { }).catch(error => {

View File

@ -110,12 +110,11 @@ interface PrintOptions {
* @param urls PDF文件URL数组 * @param urls PDF文件URL数组
* @param options 打印选项(可选) * @param options 打印选项(可选)
*/ */
function silentPrint(urls: string[], options?: PrintOptions) { function silentPrint(urls: string[], printName: string, options?: PrintOptions) {
if (urls.length === 0) return ElMessage.error('没有打印数据'); if (urls.length === 0) return ElMessage.error('没有打印数据');
const printStore = usePrintStore(); const printStore = usePrintStore();
const handlePrint = () => { const handlePrint = () => {
ElMessage.success('正在打印文件,请稍候...'); urls.forEach(url => silentPrintPdf(url, printName, options));
urls.forEach(url => silentPrintPdf(url, options));
}; };
// 已连接直接打印,未连接则初始化后打印 // 已连接直接打印,未连接则初始化后打印
@ -133,14 +132,11 @@ function silentPrint(urls: string[], options?: PrintOptions) {
} }
//静默打印单个PDF文件 //静默打印单个PDF文件
function silentPrintPdf(url: string, options?: PrintOptions) { function silentPrintPdf(url: string, printName: string, options?: PrintOptions) {
const printStore = usePrintStore(); const printStore = usePrintStore();
const templateId = `pdf-print-${Date.now()}`; const templateId = `pdf-print-${Date.now()}`;
const targetPrinter = printStore.defaultPrinter; const targetPrinter = printName;
if (!targetPrinter) { ElMessage.success('正在打印文件,请稍候...');
console.log("未检测到打印机,请先连接打印机!");
return;
}
socket?.emit("news", { socket?.emit("news", {
client: clientInfo, client: clientInfo,
printer: targetPrinter, printer: targetPrinter,
@ -148,7 +144,7 @@ function silentPrintPdf(url: string, options?: PrintOptions) {
templateId: templateId, templateId: templateId,
pdf_path: url, pdf_path: url,
}); });
// console.log(`🚀 PDF打印请求已发送,任务ID:${templateId},打印机:${targetPrinter}`); console.log(`🚀 PDF打印请求已发送,任务ID:${templateId},打印机:${targetPrinter}`);
} }
// 销毁 // 销毁

View File

@ -67,12 +67,14 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" :size="aotuSize" plain @click="cancelHandle">条码作废</el-button> <el-button type="warning" :size="aotuSize" plain @click="cancelHandle">条码作废</el-button>
</el-col> </el-col>
<!-- <el-col :span="1.5">
<el-button type="warning" :size="aotuSize" plain>打印机设置</el-button>
</el-col>-->
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" :size="aotuSize" plain @click="sendSampleHandle">清单打印</el-button> <el-button type="warning" :size="aotuSize" plain @click="sendSampleHandle">清单打印</el-button>
</el-col> </el-col>
<el-col :span="4">
<el-select v-model="printName" placeholder="打印机设置" @visible-change="visibleChange" @change="changePrinter">
<el-option v-for="item in printerList" :key="item.name" :label="item.name" :value="item.name" />
</el-select>
</el-col>
</el-row> </el-row>
<CustomVxeTable :table-data="tableData" :loading="loading" :columns="columns" @current-change="rowHandle" <CustomVxeTable :table-data="tableData" :loading="loading" :columns="columns" @current-change="rowHandle"
@ -186,12 +188,15 @@ const router = useRouter()
const route = useRoute() const route = useRoute()
const userStore = useUserStore() const userStore = useUserStore()
const printStore = usePrintStore(); const printStore = usePrintStore();
const printerList = computed(() => printStore.printerList);
//@ts-ignore //@ts-ignore
import { getDicts } from '@/api/system/dict/data' import { getDicts } from '@/api/system/dict/data'
//@ts-ignore //@ts-ignore
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { lis_reqcx_type } = proxy.useDict("lis_reqcx_type"); //参数类别 const { lis_reqcx_type } = proxy.useDict("lis_reqcx_type"); //参数类别
const compactForm = ref<HTMLElement | null>(null); const printName = ref(printStore.defaultPrinter || '');
const lisReqs: any = ref([]) const lisReqs: any = ref([])
const nickName = ref('') const nickName = ref('')
const dialogVisible = ref(false) const dialogVisible = ref(false)
@ -213,7 +218,7 @@ const queryParams = reactive<QueryParams>({
st: '2025-07-18', st: '2025-07-18',
et: '2025-07-18', et: '2025-07-18',
yljg: '1', yljg: '1',
zt: '2', zt: '1',
queryType: '', queryType: '',
queryValue: '', queryValue: '',
ksdh: '', ksdh: '',
@ -595,6 +600,18 @@ const sendSampleHandle = () => {
}) })
} }
const visibleChange = (visible: boolean) => {
if (visible) {
if (!printerList.value.length) {
HiprintPrinter.initSocket()
}
}
};
const changePrinter = (value: string) => {
printStore.setDefaultPrinter(value);
};
const timeDate = () => { const timeDate = () => {
// 获取当前日期(当天) // 获取当前日期(当天)
const today = dayjs(); const today = dayjs();
@ -735,6 +752,7 @@ const selectStatusRows = () => {
// 打印pdf // 打印pdf
const printPdf = () => { const printPdf = () => {
if (!printName.value) return ElMessage.warning("未设置打印机,请先设置打印机!");
const selections = tableRef.value.allSelection() const selections = tableRef.value.allSelection()
if (selections.length <= 0) return ElMessage.warning('请选择数据!') if (selections.length <= 0) return ElMessage.warning('请选择数据!')
ElMessageBox.confirm(`确定打印选中的数据吗?`, "提示", { ElMessageBox.confirm(`确定打印选中的数据吗?`, "提示", {
@ -752,7 +770,7 @@ const printPdf = () => {
if (res.code == 0) { if (res.code == 0) {
done(); done();
getList() getList()
HiprintPrinter.silentPrint(res.data); HiprintPrinter.silentPrint(res.data, printName.value);
} }
}) })
} else { } else {

View File

@ -88,15 +88,12 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Upload" :size="aotuSize" @click="printPdf">打印报告</el-button> <el-button type="primary" plain icon="Upload" :size="aotuSize" @click="printPdf">打印报告</el-button>
</el-col> </el-col>
<!-- <right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery"
:columns="columns"></right-toolbar> -->
</el-row> </el-row>
<CustomVxeTable :table-data="tableData" :loading="loading" :columns="columns" @current-change="rowHandle" <CustomVxeTable :table-data="tableData" :loading="loading" :columns="columns" @current-change="rowHandle"
size="small" :scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" class="mytable-style" size="small" :scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" class="mytable-style"
ref="tableRefs" :cell-style="cellStyleHd" @selection-change="selectionChange" :config="tableConfig" ref="tableRefs" :row-style="rowClassNameHd" :cell-style="cellStyleHd" @selection-change="selectionChange"
:enable-column-drag="true" @column-drag-end="handleColumnDragEnd"> :config="tableConfig" :enable-column-drag="true" @column-drag-end="handleColumnDragEnd">
<template #dybz="{ row }"> <template #dybz="{ row }">
<svg-icon v-if="row.dybz == 1" icon-class="lvgou" /> <svg-icon v-if="row.dybz == 1" icon-class="lvgou" />
</template> </template>
@ -186,6 +183,8 @@ import { HiprintPrinter } from '@/utils/hiprintPrinter';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
//@ts-ignore //@ts-ignore
import { comDict } from '@/utils/dict' import { comDict } from '@/utils/dict'
//@ts-ignore
import { getDicts } from '@/api/system/dict/data'
import { usePrintStore } from '@/store/modules/printStore' import { usePrintStore } from '@/store/modules/printStore'
//@ts-ignore //@ts-ignore
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
@ -194,7 +193,7 @@ const route = useRoute()
const aotuSize = classCom.useAutoSize(); const aotuSize = classCom.useAutoSize();
const userStore = useUserStore() const userStore = useUserStore()
const compactForm = ref<HTMLElement | null>(null); const compactForm = ref<HTMLElement | null>(null);
const heightForm = ref(90); const lisReqs: any = ref([])
const dialogVisible = ref(false) const dialogVisible = ref(false)
const tableRefs = ref() const tableRefs = ref()
const diaTableRef = ref() const diaTableRef = ref()
@ -413,13 +412,35 @@ const cellStyleHd = ({ row, column, rowIndex, columnIndex }: {
rowIndex: number; rowIndex: number;
columnIndex: number; columnIndex: number;
}) => { }) => {
// console.log(row, column, rowIndex, columnIndex); // // console.log(row, column, rowIndex, columnIndex);
if (column.title == "急" && row.jzbz == "1") { // if (column.title == "急" && row.jzbz == "1") {
return { color: '#f00' }; // return { color: '#f00' };
// }
// if (column.title == "危" && row.alarmflag == "1") {
// return { color: '#f00' };
// }
};
const rowClassNameHd = ({ row, rowIndex }: {
row: any;
rowIndex: number;
}) => {
// 最高优先级:警告/异常
if (row.jzbz == "1" || row.alarmflag == "1") {
return {
backgroundColor: '#f00 !important',
color: '#000 !important',
};
} }
if (column.title == "危" && row.alarmflag == "1") {
return { color: '#f00' }; if (row.jgbz == '2') {
return { background: '#C0FFC0 !important' };
} }
if (row.dybz == '1') {
return { background: '#C0C0C0 !important' };
}
return 'custom-row-class'; // 返回自定义类名
}; };
// 表格配置 // 表格配置
@ -852,8 +873,10 @@ onMounted(async () => {
// 计算前6天的日期(当天 + 前6天 = 7天) // 计算前6天的日期(当天 + 前6天 = 7天)
const startOfWeek = today.subtract(6, 'day'); const startOfWeek = today.subtract(6, 'day');
queryParams.st = route.query.startdate ? String(route.query.startdate) : startOfWeek.format('YYYY-MM-DD'); // queryParams.st = route.query.startdate ? String(route.query.startdate) : startOfWeek.format('YYYY-MM-DD');
queryParams.et = route.query.endate ? String(route.query.endate) : today.format('YYYY-MM-DD'); // queryParams.et = route.query.endate ? String(route.query.endate) : today.format('YYYY-MM-DD');
queryParams.st = '2025-08-07'
queryParams.et = '2025-08-14'
queryParams.ksdh = (route.query.deptcode as string) ? (route.query.deptcode as string) : ''; queryParams.ksdh = (route.query.deptcode as string) ? (route.query.deptcode as string) : '';
@ -863,6 +886,16 @@ onMounted(async () => {
// 进入页面单点登录 // 进入页面单点登录
const result = await userStore.loginAnonymous({ name: queryParams.userid, yljg: queryParams.yljg }) const result = await userStore.loginAnonymous({ name: queryParams.userid, yljg: queryParams.yljg })
if (result) { if (result) {
// 获取状态字典
getDicts('lis_req_type').then((resp: any) => {
lisReqs.value = resp.data.map((p: any) => ({
label: p.dictLabel,
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass
}))
})
getList() getList()
} }
@ -1044,5 +1077,7 @@ const getList = () => {
pointer-events: auto; pointer-events: auto;
/* 允许鼠标交互 */ /* 允许鼠标交互 */
cursor: default; cursor: default;
font-size: .875rem;
margin-top: .3125rem;
} }
</style> </style>

View File

@ -2,62 +2,141 @@
<div class="app-container"> <div class="app-container">
<div ref="compactForm"> <div ref="compactForm">
<el-row :gutter="5" class="compact-form" v-show="showSearch"> <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-form :model="queryParams" ref="queryRef" label-width="5rem" :rules="queryRules" style="width: 100%;"
size="small">
<el-row> <el-row>
<el-col :span="6"> <el-col :span="3">
<el-form-item label="标本日期:" prop="times"> <el-form-item label="部门组别:" prop="instrGroup">
<el-date-picker v-model="queryParams.times" type="daterange" range-separator="-" <SelectTable v-model:data="queryParams.instrGroup" :fields="instrGroupFields"
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :tableData="instrGroupOptions" label="zdmc" value="zddh" objKey="zddh" :border="true" size="small"
@change="handletime" /> placeholder="请选择部门" :clearable="false" @get-data-value="getYqConfig" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="3">
<el-form-item label="打印状态:" prop="dybz"> <el-form-item label="仪器:" prop="yq">
<el-select v-model="queryParams.dybz" placeholder="请选择" clearable @change="handleQuery"> <SelectTable v-model:data="queryParams.yq" :tableData="instrOptions" width="100%" placeholder="请选择仪器"
<el-option label="已打印" value="1" /> clearable size="small" @getDataValue="getXmList" />
<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-form-item>
</el-col> </el-col>
</el-row> <el-col :span="3">
<el-row> <el-form-item label="申请号:" prop="sqh">
<el-col :span="6"> <el-input v-model="queryParams.sqh" clearable placeholder="请输入" @keyup.enter="handleQuery"
<el-form-item label-width="50px"> @clear="handleQuery" />
<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-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="3">
<el-form-item label="身份证号:" prop="brxm">
<el-input v-model="queryParams.sqh" clearable placeholder="请输入" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="病人类型:" prop="brly">
<SelectTable v-model:data="queryParams.brly" dictType="PT" placeholder="请选择" size="small"
@getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="送检医生:" prop="sjys">
<SelectTable v-model:data="queryParams.sjys" :fields="ysFields" :tableData="userList || []" size="small"
label="nickName" value="userName" objKey="userName" placeholder="请选择" @getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="检验医生:" prop="yhdh">
<SelectTable v-model:data="queryParams.yhdh" :fields="ysFields" :tableData="userList || []" size="small"
label="nickName" value="userName" objKey="userName" placeholder="请选择" @getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="标本类型:" prop="yblx">
<SelectTable v-model:data="queryParams.yblx" dictType="BT" placeholder="请选择" size="small"
@getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="标本号:" prop="brxm">
<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="检验项:" prop="brxm">
<SelectTable v-model:data="queryParams.yblx" :tableData="xmList" placeholder="请选择" size="small"
@getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="科室:" prop="ksdh">
<SelectTable v-model:data="queryParams.ksdh" dictType='DP' placeholder="请选择科室" size="small"
@getDataValue="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="姓名:" prop="brxm"> <el-form-item label="姓名:" prop="brxm">
<el-input v-model="queryParams.brxm" clearable placeholder="请输入姓名" @keyup.enter="handleQuery" <el-input v-model="queryParams.brxm" clearable placeholder="请输入姓名" @keyup.enter="handleQuery"
@clear="handleQuery" /> @clear="handleQuery" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="3">
<el-form-item label="病人号:" prop="brdh"> <el-form-item label="病人号:" prop="brdh">
<el-input v-model="queryParams.brdh" clearable placeholder="请输入病人号" @keyup.enter="handleQuery" <el-input v-model="queryParams.brdh" clearable placeholder="请输入病人号" @keyup.enter="handleQuery"
@clear="handleQuery" /> @clear="handleQuery" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="3">
<el-form-item label="床号:" prop="ch"> <el-form-item label="床号:" prop="ch">
<el-input v-model="queryParams.ch" clearable placeholder="请输入床号" @keyup.enter="handleQuery" <el-input v-model="queryParams.ch" clearable placeholder="请输入床号" @keyup.enter="handleQuery"
@clear="handleQuery" /> @clear="handleQuery" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="3">
<el-form-item label="打印状态:" prop="dybz">
<el-select v-model="queryParams.dybz" placeholder="请选择" clearable @change="handleQuery">
<el-option label="未打印" value="0" />
<el-option label="已打印" value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="审核状态:" prop="jgbz">
<el-select v-model="queryParams.jgbz" placeholder="请选择" clearable @change="handleQuery">
<el-option label="未审核" value="0" />
<el-option label="已审核" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="标本日期:" prop="times">
<el-date-picker v-model="queryParams.st" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="handletime" style="width: 47%" /> &nbsp; - &nbsp;
<el-date-picker v-model="queryParams.et" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="handletime" style="width: 47%" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="备注:" prop="bz">
<el-input v-model="queryParams.bz" clearable placeholder="请输入" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="医疗机构:" prop="yljg">
<SelectTable v-model:data="queryParams.yljg" dictType="HOS" placeholder="请选择" size="small"
@getDataValue="handleQuery" />
</el-form-item>
</el-col>
&nbsp;
<el-checkbox v-model="queryParams.jzbz" label="急诊" true-value="1" false-value="0" />
<el-checkbox v-model="queryParams.dcny" label="多重耐药(细菌查询用)" true-value="1" false-value="0" />
<el-col :span="3"> <el-col :span="3">
<el-form-item label-width="50px"> <el-form-item label-width="50px">
<el-button icon="search" type="primary" @click="handleQuery"> <el-button icon="search" type="primary" @click="handleQuery">
@ -116,6 +195,20 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<!-- 样本流程 -->
<div class="time-line-box">
<div v-for="(item, i) in timelineItems" :key="i" class="time-line-item">
<div v-if="i != 0" class="timeline-connector">
<img class="timg" src="@/assets/images/yjt.png" alt="">
</div>
<div :class="['timeline-content', i > lastTimeIndex ? 'no_active' : '']">
<div :class="['index', lastTimeIndex == i ? 'active' : '']">{{ i + 1 }}</div>
<div class="timeline-title">{{ item.title }}</div>
<div class="timeline-time">{{ item.time }}</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
@ -129,23 +222,30 @@ import SelectTable from '@/components/SelectTable/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { classCom } from '@/utils/classCom' import { classCom } from '@/utils/classCom'
import { HiprintPrinter } from '@/utils/hiprintPrinter'; import { HiprintPrinter } from '@/utils/hiprintPrinter';
import * as echarts from 'echarts'; import YQSelectTable from '@/components/SelectTable/YQSelectTable/index.vue';
//@ts-ignore // @ts-ignore
import { comDict } from '@/utils/dict' import { listhospital } from "@/api/regional/dict/hospital.js";
// @ts-ignore
import { listUser } from '@/api/system/user.js'
import { usePrintStore } from '@/store/modules/printStore' import { usePrintStore } from '@/store/modules/printStore'
//@ts-ignore //@ts-ignore
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { getComDicts } from '@/api/liswork/dict/ComDict';
import { getGroupInstrdList, queryXmInfo } from '@/api/liswork/work/LisWork';
const route = useRoute() const route = useRoute()
const router = useRouter(); const router = useRouter();
const aotuSize = classCom.useAutoSize(); const aotuSize = classCom.useAutoSize();
const userStore = useUserStore()
const tableRefs = ref() const tableRefs = ref()
interface QueryParams { interface QueryParams {
times: string[]; // 明确类型为字符串元组 // times: string[]; // 明确类型为字符串元组
instrGroup: string;
yq: string;
st: string;
et: string;
alarmflag: boolean, alarmflag: boolean,
jzbz: boolean, jzbz: boolean,
ksdh: string, ksdh: string,
@ -157,14 +257,25 @@ interface QueryParams {
sqh: string, sqh: string,
userid: string, userid: string,
yljg: string, yljg: string,
bz: string,
yblx: string,
yhdh: string,
sjys: string,
brly: string,
jgbz: string,
dcny: string
} }
// 提交表单数据 // 提交表单数据
const queryParams = reactive<QueryParams>({ const queryParams = reactive<QueryParams>({
times: ['2025-08-19', '2025-08-19'], // times: ['2025-08-19', '2025-08-19'],
instrGroup: '',
yq: '',
st: '',
et: '',
ksdh: '0210', ksdh: '0210',
brlyname: '', brlyname: '',
sqh: '25080109476', sqh: '',
alarmflag: false, alarmflag: false,
jzbz: false, jzbz: false,
brxm: '', brxm: '',
@ -172,7 +283,14 @@ const queryParams = reactive<QueryParams>({
ch: '', ch: '',
dybz: '', dybz: '',
userid: '', userid: '',
yljg: '1' yljg: userStore.sysLoginParam.loginYLJG,
bz: '',
yblx: '',
yhdh: '',
sjys: '',
brly: '',
jgbz: '',
dcny: ''
}); });
const single = ref(true); const single = ref(true);
@ -187,18 +305,39 @@ const Info = ref<any>({})
const queryRules = ref({ const queryRules = ref({
}) })
const ysFields = ref([
{ prop: 'userName', label: '用户代号', width: 80, enablePinyinSearch: true },
{ prop: 'nickName', label: '用户姓名', width: 150, enablePinyinSearch: true },
])
const handletime = (val: Array<string>) => { const handletime = (val: Array<string>) => {
// console.log('时间', val);
// 校验日期格式 // 校验日期格式
const dayDiff = getDayDiff(val[0], val[1]); const dayDiff = getDayDiff(queryParams.st, queryParams.et);
if (dayDiff === -1) return ElMessage.warning('请选择有效的日期范围'); if (dayDiff === -1 || dayDiff === -2) return ElMessage.warning('请选择有效的日期范围');
//校验是否超过7天
if (dayDiff > 7) return ElMessage.warning('查询时间范围不能超过7天,请重新选择');
} }
// 时间线数据
const timelineItems = ref(
[
{ title: '检验申请', time: '' },
{ title: '标本采集', time: '' },
{ title: '上机检验', time: '' },
{ title: '标本送检', time: '' },
// { title: '检验接收', time: '' },
{ title: '标本签收', time: '' },
{ title: '结果审核', time: '' },
]
);
// 计算最后一个有时间的节点索引
const lastTimeIndex = computed(() => {
const lastIndex = timelineItems.value.findLastIndex(item => item.time);
if (lastIndex === -1) {
return -1
} else {
return lastIndex;
}
});
const handleQuery = async () => { const handleQuery = async () => {
const valid = await queryRef.value.validate().catch(() => { }); const valid = await queryRef.value.validate().catch(() => { });
@ -245,7 +384,7 @@ const columns = ref([
{ field: 'hdys', title: '核对医生', align: 'center', resizable: true, width: 60 }, { field: 'hdys', title: '核对医生', align: 'center', resizable: true, width: 60 },
{ field: 'zd', title: '临床诊断', align: 'center', resizable: true, width: 100 }, { field: 'zd', title: '临床诊断', align: 'center', resizable: true, width: 100 },
{ field: 'bz', 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: 'shysname', title: '审核人', align: 'center', resizable: true, width: 60 },
{ field: 'confirmtime', title: '审核时间', align: 'center', resizable: true, width: 80 }, { field: 'confirmtime', title: '审核时间', align: 'center', resizable: true, width: 80 },
{ field: 'zh', title: '最后修改人', align: 'center', resizable: true, width: 100 }, { field: 'zh', title: '最后修改人', align: 'center', resizable: true, width: 100 },
{ field: 'zhtime', title: '最后修改时间', align: 'center', resizable: true, width: 100 }, { field: 'zhtime', title: '最后修改时间', align: 'center', resizable: true, width: 100 },
@ -276,6 +415,16 @@ const rowHandle = (row: any) => {
rightColumns.value[6].visible = false rightColumns.value[6].visible = false
} }
timelineItems.value =
[
{ title: '检验申请', time: row.sqsj },
{ title: '标本采集', time: row.cysj },
{ title: '上机检验', time: row.zxsj },
{ title: '标本送检', time: row.bbsjsj },
// { title: '检验接收', time: row.jssj},
{ title: '标本签收', time: row.jssj },
{ title: '结果审核', time: row.confirmtime },
]
Info.value = row Info.value = row
const data = { const data = {
@ -416,18 +565,22 @@ const jzbzHandle = (val: boolean) => {
getList() getList()
} }
const xmList = ref([])
const getXmList = () => {
const ksdhFields = ref([ const data = {
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true }, yq: queryParams.yq,
{ prop: 'label', label: '名称', enablePinyinSearch: true }, pageSize: 1000,
]) pageNum: 1
}
const brlxFields = ref([ queryXmInfo(data).then((res: any) => {
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true }, xmList.value = res.data.map((item: any) => {
{ prop: 'label', label: '名称', enablePinyinSearch: true }, return {
]) label: item.xmmc,
value: item.xmdh
}
})
})
}
// 右侧数据源 // 右侧数据源
@ -472,7 +625,7 @@ const rightTableConfig = ref(
{ {
// stripe: true, // 斑马纹 // stripe: true, // 斑马纹
border: true, // 边框 border: true, // 边框
height: '68vh', // 高度 height: '63vh', // 高度
highlightCurrentRow: true, // 高亮当前行 highlightCurrentRow: true, // 高亮当前行
cellStyle: rightCellStyleHd cellStyle: rightCellStyleHd
} }
@ -512,7 +665,7 @@ const xmrowHandle = (row: any) => {
}); });
} else { } else {
nextTick(() => { nextTick(() => {
rightTableConfig.value.height = `68vh` rightTableConfig.value.height = `63vh`
rightTableRef.value?.doLayout && rightTableRef.value.doLayout(); rightTableRef.value?.doLayout && rightTableRef.value.doLayout();
}); });
} }
@ -520,12 +673,6 @@ const xmrowHandle = (row: any) => {
}) })
} }
const periods = ref<string[]>([])
const compareTableData = ref([])
// 右侧数据源 // 右侧数据源
const bottomTableData = ref([]) const bottomTableData = ref([])
// 配置项 // 配置项
@ -554,63 +701,83 @@ const bottomTableConfig = ref(
{ {
// stripe: true, // 斑马纹 // stripe: true, // 斑马纹
border: true, // 边框 border: true, // 边框
height: '37vh', // 高度 height: '32vh', // 高度
highlightCurrentRow: true, // 高亮当前行 highlightCurrentRow: true, // 高亮当前行
cellStyle: botCellStyleHd cellStyle: botCellStyleHd
} }
) )
interface DictData {
DP?: Array<any>;
PT?: Array<any>; const instrGroupOptions = ref<any[]>([])
[key: string]: any[] | undefined; // 添加索引签名以支持动态访问 const instrGroupFields = ref([
{ prop: 'zddh', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'zdmc', label: '名称', width: 150, enablePinyinSearch: true },
])
// 部门
const getinstrGroupOptions = () => {
getComDicts({ zdlb: 'GROUP' }).then((res: any) => {
instrGroupOptions.value = res.data
instrGroupOptions.value.push({ zddh: 'ALL', zdmc: '所有仪器' })
// getYqConfig()
});
} }
const instrOptions = ref([])
// 仪器
// 字典数据存储 const getYqConfig = () => {
const dictData = ref<DictData>({}); getGroupInstrdList({ lisgroup: queryParams.instrGroup })
.then((res: any) => {
if (res.code == 0) {
instrOptions.value = res.data.map((item: any) => ({
value: item.yq.trim(),
label: item.yqmc
}))
}
})
}
const userList = ref([]);
const hosList = ref([]);
onMounted(async () => { onMounted(async () => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res: any) => {
userList.value = res.rows;
});
getinstrGroupOptions()
// 获取当前日期(当天) // 获取当前日期(当天)
const today = dayjs(); const today = dayjs();
// 计算前6天的日期(当天 + 前6天 = 7天) // 计算前6天的日期(当天 + 前6天 = 7天)
const startOfWeek = today.subtract(6, 'day'); const startOfWeek = today.subtract(6, 'day');
const startDate = route.query.startdate ? String(route.query.startdate) : startOfWeek.format('YYYY-MM-DD'); queryParams.st = 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.et = route.query.endate ? String(route.query.endate) : today.format('YYYY-MM-DD');
// queryParams.times = [startDate, endDate]; // queryParams.times = [startDate, endDate];
listhospital().then((res: any) => {
hosList.value = res.rows.map((item: any) => {
return {
label: item.hospitalName,
value: item.hospitalId.toString()
}
})
})
getList() 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 getDayDiff = (start: string, end: string): number => {
const startTime = new Date(start).getTime(); const startTime = new Date(start).getTime();
const endTime = new Date(end).getTime(); const endTime = new Date(end).getTime();
// 校验日期有效性
if (isNaN(startTime) || isNaN(endTime)) { if (isNaN(startTime) || isNaN(endTime)) {
return -1; // 无效日期 return -2; // -2 表示日期无效
} }
// 计算毫秒差 → 转换为天数(向上取整,避免因时分秒导致的误差) const dayDiff = Math.ceil((endTime - startTime) / (24 * 60 * 60 * 1000));
return Math.ceil((endTime - startTime) / (24 * 60 * 60 * 1000)); return dayDiff < 0 ? -1 : dayDiff; // -1 表示结束时间早于开始时间,>=0 为正常天数
}; };
const getList = () => { const getList = () => {
@ -624,27 +791,17 @@ const getList = () => {
brdh: queryParams.brdh, brdh: queryParams.brdh,
ch: queryParams.ch, ch: queryParams.ch,
userid: queryParams.userid, userid: queryParams.userid,
st: '', st: queryParams.st,
et: '', et: queryParams.et,
yljg: queryParams.yljg, yljg: queryParams.yljg,
} }
if (!data.ksdh) return ElMessage.error('科室代号不能为空'); if (!data.ksdh) return ElMessage.error('科室代号不能为空');
if (!data.yljg) return ElMessage.error('医疗机构yljg不能为空'); if (!data.yljg) return ElMessage.error('医疗机构yljg不能为空');
// 校验日期格式 // 校验日期格式
const dayDiff = getDayDiff(queryParams.times[0], queryParams.times[1]); const dayDiff = getDayDiff(queryParams.st, queryParams.et);
if (dayDiff === -1) return ElMessage.warning('请选择有效的日期范围'); if (dayDiff === -1 || dayDiff === -2) 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 loading.value = true
reportList(data).then((res: any) => { reportList(data).then((res: any) => {
if (res.code == 0) { if (res.code == 0) {
@ -667,7 +824,7 @@ const getList = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.mytable-style { .mytable-style {
height: 68vh; height: 63vh;
} }
.tips { .tips {
@ -682,7 +839,7 @@ const getList = () => {
border-bottom: 1px solid #E1E9F7; border-bottom: 1px solid #E1E9F7;
.el-form-item { .el-form-item {
margin-bottom: .625rem; margin-bottom: .3125rem;
} }
} }

View File

@ -43,7 +43,7 @@
{{ row.xmdh }} {{ row.xmmc }} {{ row.xmdh }} {{ row.xmmc }}
</template> </template>
<template #zkbz="{ row }"> <template #zkbz="{ row }">
<el-input v-model="row.zkbz" class="full-width-input" /> <el-input v-model="row.zkbz" class="full-width-input" @change="handleZkbzEnter(row)" />
</template> </template>
<template #sd="{ row }"> <template #sd="{ row }">
<el-input v-model="row.sd" class="full-width-input" @change="handleSdEnter(row)" /> <el-input v-model="row.sd" class="full-width-input" @change="handleSdEnter(row)" />
@ -90,8 +90,8 @@
<template #sjph="{ row }"> <template #sjph="{ row }">
{{ formatDict(row.sjph, 'LOT') }} {{ formatDict(row.sjph, 'LOT') }}
</template> </template>
<template #action="{ row }"> <template #action="{ row, $index }">
<el-button type="danger" text @click="delBzHandle(row)">删除</el-button> <el-button type="danger" v-if="$index != 0" text @click="delBzHandle(row)">删除</el-button>
</template> </template>
</CustomTable> </CustomTable>
</div> </div>
@ -193,7 +193,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { queryBatch, queryValXmdh, queryBatchXm, delBatchno, addBatchno, updateBatchno, saveBtQcSample, copyQcSample } from '@/api/liswork/qualityControl' import { queryBatch, queryValXmdh, queryBatchXm, delBatchno, addBatchno, updateBatchno, saveBtQcSample, copyQcSample, delQcSample } from '@/api/liswork/qualityControl'
import { templateRef } from '@vueuse/core' import { templateRef } from '@vueuse/core'
import { formatDict, getDictData, dictData } from '@/hooks'; import { formatDict, getDictData, dictData } from '@/hooks';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
@ -393,6 +393,11 @@ const batchClick = (row: any) => {
getXmList(row) getXmList(row)
} }
const handleZkbzEnter = (row: any) => {
// cv = sd * 100 / zkbz
row.cv = row.sd * 100 / row.zkbz
}
const handleSdEnter = (row: any) => { const handleSdEnter = (row: any) => {
// cv = sd * 100 / zkbz // cv = sd * 100 / zkbz
row.cv = row.sd * 100 / row.zkbz row.cv = row.sd * 100 / row.zkbz
@ -412,6 +417,7 @@ const getBatchList = () => {
tableData.value = res.data || [] tableData.value = res.data || []
if (tableData.value.length) { if (tableData.value.length) {
getXmList(tableData.value[0]) getXmList(tableData.value[0])
batchInfo.value = tableData.value[0]
leftTableRef.value.setCurrentRow(tableData.value[0]) leftTableRef.value.setCurrentRow(tableData.value[0])
} else { } else {
topTableData.value = [] topTableData.value = []
@ -446,7 +452,7 @@ const subHandle = () => {
zkph: selectedRow.value.zkph, zkph: selectedRow.value.zkph,
newyq: copyFormData.value.yq, newyq: copyFormData.value.yq,
newzkpph: copyFormData.value.newzkpph, newzkpph: copyFormData.value.newzkpph,
newzkph: selectedRow.value.zkph, newzkph: copyFormData.value.newzkph,
newstartda: copyFormData.value.newstartda, newstartda: copyFormData.value.newstartda,
newdtflag: copyFormData.value.newdtflag newdtflag: copyFormData.value.newdtflag
} }
@ -481,7 +487,12 @@ const delBzHandle = (row: any) => {
ElMessageBox.confirm('确定要删除这个靶值吗,可能会影响其区间内的质控计算?', '提示', { ElMessageBox.confirm('确定要删除这个靶值吗,可能会影响其区间内的质控计算?', '提示', {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
console.log("🚀 ~ delBzHandle ~ row:", row) delQcSample(row).then((res: any) => {
if (res.code == 0) {
ElMessage.success('删除成功')
batchClick(batchInfo.value)
}
})
}) })
} }
const handleSave = () => { const handleSave = () => {

View File

@ -0,0 +1,173 @@
/**
* @file index.vue 室内质控数据统计表
* @author: w
* @since: 2026-01-22
*/
<template>
<div class="app-container">
<el-form :model="queryParams" inline class="demo-form-inline">
<el-form-item label="仪器:">
<YQSelectTable v-model:data="queryParams.yq" style="width: 150px;" />
</el-form-item>
<el-form-item label="质控月份:">
<el-date-picker v-model="queryParams.zkrq" type="month" placeholder="请选择检查时间" value-format="YYYY-MM" />
</el-form-item>
<el-form-item label="质控品号:">
<el-input v-model="queryParams.zkph" placeholder="请输入" />
</el-form-item>
<el-form-item label="质控批号:">
<el-input v-model="queryParams.zkpph" placeholder="请输入" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getList">查询</el-button>
</el-form-item>
</el-form>
<div>
<el-button type="primary" plain @click="printHandle">打印</el-button>
<el-button type="primary" plain @click="viewHandle">预览</el-button>
<el-button type="primary" plain @click="skHandle">失控总结</el-button>
<el-button type="primary" plain @click="byHandle">本月总结</el-button>
</div>
<div class="title">{{ formatDict(userStore.sysLoginParam.loginYLJG, 'HOS') }}</div>
<div class="content">室内质控数据统计表</div>
<div class="info">
<div> 专业组:<span></span></div>
<div> 仪器:<span>{{ queryParams.yq }}</span> </div>
<div> 浓度: <span></span></div>
<div> 质控品号:<span>{{ queryParams.zkph }}</span> </div>
<div> 质控月份:<span>{{ queryParams.zkrq }}</span> </div>
</div>
<div class="table-container">
<el-table :data="tableData" height="100%" border highlightCurrentRow>
<el-table-column label="质控项目" prop="xmdh" align="center" />
<el-table-column label="测定数" prop="cnt" align="center" />
<el-table-column label="失控数" prop="badcnt" align="center" />
<el-table-column label="失控率%" prop="skl" align="center" />
<el-table-column label="处理率%" prop="cll" align="center" />
<el-table-column label="绘图基准数据" align="center">
<el-table-column label="靶值" prop="avg0" align="center" />
<el-table-column label="基准SD" prop="sd0" align="center" />
<el-table-column label="CV(%)" prop="cv0" align="center" />
</el-table-column>
<el-table-column label="本月原始数据" align="center">
<el-table-column label="原始均值" prop="avg1" align="center" />
<el-table-column label="SD" prop="sd1" align="center" />
<el-table-column label="CV(%)" prop="cv1" align="center" />
</el-table-column>
<el-table-column label="本月非失控数据" align="center">
<el-table-column label="在控均值" prop="avg2" align="center" />
<el-table-column label="SD" prop="sd2" align="center" />
<el-table-column label="CV(%)" prop="cv2" align="center" />
</el-table-column>
<el-table-column label="累计数据" align="center">
<el-table-column label="累计次数" prop="cnt3" align="center" />
<el-table-column label="累计均值" prop="avg3" align="center" />
<el-table-column label="SD" prop="sd3" align="center" />
<el-table-column label="CV(%)" prop="cv3" align="center" />
</el-table-column>
<el-table-column label="质控批号" prop="zkpph" align="center" />
<el-table-column label="标准CV(%)" prop="qccv" align="center" />
</el-table>
</div>
<el-dialog v-model="dialogVisible" :title="title" width="30%">
<el-input v-model="inputValue" type="textarea" :rows="10" placeholder="请输入内容" />
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="subHandle">确 定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="IndoorStatistics">
import { getDictData, formatDict } from '@/hooks'
import { useCommonStore } from '@/store/modules/commonStore';
import useUserStore from '@/store/modules/user'
import YQSelectTable from '@/components/SelectTable/YQSelectTable/index.vue'
import { getmonthst } from '@/api/liswork/qualityControl/index'
import dayjs from 'dayjs';
const userStore = useUserStore()
const mbStore = useCommonStore();
const tableData = ref([])
const queryParams = reactive({
yq: mbStore.defaultConfig.defaultinstr || '1',
zkrq: dayjs().format('YYYY-MM'),
zkph: '1',
zkpph: ''
})
const inputValue = ref('')
const dialogVisible = ref(false)
const title = ref('')
const printHandle = () => {
}
const viewHandle = () => {
}
const skHandle = () => {
title.value = '失控总结'
dialogVisible.value = true
}
const byHandle = () => {
title.value = '本月总结'
dialogVisible.value = true
}
const subHandle = () => {
inputValue.value = ''
dialogVisible.value = false
}
const getList = () => {
getmonthst(queryParams).then((res) => {
tableData.value = res.data || []
})
}
getList()
onMounted(() => {
getDictData('HOS')
})
</script>
<style scoped lang="scss">
.title {
font-size: 20px;
text-align: center;
font-weight: 600;
}
.content {
font-size: 16px;
text-align: center;
margin-top: 10px;
font-weight: 600;
}
.info {
display: flex;
justify-content: space-around;
margin: 10px 0;
span {
display: inline-block;
padding: 5px 10px;
border-bottom: 1px solid #000;
min-width: 100px;
text-align: center;
}
}
.table-container {
height: calc(100% - 200px);
}
</style>

View File

@ -0,0 +1,372 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="40px">
<!-- 原有表单项 -->
<el-form-item label="区间">
<el-date-picker v-model="queryParams.st" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
style="width:150px" /> &nbsp; - &nbsp;
<el-date-picker v-model="queryParams.et" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
style="width:150px" />
</el-form-item>
<el-form-item label="仪器">
<SelectTable v-model:data="queryParams.yq" :tableData="instrOptions" width="150px" clearable
@get-data-value="getDataValue" />
</el-form-item>
<el-form-item>
<el-checkbox label="不分批号" value="1" />
<el-checkbox label="多图" v-model="isMultiChart" @change="getEcharts" />
<el-checkbox label="最好值画图" value="1" />
</el-form-item>
<div>
<el-form-item label=" ">
<el-radio-group v-model="queryParams.radio1">
<el-radio value="1">单仪器</el-radio>
<el-radio value="2">多仪器</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label=" ">
<el-checkbox-group v-model="selectedLevels" @change="handleLevelChange">
<el-checkbox v-for="level in 6" :key="level" :label="`水平${level}`" :value="level" />
</el-checkbox-group>
</el-form-item>
</div>
</el-form>
<el-row :gutter="10" class="table-row">
<!-- 原有表格和图表容器 -->
<el-col :span="4" class="table-col">
<div style="height: 30%">
<CustomTable ref="topTableRef" :data="topTableData" :columns="topColumns"
:config="{ border: true, highlightCurrentRow: true, height: '100%', }" />
</div>
<div style="height: 70%">
<CustomTable ref="bottomTableRef" :data="bottomTableData" :columns="bottomColumns"
:config="{ border: true, highlightCurrentRow: true, height: '100%', }">
<template #xmdh="{ row }">
{{ row.xmdh }} {{ row.xmmc }}
</template>
</CustomTable>
</div>
</el-col>
<el-col :span="20" class="table-col">
<div class="tips">
仪器:{{ yqmc }} &nbsp; 质控项目:{{ xmInfo.xmdh }} {{ xmInfo.xmmc }} &nbsp;
区间:{{ queryParams.st }} - {{ queryParams.et }}
</div>
<div class="echarts-box">
<div v-if="isMultiChart" class="charts-container">
<div v-for="(item, index) in filteredChartData" :key="index" ref="multiEcharts"
style="width: 100%; height: 300px; margin-bottom: 10px;"></div>
</div>
<div v-else ref="myEcharts" style="width: 100%; height: 300px;"></div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { getGroupInstrdList } from '@/api/liswork/work/LisWork';
import dayjs from 'dayjs';
import { queryBatch, queryBatchXm } from '@/api/liswork/qualityControl';
import { useCommonStore } from '@/store/modules/commonStore';
const mbStore = useCommonStore();
import * as echarts from 'echarts';
const isMultiChart = ref(false);
// 原始水平图表数据
const chartData = ref([
{ label: '水平1', arr: [1, 2, 0, 4, 5, 3] },
{ label: '水平2', arr: [2, -3, 2, 3, 4, 5] },
{ label: '水平3', arr: [5, 4, 3, -2, 1, -2.3] },
{ label: '水平4', arr: [0, 1, 2.1, 3, 4, 5] },
{ label: '水平5', arr: [3, 3, -3, 3.2, 3, 3] },
{ label: '水平6', arr: [6, 5, -4, 3, 2, 1] },
]);
// 定义选中的水平数组(双向绑定复选框)
const selectedLevels = ref([1, 2]); // 默认选中水平1
// 计算属性 - 根据选中的水平过滤图表数据
const filteredChartData = computed(() => {
if (selectedLevels.value.length === 0) return []; // 未选中时返回空
return chartData.value.filter(item => {
// 从label中提取水平数字(如"水平1"→1)
const level = Number(item.label.replace('水平', ''));
return selectedLevels.value.includes(level);
});
});
const queryParams = reactive({
yq: mbStore.defaultConfig.defaultinstr || '46',
st: "",
et: "",
radio1: "1"
});
const yqmc = ref("")
const topTableData = ref([])
const topColumns = [
{ label: "批号", prop: "zkpph", visible: true },
{ label: "启用日期", prop: "startda", visible: true },
];
const bottomTableData = ref([])
const bottomColumns = [
{ label: "项目", prop: "xmdh", visible: true, slot: 'xmdh' },
];
const getBatchList = async () => {
const res = await queryBatch(queryParams);
if (res.code == 0) {
topTableData.value = res.data;
if (res.data.length) {
getXmList(res.data[0])
} else {
bottomTableData.value = []
}
}
};
const xmInfo = ref({})
const getXmList = async (row) => {
queryBatchXm({ yq: queryParams.yq, zkpph: row.zkpph, zkph: row.zkph }).then((res) => {
bottomTableData.value = res.data
xmInfo.value = res.data[0]
getEcharts()
});
};
// 单/多图表容器
const myEcharts = ref();
const multiEcharts = ref([]);
// 颜色配置(扩展到6个水平)
const color = ['#6696ee', '#5da761', '#2b63b3', '#ff646a', '#f99b11', '#9370db'];
// 封装转折点颜色判断函数
const getSymbolColor = (value, defaultColor) => {
if (Math.abs(value) > 3) {
return '#ff0000'; // 红色
} else if (Math.abs(value) > 2) {
return '#ffc0cb'; // 粉色
}
return defaultColor; // 其他值用默认颜色
};
const getEcharts = () => {
const xAxisData = ['1', '2', '3', '4', '5', '6'];
// 多图模式
if (isMultiChart.value) {
// 清空原有图表实例
multiEcharts.value.forEach(chartDom => {
if (chartDom) {
const chartInstance = echarts.getInstanceByDom(chartDom);
if (chartInstance) chartInstance.dispose();
}
});
// 遍历过滤后的水平数据渲染图表
filteredChartData.value.forEach((item, index) => {
const chartDom = multiEcharts.value[index];
if (!chartDom) return;
const instance = echarts.init(chartDom);
const seriesData = [{
name: item.label,
type: 'line',
symbol: 'circle',
showAllSymbol: true,
symbolSize: 7,
smooth: true,
lineStyle: { color: color[index], width: 1.5 }, // 线条保持原有颜色
itemStyle: {
// 按数据点数值动态设置转折点颜色
color: (params) => getSymbolColor(params.value, color[index]),
shadowColor: color[index],
shadowBlur: index === 0 ? 10 : 0,
},
data: item.arr,
}];
const option = {
backgroundColor: 'transparent',
tooltip: { show: true },
grid: { top: '10%', left: '2%', right: '10%', bottom: '10%', containLabel: true },
legend: {
show: true,
left: '5%',
bottom: '0%',
itemWidth: 7,
itemHeight: 7,
itemGap: 15,
textStyle: { color: '#000', fontSize: 12 },
data: [{
name: item.label,
icon: index > 2 ? 'path://m0.010277,5.945418l24.979446,0l0,2.109164l-24.979446,0l0,-2.109164z' : 'circle',
itemStyle: { shadowColor: color[index], shadowBlur: index === 0 ? 10 : 0 },
}],
},
xAxis: {
boundaryGap: true,
data: xAxisData,
axisLine: { lineStyle: { color: '#305cd0' } },
axisTick: { show: true, alignWithLabel: true },
axisLabel: { color: '#474747' },
},
yAxis: [{
name: 'SD',
type: 'value',
inverse: false,
splitLine: { show: true, lineStyle: { type: 'dashed' } },
axisLabel: { formatter: '{value}', color: '#474747', fontSize: 14 },
axisTick: { show: false },
}],
series: seriesData,
};
instance.setOption(option);
window.addEventListener('resize', () => instance.resize());
});
}
// 单图模式
else {
// 清空原有单图表实例
if (myEcharts.value) {
const chartInstance = echarts.getInstanceByDom(myEcharts.value);
if (chartInstance) chartInstance.dispose();
}
const instance = echarts.init(myEcharts.value);
const seriesData = [];
const legendData = [];
// 遍历过滤后的水平数据
filteredChartData.value.forEach((item, index) => {
seriesData.push({
name: item.label,
type: 'line',
symbol: 'circle',
showAllSymbol: true,
symbolSize: 7,
smooth: true, // 折线图圆滑
lineStyle: { color: color[index], width: 1.5 }, // 线条保持原有颜色
itemStyle: {
// 按数据点数值动态设置转折点颜色
color: (params) => getSymbolColor(params.value, color[index]),
shadowColor: color[index],
shadowBlur: index === 0 ? 10 : 0,
},
data: item.arr,
});
legendData.push({
name: item.label,
icon: index > 2 ? 'path://m0.010277,5.945418l24.979446,0l0,2.109164l-24.979446,0l0,-2.109164z' : 'circle',
itemStyle: { shadowColor: color[index], shadowBlur: index === 0 ? 10 : 0 },
});
});
const option = {
backgroundColor: 'transparent',
tooltip: { show: true },
grid: { top: '10%', left: '2%', right: '10%', bottom: '10%', containLabel: true },
legend: {
show: true,
left: '5%',
bottom: '0%',
itemWidth: 7,
itemHeight: 7,
itemGap: 15,
textStyle: { color: '#000', fontSize: 12 },
data: legendData,
},
xAxis: {
boundaryGap: true,
data: xAxisData,
axisLine: { lineStyle: { color: '#305cd0' } },
axisTick: { show: true, alignWithLabel: true },
axisLabel: { color: '#474747' },
},
yAxis: [{
name: 'SD',
type: 'value',
inverse: false,
splitLine: { show: true, lineStyle: { type: 'dashed' } },
// axisLine: { show: true, lineStyle: { color: '#305cd0' } },
axisLabel: { formatter: '{value}', color: '#474747', fontSize: 14 },
axisTick: { show: false },
}],
series: seriesData,
};
instance.setOption(option);
window.addEventListener('resize', () => instance.resize());
}
};
//水平复选框变化时重新渲染图表
const handleLevelChange = () => {
getEcharts();
};
getBatchList();
const getDataValue = (value) => {
console.log('value==>', value);
yqmc.value = value.yqmc;
};
const instrOptions = ref([]);
onMounted(async () => {
const today = dayjs();
const startOfWeek = today.subtract(6, 'day');
queryParams.st = startOfWeek.format('YYYY-MM-DD');
queryParams.et = today.format('YYYY-MM-DD');
getGroupInstrdList().then((res) => {
if (res.code == 0) {
instrOptions.value = res.data.map((item) => ({
value: item.yq.trim(),
label: item.yqmc,
}));
yqmc.value = instrOptions.value.find(item => item.value === queryParams.yq)?.label;
getEcharts(); // 初始化时渲染图表
}
});
});
</script>
<style scoped lang="scss">
.table-row {
height: calc(100% - 90px);
.table-col {
height: 100%;
}
}
.echarts-box {
height: calc(100% - 50px);
overflow: auto;
}
.el-form-item {
margin-bottom: 5px;
}
.tips {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px;
color: #409eff;
font-weight: 600;
margin-bottom: 10px;
}
.charts-container {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>

View File

@ -67,8 +67,11 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" :size="aotuSize" plain>清单打印</el-button> <el-button type="warning" :size="aotuSize" plain>清单打印</el-button>
</el-col> </el-col>
<!-- <right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery" <el-col :span="4">
:columns="columns"></right-toolbar> --> <el-select v-model="printName" placeholder="打印机设置" @visible-change="visibleChange" @change="changePrinter">
<el-option v-for="item in printerList" :key="item.name" :label="item.name" :value="item.name" />
</el-select>
</el-col>
</el-row> </el-row>
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig" :loading="loading" <CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig" :loading="loading"
@ -174,6 +177,8 @@ import SelectTable from '@/components/SelectTable/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
const router = useRouter() const router = useRouter()
const printStore = usePrintStore(); const printStore = usePrintStore();
const printerList = computed(() => printStore.printerList);
const printName = ref(printStore.defaultPrinter || '');
const compactForm = ref<HTMLElement | null>(null); const compactForm = ref<HTMLElement | null>(null);
const heightForm = ref(90); const heightForm = ref(90);
const dialogVisible = ref(false) const dialogVisible = ref(false)
@ -543,6 +548,18 @@ const cancelHandle = () => {
} }
const visibleChange = (visible: boolean) => {
if (visible) {
if (!printerList.value.length) {
HiprintPrinter.initSocket()
}
}
};
const changePrinter = (value: string) => {
printStore.setDefaultPrinter(value);
};
const hosList = ref([]) const hosList = ref([])
onMounted(async () => { onMounted(async () => {
// 初始化3天区间(今天-前两天) // 初始化3天区间(今天-前两天)
@ -653,6 +670,7 @@ const selectStatusRows = () => {
// 打印pdf // 打印pdf
const printPdf = () => { const printPdf = () => {
if (!printName.value) return ElMessage.warning("未设置打印机,请先设置打印机!");
if (sqhs.value.length <= 0) return ElMessage.warning('请选择数据!') if (sqhs.value.length <= 0) return ElMessage.warning('请选择数据!')
ElMessageBox.confirm(`确定打印选中的数据吗?`, "提示", { ElMessageBox.confirm(`确定打印选中的数据吗?`, "提示", {
confirmButtonText: "确认", confirmButtonText: "确认",
@ -669,7 +687,7 @@ const printPdf = () => {
if (res.code == 0) { if (res.code == 0) {
done(); done();
getList() getList()
HiprintPrinter.silentPrint(res.data); HiprintPrinter.silentPrint(res.data, printName.value);
} }
}) })
} else { } else {