2026-03-02 18:01:54 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @file index.vue 科内开单
|
|
|
|
|
|
* @author: w
|
2026-05-13 17:50:49 +08:00
|
|
|
|
* @since: 2026-03-09
|
2026-03-02 18:01:54 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
|
<el-row :gutter="10" class="row-box">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-col :span="16" style="height: 100%;">
|
2026-03-02 18:01:54 +08:00
|
|
|
|
<div class="box-shadow">
|
2026-03-05 18:03:05 +08:00
|
|
|
|
<el-row class="top-box">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-col :span="8" class="col_box">
|
|
|
|
|
|
<el-button type="success" :size="aotuSize" :loading="saveLoading" @click="saveHandle">保存</el-button>
|
|
|
|
|
|
<el-button type="primary" :size="aotuSize" @click="addHandle">新增</el-button>
|
|
|
|
|
|
<el-button type="warning" :size="aotuSize" :loading="printLoading" @click="printHandle">打印</el-button>
|
|
|
|
|
|
<el-button type="danger" :size="aotuSize" @click="deleteHandle">作废</el-button>
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</el-col>
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-col :span="16">
|
2026-03-05 18:03:05 +08:00
|
|
|
|
<div class="tips">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
项目检索: <el-input v-model="searchKey" style="width: 150px;" @clear="filterDictData"
|
2026-03-05 18:03:05 +08:00
|
|
|
|
@input="filterDictData" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<el-row :gutter="5" style="height: 100%;">
|
|
|
|
|
|
<el-col :span="7">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<Info v-model:labPat="labpat" :visbileMask="visbileMask" @getBrdh="getBrdhHandle" />
|
2026-03-05 18:03:05 +08:00
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="17" style="height: 100%;">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-tabs v-model="activeName" type="card" class="tabs_box" @tab-click="handleTabClick">
|
|
|
|
|
|
<el-tab-pane v-for="item in xmdlList" :key="item.dictCode" :label="item.dictName" :name="item.dictCode">
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
<div style="height: calc(60% - 55px)" class="mb5">
|
|
|
|
|
|
<CustomVxeTable :table-data="filterData" :columns="xmColumns" @cell-dblclick="dbRowclick"
|
|
|
|
|
|
:cell-style="xmcellStyle" keyField='applyid'
|
|
|
|
|
|
:scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" class="mytable-style">
|
|
|
|
|
|
<template #itemclass="{ row }">
|
|
|
|
|
|
{{itemclassList.find((item) => item.itemclassCode == row.itemclass)?.itemclassName}}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</CustomVxeTable>
|
2026-03-05 18:03:05 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div style="height: calc(40% - 30px)">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<CustomVxeTable :table-data="sqxmCzList" :columns="sqXmColumns" keyField='uid' :cell-style="cellStyle"
|
|
|
|
|
|
:config="tableConfig" :scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" ref="xmRef"
|
|
|
|
|
|
@selection-change="selectionChange" class="mytable-style">
|
|
|
|
|
|
<template #newitemclass="{ row }">
|
|
|
|
|
|
{{itemclassList.find((item) => item.itemclassCode == row.newitemclass)?.itemclassName}}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #itemclass_yblx="{ row }">
|
|
|
|
|
|
<SelectTable v-model:data="row.itemclass_yblx" :tableData="dictData.BT" placeholder="请选择"
|
|
|
|
|
|
:clearable="false" v-if="row.status == 10 || !row.status" value="label"
|
|
|
|
|
|
@getDataValue="(data) => { getyblxData(data, row) }" class="full-width-input" />
|
|
|
|
|
|
<span v-else style="padding-left: 5px;"> {{ formatDict(row.itemclass_yblx, 'BT') }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #status="{ row }">
|
|
|
|
|
|
<span v-if="row.rowSeq == 1"> {{ formatDict(row.status, 'ZT') }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #action="{ row }">
|
|
|
|
|
|
<el-button type="danger" :text='true' v-if="row.status == 10 || !row.status" size="small"
|
|
|
|
|
|
@click="delItem(row)">删除</el-button>
|
2026-03-05 18:03:05 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</CustomVxeTable>
|
|
|
|
|
|
</div>
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<div class="yblxtext">共有{{ barcodes }}个样本,合计金额:<span>{{ totalAmount }}</span></div>
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-col :span="8" style="height: 100%;">
|
2026-03-02 18:01:54 +08:00
|
|
|
|
<div class="box-shadow">
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-form :model="searchForm" inline>
|
|
|
|
|
|
<el-form-item label="" prop="brxm">
|
|
|
|
|
|
<el-input v-model="searchForm.brxm" clearable style="width: 8rem;" @change="handleQuery" :size="aotuSize"
|
|
|
|
|
|
placeholder="请输入姓名" />
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</el-form-item>
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<el-form-item label="" prop="status">
|
|
|
|
|
|
<div class="radio_box">
|
|
|
|
|
|
<el-radio-group v-model="searchForm.status" @change="handleQuery" :size="aotuSize">
|
|
|
|
|
|
<el-radio :value="''">所有</el-radio>
|
|
|
|
|
|
<el-radio :value="item.value" v-for="item in dictData.SQDS">{{ item.label }}</el-radio>
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
</div>
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</el-form-item>
|
2026-05-13 17:50:49 +08:00
|
|
|
|
|
|
|
|
|
|
<el-form-item label="" prop="begdate">
|
|
|
|
|
|
<div class="radio_box">
|
|
|
|
|
|
<el-radio-group v-model="searchForm.days" @change="changeDays" :size="aotuSize">
|
|
|
|
|
|
<el-radio :value="3">近3天</el-radio>
|
|
|
|
|
|
<el-radio :value="7">近7天</el-radio>
|
|
|
|
|
|
<el-radio :value="0">此日
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<Right />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</el-radio>
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
<el-date-picker v-model="today" type="date" value-format="YYYY-MM-DD" style="width: 9rem;"
|
|
|
|
|
|
:size="aotuSize" @change="dateHandle" />
|
|
|
|
|
|
<el-button type="primary" icon="RefreshRight" :size="aotuSize" @click="handleQuery">刷新</el-button>
|
|
|
|
|
|
</div>
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<div class="table-box">
|
|
|
|
|
|
<CustomVxeTable :table-data="labPatList" :loading="loading" :columns="tableColumns"
|
2026-05-13 17:50:49 +08:00
|
|
|
|
@cell-click="handleRowClick" size="small" keyField='applyid' :cell-style="sqdcellStyle"
|
|
|
|
|
|
:scrollYConfig="{ enabled: true, rSize: 30, height: '100%', }" class="mytable-style" ref="tableRef">
|
|
|
|
|
|
<template #status="{ row }">
|
|
|
|
|
|
{{ formatDict(row.status, 'SQDS') }}
|
2026-03-11 15:27:15 +08:00
|
|
|
|
</template>
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</CustomVxeTable>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
<script setup name="Issueorder">
|
2026-03-02 18:01:54 +08:00
|
|
|
|
import Info from './components/info.vue'
|
|
|
|
|
|
import { getDictData, formatDict, dictData } from '@/hooks'
|
2026-05-13 17:50:49 +08:00
|
|
|
|
import { saveReqInfo, queryPatInfo } from '@/api/liswork/order'
|
2026-03-02 18:01:54 +08:00
|
|
|
|
import dayjs from 'dayjs'
|
2026-05-13 17:50:49 +08:00
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
2026-03-05 18:03:05 +08:00
|
|
|
|
import { getFirstLetter } from '@/utils/pinyin';
|
2026-05-13 17:50:49 +08:00
|
|
|
|
import { listregdict } from "@/api/regional/dict/regdict";
|
|
|
|
|
|
import { checkPrintService } from '@/utils/printHelper.ts';
|
|
|
|
|
|
import { listitemclass } from "@/api/regional/dict/itemclass";
|
|
|
|
|
|
import { classCom } from '@/utils/classCom';
|
|
|
|
|
|
import { useGroupTableData } from '@/utils/groupHelper'
|
2026-03-11 15:27:15 +08:00
|
|
|
|
const { proxy } = getCurrentInstance();
|
2026-05-13 17:50:49 +08:00
|
|
|
|
// import { initWebSocket, sendWebSocketMessage, closeWebSocket, getWebSocketState } from '@/utils/webSocket';
|
|
|
|
|
|
const aotuSize = classCom.useAutoSize();
|
|
|
|
|
|
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const labpat = ref({
|
2026-05-13 17:50:49 +08:00
|
|
|
|
ageUnit: '1'
|
2026-03-05 18:03:05 +08:00
|
|
|
|
})
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const saveLoading = ref(false)
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const searchKey = ref('')
|
2026-03-02 18:01:54 +08:00
|
|
|
|
const loading = ref(false)
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const tableRef = useTemplateRef('tableRef')
|
2026-03-02 18:01:54 +08:00
|
|
|
|
const labPatList = ref([])
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const xmdlList = ref([])
|
|
|
|
|
|
const activeName = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
const today = ref(dayjs().format('YYYY-MM-DD'))
|
|
|
|
|
|
|
|
|
|
|
|
// 记录初始数据(用于对比是否有变化)
|
|
|
|
|
|
const initialLabPat = ref({})
|
|
|
|
|
|
const initialSqxmCzList = ref([])
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const searchForm = ref({
|
|
|
|
|
|
brxm: '',
|
|
|
|
|
|
status: '',
|
|
|
|
|
|
days: 3,
|
|
|
|
|
|
begdate: '',
|
|
|
|
|
|
enddate: '',
|
|
|
|
|
|
})
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-03-02 18:01:54 +08:00
|
|
|
|
const tableColumns = ref([
|
2026-05-13 17:50:49 +08:00
|
|
|
|
{ field: 'inputDate', title: '登记时间', width: 80, align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'rowSeq', title: 'No', width: 40, align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'patName', title: '姓名', width: 80, align: 'center', resizable: true, },
|
|
|
|
|
|
{ field: 'status', title: '状态', width: 80, align: 'center', slotName: 'status', resizable: true, },
|
|
|
|
|
|
{ field: 'userName', title: '登记人', width: 80, align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'reqDetailName', title: '项目', width: 200, align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'patId', title: '病人代号', align: 'center', width: 80, resizable: true },
|
2026-03-02 18:01:54 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const sqdcellStyle = ({ row, column }) => {
|
|
|
|
|
|
if (column.title == "状态") {
|
|
|
|
|
|
return {
|
|
|
|
|
|
backgroundColor: getColor(row.status),
|
|
|
|
|
|
// color: ,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-02 18:01:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const getColor = (type) => {
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case '10':
|
|
|
|
|
|
return '';
|
|
|
|
|
|
case '20':
|
|
|
|
|
|
return ' rgba(238, 8, 8, 0.25)';
|
|
|
|
|
|
case '30':
|
|
|
|
|
|
return 'rgba(0, 128, 0, 0.25)';
|
|
|
|
|
|
case '40':
|
|
|
|
|
|
return 'rgba(255, 165, 0, 0.25)';
|
|
|
|
|
|
case '50':
|
|
|
|
|
|
return 'rgba(255, 165, 0, 0.25)';
|
|
|
|
|
|
default:
|
|
|
|
|
|
return 'rgba(40, 189, 187, 0.25)';
|
|
|
|
|
|
}
|
2026-03-02 18:01:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
|
|
|
|
|
|
const deepClone = (data) => {
|
|
|
|
|
|
return JSON.parse(JSON.stringify(data))
|
2026-03-02 18:01:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
// 更新初始数据(在新增/加载数据时调用)
|
|
|
|
|
|
const updateInitialData = () => {
|
|
|
|
|
|
initialLabPat.value = deepClone(labpat.value)
|
|
|
|
|
|
initialSqxmCzList.value = deepClone(sqxmCzList.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断数据是否有变化
|
|
|
|
|
|
const isDataChanged = () => {
|
|
|
|
|
|
const labPatChanged = JSON.stringify(labpat.value) !== JSON.stringify(initialLabPat.value)
|
|
|
|
|
|
const sqxmChanged = JSON.stringify(sqxmCzList.value) !== JSON.stringify(initialSqxmCzList.value)
|
|
|
|
|
|
return labPatChanged || sqxmChanged
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const rowInfo = ref({})
|
|
|
|
|
|
|
|
|
|
|
|
const visbileMask = ref(false)
|
|
|
|
|
|
const handleRowClick = ({ row }) => {
|
|
|
|
|
|
rowInfo.value = row
|
|
|
|
|
|
if (!row?.applyid) return
|
|
|
|
|
|
// queryReqDetail({ applyId: row.applyid }).then(res => {
|
|
|
|
|
|
// labpat.value = res.data.regApply
|
|
|
|
|
|
// visbileMask.value = res.data.regApplyDetailList.some(item => { return item.status > 10 })
|
|
|
|
|
|
// sqXmList.value = res.data.regApplyDetailList.map(item => ({
|
|
|
|
|
|
// itemcode: item.orderItemCode,
|
|
|
|
|
|
// itemname: item.orderItemName,
|
|
|
|
|
|
// itemclass_yblx: item.sampleTypeName,
|
|
|
|
|
|
// amount: item.amount,
|
|
|
|
|
|
// applyid: item.applyid,
|
|
|
|
|
|
// uid: item.uid,
|
|
|
|
|
|
// classid: item.classid,
|
|
|
|
|
|
// barcode: item.barcode,
|
|
|
|
|
|
// itemclass: item.itemclass,
|
|
|
|
|
|
// itemclass_tips: item.itemclass_tips,
|
|
|
|
|
|
// status: item.status,
|
|
|
|
|
|
// }))
|
|
|
|
|
|
// // 异步初始化数据
|
|
|
|
|
|
// nextTick(() => {
|
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
|
// updateInitialData()
|
|
|
|
|
|
// }, 10);
|
|
|
|
|
|
// })
|
|
|
|
|
|
// })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const changeDays = (val) => {
|
|
|
|
|
|
if (val == 3) {
|
|
|
|
|
|
searchForm.value.begdate = dayjs().subtract(3, 'day').format('YYYY-MM-DD') + ' 00:00:00'
|
|
|
|
|
|
searchForm.value.enddate = dayjs().format('YYYY-MM-DD') + ' 23:59:59'
|
|
|
|
|
|
} else if (val == 7) {
|
|
|
|
|
|
searchForm.value.begdate = dayjs().subtract(7, 'day').format('YYYY-MM-DD') + ' 00:00:00'
|
|
|
|
|
|
searchForm.value.enddate = dayjs().format('YYYY-MM-DD') + ' 23:59:59'
|
|
|
|
|
|
} else {
|
|
|
|
|
|
searchForm.value.begdate = dayjs(today.value).format('YYYY-MM-DD') + ' 00:00:00'
|
|
|
|
|
|
searchForm.value.enddate = dayjs(searchForm.value.begdate).format('YYYY-MM-DD') + ' 23:59:59'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
handleQuery()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const dateHandle = (val) => {
|
|
|
|
|
|
searchForm.value.begdate = dayjs(val).format('YYYY-MM-DD') + ' 00:00:00'
|
|
|
|
|
|
searchForm.value.enddate = dayjs(searchForm.value.begdate).format('YYYY-MM-DD') + ' 23:59:59'
|
|
|
|
|
|
handleQuery()
|
|
|
|
|
|
}
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const handleQuery = () => {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
rowInfo.value = {}
|
|
|
|
|
|
// queryRegRequestInfo(searchForm.value).then(res => {
|
|
|
|
|
|
// // 数据分组
|
|
|
|
|
|
// const { tableData } = useGroupTableData(
|
|
|
|
|
|
// res.data,
|
|
|
|
|
|
// 'orderDate', // 分组字段
|
|
|
|
|
|
// ['inputDate',], // 仅第一条显示的字段
|
|
|
|
|
|
// 'orderDate', // 排序字段
|
|
|
|
|
|
// ['orderDate'],//原数据赋值对应字段
|
|
|
|
|
|
// )
|
|
|
|
|
|
|
|
|
|
|
|
// labPatList.value = tableData.value
|
|
|
|
|
|
// const item = res.data.find(item => item.patId == labpat.value.patId)
|
|
|
|
|
|
|
|
|
|
|
|
// nextTick(() => {
|
|
|
|
|
|
// tableRef.value.setCurrentRow(item)
|
|
|
|
|
|
// handleRowClick({ row: item })
|
|
|
|
|
|
// })
|
|
|
|
|
|
// })
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
2026-05-13 17:50:49 +08:00
|
|
|
|
// 获取病人代号查询信息
|
|
|
|
|
|
const getBrdhHandle = (val) => {
|
|
|
|
|
|
queryPatInfo({ patId: val }).then(res => {
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
|
labpat.value = res.data
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
//保存
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const saveHandle = () => {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
if (!labpat.value.patId) return ElMessage.warning('病人代号不能为空!')
|
|
|
|
|
|
if (!labpat.value.patName) return ElMessage.warning('病人姓名不能为空!')
|
|
|
|
|
|
if (!labpat.value.patSex) return ElMessage.warning('病人性别不能为空!')
|
|
|
|
|
|
if (!labpat.value.patAge || !labpat.value.ageUnit) return ElMessage.warning('病人年龄不能为空!')
|
2026-03-05 18:03:05 +08:00
|
|
|
|
if (!sqXmList.value.length) return ElMessage.warning('请选择项目!')
|
2026-05-13 17:50:49 +08:00
|
|
|
|
labpat.value.status = labpat.value.status ? labpat.value.status : ''
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const data = {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
regApply: labpat.value,
|
|
|
|
|
|
regApplyDetailList: sqxmCzList.value.map(item => ({
|
|
|
|
|
|
orderItemCode: item.itemcode,
|
|
|
|
|
|
orderItemName: item.itemname,
|
|
|
|
|
|
sampleTypeCode: item.itemclass_yblx,
|
|
|
|
|
|
sampleTypeName: item.itemclass_yblx,
|
|
|
|
|
|
amount: item.amount,
|
|
|
|
|
|
applyid: labpat.value.applyid,
|
|
|
|
|
|
uid: item.uid
|
|
|
|
|
|
}))
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
2026-05-13 17:50:49 +08:00
|
|
|
|
saveLoading.value = true
|
2026-03-05 18:03:05 +08:00
|
|
|
|
saveReqInfo(data).then(res => {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
if (res.code == 0) {
|
|
|
|
|
|
ElMessage.success('保存成功!')
|
|
|
|
|
|
handleQuery()
|
|
|
|
|
|
updateInitialData()
|
|
|
|
|
|
}
|
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
saveLoading.value = false
|
2026-03-05 18:03:05 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const addHandle = () => {
|
|
|
|
|
|
labpat.value = {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
ageUnit: '1'
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
2026-05-13 17:50:49 +08:00
|
|
|
|
sqXmList.value = []
|
|
|
|
|
|
visbileMask.value = false
|
|
|
|
|
|
updateInitialData()
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
const deleteHandle = () => {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
// if (!rowInfo.value.applyid) return ElMessage.warning('请选择要作废的申请单!')
|
|
|
|
|
|
// ElMessageBox.confirm('确定要作废此申请单吗?', '提示', {
|
|
|
|
|
|
// confirmButtonText: '确定',
|
|
|
|
|
|
// cancelButtonText: '取消',
|
|
|
|
|
|
// type: 'warning',
|
|
|
|
|
|
// }).then(() => {
|
|
|
|
|
|
// cancelReq({ applyId: rowInfo.value.applyid }).then(res => {
|
|
|
|
|
|
// if (res.code == 0) {
|
|
|
|
|
|
// ElMessage.success(res.msg)
|
|
|
|
|
|
// handleQuery()
|
|
|
|
|
|
// }
|
|
|
|
|
|
// })
|
|
|
|
|
|
// }).catch(() => { })
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
}
|
2026-03-02 18:01:54 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const printLoading = ref(false)
|
|
|
|
|
|
const printHandle = () => {
|
|
|
|
|
|
if (isDataChanged()) {
|
|
|
|
|
|
ElMessageBox.confirm('数据已修改,请先保存后再打印!', '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning',
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
saveHandle()
|
|
|
|
|
|
}).catch(() => { })
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const selectList = xmRef.value.allSelection()
|
2026-03-02 18:01:54 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
let arr = selectList.map(item => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
applyId: item.applyid,
|
|
|
|
|
|
barcode: item.barcode,
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
const uniqueData = [];
|
|
|
|
|
|
const tempObj = {}; // 记录已出现的 barcode
|
|
|
|
|
|
|
|
|
|
|
|
arr.forEach(item => {
|
|
|
|
|
|
// 若 tempObj 中无该 barcode,则保留并记录
|
|
|
|
|
|
if (!tempObj[item.barcode]) {
|
|
|
|
|
|
tempObj[item.barcode] = true;
|
|
|
|
|
|
uniqueData.push(item);
|
|
|
|
|
|
}
|
2026-03-02 18:01:54 +08:00
|
|
|
|
});
|
2026-05-13 17:50:49 +08:00
|
|
|
|
if (!uniqueData.length) return ElMessage.warning('请选择要打印的条码!')
|
|
|
|
|
|
checkPrintService().then((isConnected) => {
|
|
|
|
|
|
if (!isConnected) return
|
|
|
|
|
|
printLoading.value = true
|
|
|
|
|
|
// reqPrint(uniqueData).then(res => {
|
|
|
|
|
|
// if (res.code == 0) {
|
|
|
|
|
|
// sendWebSocketMessage(res.data)
|
|
|
|
|
|
// handleRowClick({ row: rowInfo.value })
|
|
|
|
|
|
// ElMessage.success('正在打印中...')
|
|
|
|
|
|
// handleQuery()
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }).finally(() => {
|
|
|
|
|
|
// printLoading.value = false
|
|
|
|
|
|
// })
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-03-02 18:01:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const filterData = ref([])
|
|
|
|
|
|
const xmColumns = ref([
|
2026-05-13 17:50:49 +08:00
|
|
|
|
{ field: 'itemcode', title: '代号', align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'itemname', title: '项目名称', align: 'center', width: 280, resizable: true },
|
|
|
|
|
|
{ field: 'price', title: '单价', align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'itemclass', title: '类别', align: 'center', slotName: 'itemclass', resizable: true },
|
|
|
|
|
|
{ field: 'itemclass_tips', title: '采样提示', align: 'center', resizable: true },
|
|
|
|
|
|
{ field: 'itemclass_yblx', title: '样本', align: 'center', resizable: true },
|
2026-03-05 18:03:05 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const xmcellStyle = ({ row, column }) => {
|
|
|
|
|
|
if (column.title == "类别") {
|
|
|
|
|
|
const bkcolor = itemclassList.value.find((item) => item.itemclassCode == row.itemclass)?.itemclassBkcolor;
|
|
|
|
|
|
const bgColor = classCom.decimalToHexColor(bkcolor);
|
|
|
|
|
|
const textColor = classCom.getContrastTextColor(bgColor);
|
|
|
|
|
|
return {
|
|
|
|
|
|
backgroundColor: `${bgColor} !important`,
|
|
|
|
|
|
color: textColor,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const searchList = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
const handleTabClick = (tab) => {
|
|
|
|
|
|
if (tab.paneName) {
|
|
|
|
|
|
const currentTab = xmdlList.value.find(item => item.dictCode === tab.paneName)
|
|
|
|
|
|
if (currentTab) {
|
|
|
|
|
|
searchList.value = processedTableData.value.filter(item => item.classid === currentTab.dictCode)
|
|
|
|
|
|
filterData.value = [...searchList.value]
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
searchList.value = [...processedTableData.value]
|
|
|
|
|
|
filterData.value = [...searchList.value]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const processedTableData = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
const filterDictData = () => {
|
|
|
|
|
|
const key = searchKey.value.trim().toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
if (!key) {
|
|
|
|
|
|
// 空搜索时显示全部预处理数据
|
2026-05-13 17:50:49 +08:00
|
|
|
|
filterData.value = [...searchList.value]
|
2026-03-05 18:03:05 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
filterData.value = searchList.value.filter((item) => {
|
|
|
|
|
|
const matchName = item.itemname.toString().toLowerCase().includes(key);
|
2026-03-05 18:03:05 +08:00
|
|
|
|
const matchLabel = item.pinyin.toString().toLowerCase().includes(key);
|
2026-05-13 17:50:49 +08:00
|
|
|
|
return matchLabel || matchName;
|
2026-03-05 18:03:05 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const processTableData = (data) => {
|
|
|
|
|
|
return data.map((item) => {
|
|
|
|
|
|
const pinyinMap = {};
|
2026-05-13 17:50:49 +08:00
|
|
|
|
pinyinMap['pinyin'] = getFirstLetter(item.itemname).toLowerCase();
|
2026-03-05 18:03:05 +08:00
|
|
|
|
return { ...item, ...pinyinMap };
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const sqXmList = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
const sqXmColumns = ref([
|
2026-05-13 17:50:49 +08:00
|
|
|
|
{ type: 'selection', field: '', title: '', resizable: true, align: 'center', width: 40 },
|
|
|
|
|
|
{ field: 'newitemclass', title: '类别', align: 'center', slotName: 'newitemclass', resizable: true },
|
|
|
|
|
|
{ field: 'newitemclass_tips', title: '采样提示', align: 'center', width: 60, resizable: true },
|
|
|
|
|
|
{ field: 'itemname', title: '项目名称', align: 'center', resizable: true, width: 220, },
|
|
|
|
|
|
{ field: 'newbarcode', title: '条码号', align: 'center', width: 120, resizable: true, },
|
|
|
|
|
|
{ field: 'amount', title: '单价', align: 'center', width: 60, resizable: true },
|
|
|
|
|
|
{ field: 'itemclass_yblx', title: '样本', width: 80, slotName: 'itemclass_yblx', resizable: true },
|
|
|
|
|
|
{ field: 'status', title: '状态', align: 'center', resizable: true, width: 80, slotName: 'status' },
|
2026-03-05 18:03:05 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const cellStyle = ({ row, column }) => {
|
|
|
|
|
|
if (column.title == "类别" && row.newitemclass) {
|
|
|
|
|
|
const bkcolor = itemclassList.value.find((item) => item.itemclassCode == row.itemclass)?.itemclassBkcolor;
|
|
|
|
|
|
const bgColor = classCom.decimalToHexColor(bkcolor);
|
|
|
|
|
|
const textColor = classCom.getContrastTextColor(bgColor);
|
|
|
|
|
|
return {
|
|
|
|
|
|
backgroundColor: `${bgColor} !important`,
|
|
|
|
|
|
color: textColor,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getyblxData = (data, row) => {
|
|
|
|
|
|
sqXmList.value.forEach(item => {
|
|
|
|
|
|
if (row.uid == item.uid) {
|
|
|
|
|
|
item.itemclass_yblx = data.label
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-05-13 17:50:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 已申请的项目
|
|
|
|
|
|
const sqxmCzList = computed(() => {
|
|
|
|
|
|
// 数据分组
|
|
|
|
|
|
const { tableData } = useGroupTableData(
|
|
|
|
|
|
sqXmList.value,
|
|
|
|
|
|
['itemclass', 'barcode'],
|
|
|
|
|
|
['newitemclass', 'newbarcode', 'newitemclass_tips'],
|
|
|
|
|
|
'orderDate',
|
|
|
|
|
|
['itemclass', 'barcode', 'itemclass_tips'],
|
|
|
|
|
|
)
|
|
|
|
|
|
return tableData.value
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
sqxmCzList,
|
|
|
|
|
|
() => {
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
// 等待 DOM 渲染完成后执行勾选
|
|
|
|
|
|
sqxmCzList.value.forEach(item => {
|
|
|
|
|
|
if (item.status < 30) {
|
|
|
|
|
|
xmRef.value.toggleRowSelection(item, true)
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
{ deep: true, immediate: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const barcodes = computed(() => {
|
|
|
|
|
|
return Array.from(new Set(sqxmCzList.value.map(item => item.barcode))).length
|
|
|
|
|
|
})
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const totalAmount = computed(() => {
|
|
|
|
|
|
return sqxmCzList.value.reduce((total, item) => {
|
|
|
|
|
|
return total + (item.amount || 0);
|
|
|
|
|
|
}, 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tableConfig = ref({
|
|
|
|
|
|
actionWidth: 60,
|
|
|
|
|
|
// 复选框配置
|
|
|
|
|
|
checkboxConfig: {
|
|
|
|
|
|
// 可选配置项
|
|
|
|
|
|
checkField: 'checked', // 绑定数据中的字段(默认:checked)
|
|
|
|
|
|
range: false, // 是否支持范围选择(按住 Shift 连续选择)
|
|
|
|
|
|
disabled: false, // 是否禁用全部复选框
|
|
|
|
|
|
reserve: false, // 是否保留勾选状态(分页/筛选时)
|
|
|
|
|
|
highlight: false, // 是否高亮选中行
|
|
|
|
|
|
checkMethod: ({ row }) => {
|
|
|
|
|
|
//标本送检之前状态才能勾选
|
|
|
|
|
|
return row.status < 30;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const xmRef = useTemplateRef('xmRef')
|
|
|
|
|
|
|
|
|
|
|
|
const selectionChange = (checked, row, selection) => {
|
|
|
|
|
|
// console.log('==>', checked, row, selection);
|
|
|
|
|
|
sqxmCzList.value.forEach(item => {
|
|
|
|
|
|
if (row.barcode === item.barcode && row.status < 30) {
|
|
|
|
|
|
xmRef.value.toggleRowSelection(item, checked)
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
2026-05-13 17:50:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const delItem = (row) => {
|
|
|
|
|
|
const index = sqXmList.value.findIndex(item => item.itemcode === row.itemcode)
|
|
|
|
|
|
if (index !== -1) {
|
2026-03-05 18:03:05 +08:00
|
|
|
|
sqXmList.value.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const dbRowclick = ({ row }) => {
|
|
|
|
|
|
if (visbileMask.value) return ElMessage.warning('申请单已审核,不能添加项目!')
|
|
|
|
|
|
const index = sqXmList.value.findIndex(item => item.itemcode == row.itemcode)
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
ElMessage.warning('已存在该项目')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
sqXmList.value.push({ ...row, amount: row.price })
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const getXmList = () => {
|
|
|
|
|
|
// getXmItemInfo().then(res => {
|
|
|
|
|
|
// processedTableData.value = processTableData(res.data);
|
|
|
|
|
|
// filterData.value = [...processedTableData.value]
|
|
|
|
|
|
|
|
|
|
|
|
listregdict({ dictType: 'CLASS' }).then(response => {
|
|
|
|
|
|
xmdlList.value = response.rows;
|
|
|
|
|
|
xmdlList.value.unshift({ dictCode: '', dictName: '全部' })
|
|
|
|
|
|
activeName.value = xmdlList.value.length > 0 ? xmdlList.value[0].dictCode : '';
|
|
|
|
|
|
handleTabClick({ paneName: activeName.value });
|
|
|
|
|
|
});
|
|
|
|
|
|
// })
|
2026-03-05 18:03:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
const itemclassList = ref([]) // 容器类别
|
|
|
|
|
|
const initSocket = () => {
|
|
|
|
|
|
initWebSocket({
|
|
|
|
|
|
fullUrl: `ws://localhost:9801`,
|
|
|
|
|
|
onOpen: () => {
|
|
|
|
|
|
console.log('WebSocket连接成功');
|
|
|
|
|
|
},
|
|
|
|
|
|
onMessage: (data) => {
|
|
|
|
|
|
console.log(`收到消息: `, JSON.parse(data));
|
|
|
|
|
|
},
|
|
|
|
|
|
onError: (error) => {
|
|
|
|
|
|
console.log(`连接错误: ${error.type}`);
|
|
|
|
|
|
},
|
|
|
|
|
|
onClose: () => {
|
|
|
|
|
|
console.log('WebSocket连接已关闭');
|
|
|
|
|
|
},
|
|
|
|
|
|
reconnectInterval: 3000 // 重连间隔(毫秒)
|
|
|
|
|
|
});
|
2026-03-11 15:27:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 18:01:54 +08:00
|
|
|
|
onMounted(() => {
|
2026-03-05 18:03:05 +08:00
|
|
|
|
getXmList()
|
2026-05-13 17:50:49 +08:00
|
|
|
|
changeDays(3)
|
|
|
|
|
|
getDictData('PT', 'SX', 'AU', 'ZT', 'ZJLX', 'DEPT', 'DOCTOR', 'SLZQ', 'BT', 'SQDS')
|
|
|
|
|
|
|
|
|
|
|
|
listitemclass({ pageSize: 9999, pageNum: 1 }).then(response => {
|
|
|
|
|
|
itemclassList.value = response.rows;
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化初始值
|
|
|
|
|
|
updateInitialData()
|
|
|
|
|
|
// initSocket()
|
2026-03-02 18:01:54 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
// 页面卸载时清理
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
// closeWebSocket();
|
|
|
|
|
|
});
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.app-container {
|
|
|
|
|
|
background: #F0F3F8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.box-shadow {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
box-shadow: 2px 2px 4px 0px rgba(206, 217, 234, 0.7);
|
|
|
|
|
|
border-radius: .75rem;
|
|
|
|
|
|
padding: .5rem;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
.flex-between {
|
|
|
|
|
|
margin-bottom: .5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.row-box {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-box {
|
2026-05-13 17:50:49 +08:00
|
|
|
|
margin-top: 2px;
|
|
|
|
|
|
height: calc(100% - 85px);
|
2026-03-02 18:01:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-form-item {
|
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
|
}
|
2026-03-05 18:03:05 +08:00
|
|
|
|
|
|
|
|
|
|
.top-box {
|
|
|
|
|
|
border-bottom: 1px solid #E6E9ED;
|
|
|
|
|
|
padding-bottom: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.xmBoxs {
|
|
|
|
|
|
|
|
|
|
|
|
height: 60%;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
|
width: 8px;
|
|
|
|
|
|
height: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: #817e7e;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tips {
|
|
|
|
|
|
font-style: 15px;
|
|
|
|
|
|
color: #123d64;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:50:49 +08:00
|
|
|
|
.yblxtext {
|
|
|
|
|
|
font-style: 15px;
|
|
|
|
|
|
color: #123d64;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 18:03:05 +08:00
|
|
|
|
.title {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #000;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
2026-05-13 17:50:49 +08:00
|
|
|
|
|
|
|
|
|
|
.radio_box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin: .125rem 0;
|
|
|
|
|
|
|
|
|
|
|
|
.el-radio {
|
|
|
|
|
|
margin-right: .3125rem;
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-radio__label) {
|
|
|
|
|
|
padding-left: .3125rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.col_box {
|
|
|
|
|
|
.el-button+.el-button {
|
|
|
|
|
|
margin-left: .5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-02 18:01:54 +08:00
|
|
|
|
</style>
|