检验录入

This commit is contained in:
wyuu 2025-09-26 17:26:06 +08:00
parent 7e876ce499
commit dafc6ea37a
37 changed files with 4857 additions and 3223 deletions

View File

@ -13,6 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.3.1", "@element-plus/icons-vue": "2.3.1",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1", "@vueuse/core": "10.6.1",
"axios": "0.27.2", "axios": "0.27.2",
@ -37,7 +38,9 @@
"vue-router": "4.2.5", "vue-router": "4.2.5",
"vue3-print-nb": "^0.1.4", "vue3-print-nb": "^0.1.4",
"vue3-select2-component": "^0.1.7", "vue3-select2-component": "^0.1.7",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0",
"vxe-pc-ui": "^4.3.2",
"vxe-table": "^4.9.4"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.27.0", "@eslint/js": "^9.27.0",

View File

@ -51,14 +51,22 @@ export function reportList(query?: Object) {
data: query, data: query,
}); });
} }
// 查询报告Pdf // 查询报告Pdf预览
export function reportPrintPdf(query?: Object) { export function reportViewPdf(query?: Object) {
return request({ return request({
url: '/report/pdf', url: '/report/pdf',
method: 'get', method: 'get',
params: query, params: query,
}); });
} }
// 查询报告Pdf打印
export function reportPrintPdf(query?: Object) {
return request({
url: '/report/print',
method: 'post',
data: query,
});
}
// 查询项目报告 // 查询项目报告
export function reportResult(query?: Object) { export function reportResult(query?: Object) {
return request({ return request({

View File

@ -1,7 +1,8 @@
// @ts-ignore
import request from '@/utils/request' import request from '@/utils/request'
// 查询字典数据列表 // 查询字典数据列表
export function queryComDictListService(query) { export function queryComDictListService(query?: Object) {
return request({ return request({
url: '/comdict/query', url: '/comdict/query',
method: 'get', method: 'get',
@ -9,7 +10,7 @@ export function queryComDictListService(query) {
}) })
} }
//增加字典数据 //增加字典数据
export function addComDictService(data) { export function addComDictService(data?: Object) {
return request({ return request({
url: '/comdict/add', url: '/comdict/add',
method: 'post', method: 'post',
@ -18,7 +19,7 @@ export function addComDictService(data) {
} }
//修改字典数据 //修改字典数据
export function updateComDictService(data) { export function updateComDictService(data?: Object) {
return request({ return request({
url: '/comdict/update', url: '/comdict/update',
method: 'post', method: 'post',
@ -27,7 +28,7 @@ export function updateComDictService(data) {
} }
//删除字典数据 //删除字典数据
export function delComDictService(data) { export function delComDictService(data?: Object) {
return request({ return request({
url: '/comdict/del', url: '/comdict/del',
method: 'delete', method: 'delete',
@ -37,7 +38,7 @@ export function delComDictService(data) {
} }
// 根据字典类型查询字典数据信息 // 根据字典类型查询字典数据信息
export function getComDicts(zdlb) { export function getComDicts(zdlb?: Object) {
return request({ return request({
url: '/comdict/getComDicts', url: '/comdict/getComDicts',
method: 'get', method: 'get',

View File

@ -1,18 +0,0 @@
import request from '@/utils/request'
// 查询字典数据列表
export function querylabInstrList(query) {
return request({
url: '/instr/query',
method: 'get',
params: query
})
}
// 查询字典数据列表
export function querylabInstrListByLisgroup(query) {
return request({
url: '/instr/querybylisgroup',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,19 @@
// @ts-ignore
import request from '@/utils/request'
// 查询字典数据列表
export function querylabInstrList(query?: Object) {
return request({
url: '/instr/query',
method: 'get',
params: query
})
}
// 查询字典数据列表
export function querylabInstrListByLisgroup(query?: Object) {
return request({
url: '/instr/querybylisgroup',
method: 'get',
params: query
})
}

View File

@ -1,125 +0,0 @@
import request from '@/utils/request'
// 查询数据列表
export function queryLabPatList(query) {
return request({
url: '/liswork/samplelist',
method: 'get',
params: query
})
}
// 查询数据
export function queryLabPat(query) {
return request({
url: '/liswork/sampleinfo',
method: 'get',
params: query
})
}
// 查询服务器时间
export function getDate() {
return request({
url: '/liswork/getdate',
method: 'get',
})
}
//
export function changeresult(data) {
return request({
url: '/lisworkoper/changeresult',
method: 'post',
data
})
}
//修改病人信息
export function updateLabPat(data) {
return request({
url: '/lisworkoper/changepat',
method: 'post',
data
})
}
export function changepatcolumn(query) {
return request({
url: '/lisworkoper/changepatcolumn',
method: 'get',
params:query
})
}
// 查询结果数据列表
export function queryLabResults(query) {
return request({
url: '/liswork/resultinfo',
method: 'get',
params: query
})
}
// 查询检验项目
export function queryXmInfo(query) {
return request({
url: '/liswork/queryxminfo',
method: 'get',
params: query
})
}
// 查询常用取值
export function queryXmVal(query) {
return request({
url: '/liswork/queryxmval',
method: 'get',
params: query
})
}
// 批量删除结果
export function deleteresult(data) {
return request({
url: '/liswork/deleteresult',
method: 'post',
data: data
})
}
//审核
export function check2(query) {
return request({
url: '/lisworkoper/check2',
method: 'get',
params: query
})
}
//取消审核
export function uncheck2(query) {
return request({
url: '/lisworkoper/uncheck2',
method: 'get',
params: query
})
}
//取消审核
export function unconfirmlog(query) {
return request({
url: '/lisworkoper/unconfirmlog',
method: 'get',
params: query
})
}
export function checkuser(query) {
return request({
url: '/liswork/checkuserid',
method: 'get',
params: query
})
}
export function reglimit(query) {
return request({
url: '/lisworkoper/reglimit',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,153 @@
// @ts-ignore
import request from '@/utils/request'
// 查询数据列表
export function queryLabPatList(query?: Object) {
return request({
url: '/liswork/samplelist',
method: 'get',
params: query
})
}
// 查询数据
export function queryLabPat(query?: Object) {
return request({
url: '/liswork/sampleinfo',
method: 'get',
params: query
})
}
// 查询服务器时间
export function getDate() {
return request({
url: '/liswork/getdate',
method: 'get',
})
}
//新增结果明细
export function newresult(data?: Object) {
return request({
url: '/lisworkoper/newresult',
method: 'post',
data
})
}
//修改结果明细
export function changeresult(data?: Object) {
return request({
url: '/lisworkoper/changeresult',
method: 'post',
data
})
}
//修改病人信息
export function updateLabPat(data?: Object) {
return request({
url: '/lisworkoper/changepat',
method: 'post',
data
})
}
export function changepatcolumn(query?: Object) {
return request({
url: '/lisworkoper/changepatcolumn',
method: 'get',
params: query
})
}
// 查询结果数据列表
export function queryLabResults(query?: Object) {
return request({
url: '/liswork/resultinfo',
method: 'get',
params: query
})
}
// 查询检验项目
export function queryXmInfo(query?: Object) {
return request({
url: '/liswork/queryxminfo',
method: 'get',
params: query
})
}
// 查询常用取值
export function queryXmVal(query?: Object) {
return request({
url: '/liswork/queryxmval',
method: 'get',
params: query
})
}
// 批量删除结果
export function deleteresult(data?: Object) {
return request({
url: '/liswork/deleteresult',
method: 'post',
data: data
})
}
//审核
export function check2(query?: Object) {
return request({
url: '/lisworkoper/check2',
method: 'get',
params: query
})
}
//取消审核
export function uncheck2(query?: Object) {
return request({
url: '/lisworkoper/uncheck2',
method: 'get',
params: query
})
}
//取消审核原因
export function unconfirmlog(query?: Object) {
return request({
url: '/lisworkoper/unconfirmlog',
method: 'get',
params: query
})
}
//取消审核原因
export function check1(query?: Object) {
return request({
url: 'lisworkoper/check1',
method: 'get',
params: query
})
}
// 获取操作userId
export function checkuser(query?: Object) {
return request({
url: '/liswork/checkuserid',
method: 'get',
params: query
})
}
export function reglimit(query?: Object) {
return request({
url: '/lisworkoper/reglimit',
method: 'get',
params: query
})
}
export function printsample(query?: Object) {
return request({
url: '/lisworkoper/printsample',
method: 'get',
params: query
})
}

View File

@ -19,6 +19,15 @@ export function login(username, password, code, uuid) {
}) })
} }
// 单点登录
export function loginAnonymous(data) {
return request({
url: '/loginAnonymous',
method: 'post',
data: data
})
}
// 注册方法 // 注册方法
export function register(data) { export function register(data) {
return request({ return request({

View File

@ -123,7 +123,7 @@ aside {
//main-container全局样式 //main-container全局样式
.app-container { .app-container {
padding: 20px; padding: 10px 10px 0 10px;
} }
.components-container { .components-container {

View File

@ -4,260 +4,404 @@
*/ */
/** 基础通用 **/ /** 基础通用 **/
.pt5 { .pt5 {
padding-top: 5px; padding-top: 5px;
} }
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { .pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.cp {
cursor: pointer;
}
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: inherit; font-family: inherit;
font-weight: 500; font-weight: 500;
line-height: 1.1; line-height: 1.1;
color: inherit; color: inherit;
} }
.el-form .el-form-item__label { .el-form .el-form-item__label {
font-weight: 700; font-weight: 700;
} }
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog.scrollbar .el-dialog__body { .el-form--inline .el-form-item {
margin-right: 15px;
}
.el-form-item__label {
font-weight: 400;
font-size: 14px;
color: #08355E;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto; overflow: auto;
overflow-x: hidden; overflow-x: hidden;
max-height: 70vh; max-height: 70vh;
padding: 10px 20px 0; padding: 10px 20px 0;
} }
.el-table { .el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper { padding: 0 !important;
border-radius: 10px !important;
.el-table__header-wrapper,
.el-table__fixed-header-wrapper {
th { th {
word-break: break-word; word-break: break-word;
background-color: #f8f8f9 !important; background-color: #1F6DD3 !important;
color: #515a6e; color: #FFF;
height: 40px !important; height: 40px !important;
font-size: 13px; font-size: 13px;
} }
} }
.el-table__body-wrapper { .el-table__body-wrapper {
.el-button [class*="el-icon-"] + span { .el-button [class*="el-icon-"]+span {
margin-left: 1px; margin-left: 1px;
} }
} }
}
/** 表单布局 **/
.form-header {
font-size:15px;
color:#6379bb;
border-bottom:1px solid #ddd;
margin:8px 10px 25px 10px;
padding-bottom:5px
}
/** 表格布局 **/ /* 表格最外层边框(可选) */
.pagination-container { .el-table--border {
border: 1px solid #1F6DD3 !important;
}
.el-scrollbar__thumb {
background-color: #6491D6 !important;
}
/* 修改选中行背景色 */
.el-table__body tr.current-row>td {
background-color: #a4beef !important;
}
// /* 可选:修改悬停颜色 */
// .el-table__body tr.hover-row>td {
// background-color: #a4beef !important;
// }
}
.el-table--border {
border: 1px solid #96B8D9 !important;
border-top: none !important;
}
.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #96B8D9 !important;
}
.el-table--border .el-table__cell {
border-right: 1px solid #96B8D9 !important;
}
/* 表格内部边框(可选) */
.el-table--border th.el-table__cell {
border-right: 1px solid #1F6DD3 !important;
}
/* 在你的全局样式文件中 */
.el-input__wrapper {
border-color: #96B8D9 !important;
// background-color: #f00;
}
.vxe-table {
border: 1px solid #96B8D9 !important;
/* 蓝色外边框 */
border-radius: 8px;
/* 可选:添加边框圆角 */
overflow: hidden;
}
.mytable-style {
.vxe-header--column {
word-break: break-word;
background-color: #1F6DD3 !important;
color: #FFF;
height: 40px !important;
font-size: 13px;
border-color: #1F6DD3 !important;
}
}
// 高亮
.vxe-table--render-default .vxe-body--row.row--current {
background-color: #a4beef !important;
}
// 滚动条
.mytable-style ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.mytable-style ::-webkit-scrollbar-thumb {
background-color: #e1e9f7;
}
.mytable-style ::-webkit-scrollbar-thumb:hover {
background-color: #e1e9f7;
}
.btn_green {
background-color: #aef5ef !important;
color: #148A7F;
border-color: #aef5ef;
}
/** 表单布局 **/
.form-header {
font-size: 15px;
color: #6379bb;
border-bottom: 1px solid #ddd;
margin: 8px 10px 25px 10px;
padding-bottom: 5px
}
/** 表格布局 **/
.pagination-container {
position: relative; position: relative;
height: 25px; height: 25px;
margin-bottom: 10px; margin-bottom: 10px;
margin-top: 15px; margin-top: 15px;
padding: 10px 20px !important; padding: 10px 20px !important;
} }
.el-dialog .pagination-container { .el-dialog .pagination-container {
position: static !important; position: static !important;
} }
/* tree border */ /* tree border */
.tree-border { .tree-border {
margin-top: 5px; margin-top: 5px;
border: 1px solid #e5e6e7; border: 1px solid #e5e6e7;
background: #FFFFFF none; background: #FFFFFF none;
border-radius:4px; border-radius: 4px;
width: 100%; width: 100%;
} }
.pagination-container .el-pagination { .pagination-container .el-pagination {
right: 0; right: 0;
position: absolute; position: absolute;
} }
@media ( max-width : 768px) { @media (max-width : 768px) {
.pagination-container .el-pagination > .el-pagination__jump { .pagination-container .el-pagination>.el-pagination__jump {
display: none !important; display: none !important;
} }
.pagination-container .el-pagination > .el-pagination__sizes {
.pagination-container .el-pagination>.el-pagination__sizes {
display: none !important; display: none !important;
} }
} }
.el-table .fixed-width .el-button--small { .el-table .fixed-width .el-button--small {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
width: inherit; width: inherit;
} }
/** 表格更多操作下拉样式 */ /** 表格更多操作下拉样式 */
.el-table .el-dropdown-link { .el-table .el-dropdown-link {
cursor: pointer; cursor: pointer;
color: #409EFF; color: #1F6DD3;
margin-left: 10px; margin-left: 10px;
} }
.el-table .el-dropdown, .el-icon-arrow-down { .el-table .el-dropdown,
.el-icon-arrow-down {
font-size: 12px; font-size: 12px;
} }
.el-tree-node__content > .el-checkbox { .el-tree-node__content>.el-checkbox {
margin-right: 8px; margin-right: 8px;
} }
.list-group-striped > .list-group-item { .list-group-striped>.list-group-item {
border-left: 0; border-left: 0;
border-right: 0; border-right: 0;
border-radius: 0; border-radius: 0;
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
.list-group { .list-group {
padding-left: 0px; padding-left: 0px;
list-style: none; list-style: none;
} }
.list-group-item { .list-group-item {
border-bottom: 1px solid #e7eaec; border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec; border-top: 1px solid #e7eaec;
margin-bottom: -1px; margin-bottom: -1px;
padding: 11px 0px; padding: 11px 0px;
font-size: 13px; font-size: 13px;
} }
.pull-right { .pull-right {
float: right !important; float: right !important;
} }
.el-card__header { .el-card__header {
padding: 14px 15px 7px !important; padding: 14px 15px 7px !important;
min-height: 40px; min-height: 40px;
} }
.el-card__body { .el-card__body {
padding: 15px 20px 20px 20px !important; padding: 15px 20px 20px 20px !important;
} }
.card-box { .card-box {
padding-right: 15px; padding-right: 15px;
padding-left: 15px; padding-left: 15px;
margin-bottom: 10px; margin-bottom: 10px;
} }
/* button color */ /* button color */
.el-button--cyan.is-active, .el-button--cyan.is-active,
.el-button--cyan:active { .el-button--cyan:active {
background: #20B2AA; background: #20B2AA;
border-color: #20B2AA; border-color: #20B2AA;
color: #FFFFFF; color: #FFFFFF;
} }
.el-button--cyan:focus, .el-button--cyan:focus,
.el-button--cyan:hover { .el-button--cyan:hover {
background: #48D1CC; background: #48D1CC;
border-color: #48D1CC; border-color: #48D1CC;
color: #FFFFFF; color: #FFFFFF;
} }
.el-button--cyan { .el-button--cyan {
background-color: #20B2AA; background-color: #20B2AA;
border-color: #20B2AA; border-color: #20B2AA;
color: #FFFFFF; color: #FFFFFF;
} }
/* text color */ /* text color */
.text-navy { .text-navy {
color: #1ab394; color: #1ab394;
} }
.text-primary { .text-primary {
color: inherit; color: inherit;
} }
.text-success { .text-success {
color: #1c84c6; color: #1c84c6;
} }
.text-info { .text-info {
color: #23c6c8; color: #23c6c8;
} }
.text-warning { .text-warning {
color: #f8ac59; color: #f8ac59;
} }
.text-danger { .text-danger {
color: #ed5565; color: #ed5565;
} }
.text-muted { .text-muted {
color: #888888; color: #888888;
} }
/* image */ /* image */
.img-circle { .img-circle {
border-radius: 50%; border-radius: 50%;
} }
.img-lg { .img-lg {
width: 120px; width: 120px;
height: 120px; height: 120px;
} }
.avatar-upload-preview { .avatar-upload-preview {
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translate(50%, -50%); transform: translate(50%, -50%);
@ -266,16 +410,16 @@
border-radius: 50%; border-radius: 50%;
box-shadow: 0 0 4px #ccc; box-shadow: 0 0 4px #ccc;
overflow: hidden; overflow: hidden;
} }
/* 拖拽列样式 */ /* 拖拽列样式 */
.sortable-ghost{ .sortable-ghost {
opacity: .8; opacity: .8;
color: #fff!important; color: #fff !important;
background: #42b983!important; background: #42b983 !important;
} }
/* 表格右侧工具栏样式 */ /* 表格右侧工具栏样式 */
.top-right-btn { .top-right-btn {
margin-left: auto; margin-left: auto;
} }

View File

@ -36,7 +36,7 @@ $base-sub-menu-background:#000c17;
$base-sub-menu-hover:#001528; $base-sub-menu-hover:#001528;
*/ */
$--color-primary: #409EFF; $--color-primary: #1F6DD3;
$--color-success: #67C23A; $--color-success: #67C23A;
$--color-warning: #E6A23C; $--color-warning: #E6A23C;
$--color-danger: #F56C6C; $--color-danger: #F56C6C;

View File

@ -0,0 +1,152 @@
<!--
* SelectTable 下拉表格组件
* @author: Goal
* @since: 2023-11-13
* SelectTable.vue
*
* 属性(带有[Required]为必填属性)
* data[Required]: 双向绑定数据
* fields[Required]: 表格列对象
* tableData[Required]: 表格数据对象
* value: 双向绑定数据的值(为空时data属性为表格当前行数据对象)
* label: 选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey)
* objKey: 对象key(绑定值的唯一标识,绑定值为对象时必填)
* placeholder: 选择框占位文本
* size: 组件大小
* border: 表格是否带有边框
-->
<template>
<div>
<el-select ref="selectTable" v-model="state.selectShowValue" :placeholder="props.placeholder" :size="props.size">
<template #empty>
<el-table :data="props.tableData" :highlight-current-row="props.isHighlight" style="width: 100%"
:size="props.size" :border="props.border" @current-change="handleCurrentChange">
<el-table-column v-for="field in props.fields" :prop="field.prop" :label="field.label" :width="field.width"
:show-overflow-tooltip="field.showTooltip" />
</el-table>
</template>
</el-select>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
interface Field {
prop: string;
label: string;
width: number;
showTooltip?: boolean;
}
interface Props {
data: any;
fields: Field[];
tableData: object[];
label?: string;
value?: string;
objKey?: string;
isHighlight?: boolean;
size?: string;
placeholder?: string;
border?: boolean;
}
const props = withDefaults(defineProps < Props > (), {
placeholder: "请选择",
size: "default",
isHighlight: true,
value: undefined,
label: undefined,
border: false,
});
interface Emits {
(e: "update:data", val: any): void;
}
const emits = defineEmits < Emits > ();
onMounted(() => {
//处理数据回显
// debugger;
if (props.data !== null) {
// 配置label则显示label
if (typeof props.label !== "undefined") {
// 配置了value属性,则找到与value属性值一样的表格行对象,将配置的label回显到下拉框
if (typeof props.value !== "undefined") {
let row = props.tableData.find(
(item) => item[props.value] === props.data
);
state.selectShowValue =
typeof row === "undefined" ? props.data : row[props.label];
// 配置了objKey属性,则找到与objKey属性值一样的表格行对象,将配置的label回显到下拉框
} else if (typeof props.objKey !== "undefined") {
let row = props.tableData.find(
(item) => item[props.objKey] === props.data[props.objKey]
);
state.selectShowValue =
typeof row === "undefined"
? props.data[props.objKey]
: row[props.label];
}
//没有配置label属性则回显value或者objKey
} else {
// 配置了value属性,则找到与value属性值一样的表格行对象,将配置的label回显到下拉框
if (typeof props.value !== "undefined") {
let row = props.tableData.find(
(item) => item[props.value] === props.data
);
state.selectShowValue =
typeof row === "undefined" ? props.data : row[props.value];
// 配置了objKey属性,则找到与objKey属性值一样的表格行对象,将配置的label回显到下拉框
} else if (typeof props.objKey !== "undefined") {
let row = props.tableData.find(
(item) => item[props.objKey] === props.data
);
state.selectShowValue =
typeof row === "undefined" ? props.data : row[props.objKey];
}
}
}
});
const selectTable = ref();
const state = reactive({
selectValue: null,
selectShowValue: null,
});
/**
* 点击当前行事件
* @param val
*/
const handleCurrentChange = (val: any) => {
// 如果没有配置label则显示value或者objKey
setLabel(val);
// 设置双向绑定值
if (typeof props.value !== "undefined") {
emits("update:data", val[props.value]);
} else {
emits("update:data", val);
}
selectTable.value.blur();
};
const setLabel = (val: any) => {
// 如果没有配置label则显示value或者objKey
if (typeof props.label === "undefined") {
// 如果配置了value,则显示value
if (typeof props.value !== "undefined") {
state.selectShowValue = val[props.value];
} else if (typeof props.objKey !== "undefined") {
//没有配置value但配置了objKey,则显示objKey
state.selectShowValue = val[props.objKey];
}
} else {
state.selectShowValue = val[props.label];
}
};
</script>
<style scoped></style>

View File

@ -1,30 +1,18 @@
<template> <template>
<el-menu <el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
:default-active="activeMenu"
mode="horizontal"
@select="handleSelect"
:ellipsis="false"
>
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"> <el-menu-item :style="{ '--theme': theme }" :index="item.path" :key="index" v-if="index < visibleNumber">
<svg-icon <svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta.icon" />
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }} {{ item.meta.title }}
</el-menu-item> </el-menu-item>
</template> </template>
<!-- 顶部菜单超出数量折叠 --> <!-- 顶部菜单超出数量折叠 -->
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber"> <el-sub-menu :style="{ '--theme': theme }" index="more" v-if="topMenus.length > visibleNumber">
<template #title>更多菜单</template> <template #title>更多菜单</template>
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item <el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber">
:index="item.path" <svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta.icon" />
:key="index"
v-if="index >= visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }} {{ item.meta.title }}
</el-menu-item> </el-menu-item>
</template> </template>
@ -79,10 +67,10 @@ const childrenMenus = computed(() => {
routers.value.map((router) => { routers.value.map((router) => {
for (let item in router.children) { for (let item in router.children) {
if (router.children[item].parentPath === undefined) { if (router.children[item].parentPath === undefined) {
if(router.path === "/") { if (router.path === "/") {
router.children[item].path = "/" + router.children[item].path; router.children[item].path = "/" + router.children[item].path;
} else { } else {
if(!isHttp(router.children[item].path)) { if (!isHttp(router.children[item].path)) {
router.children[item].path = router.path + "/" + router.children[item].path; router.children[item].path = router.path + "/" + router.children[item].path;
} }
} }
@ -104,7 +92,7 @@ const activeMenu = computed(() => {
if (!route.meta.link) { if (!route.meta.link) {
appStore.toggleSideBarHide(false); appStore.toggleSideBarHide(false);
} }
} else if(!route.children) { } else if (!route.children) {
activePath = path; activePath = path;
appStore.toggleSideBarHide(true); appStore.toggleSideBarHide(true);
} }
@ -149,7 +137,7 @@ function activeRoutes(key) {
} }
}); });
} }
if(routes.length > 0) { if (routes.length > 0) {
permissionStore.setSidebarRouters(routes); permissionStore.setSidebarRouters(routes);
} else { } else {
appStore.toggleSideBarHide(true); appStore.toggleSideBarHide(true);
@ -170,7 +158,7 @@ onMounted(() => {
</script> </script>
<style lang="scss"> <style lang="scss">
.topmenu-container.el-menu--horizontal > .el-menu-item { .topmenu-container.el-menu--horizontal>.el-menu-item {
float: left; float: left;
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
@ -179,13 +167,14 @@ onMounted(() => {
margin: 0 10px !important; margin: 0 10px !important;
} }
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title { .topmenu-container.el-menu--horizontal>.el-menu-item.is-active,
.el-menu--horizontal>.el-sub-menu.is-active .el-submenu__title {
border-bottom: 2px solid #{'var(--theme)'} !important; border-bottom: 2px solid #{'var(--theme)'} !important;
color: #303133; color: #303133;
} }
/* sub-menu item */ /* sub-menu item */
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title { .topmenu-container.el-menu--horizontal>.el-sub-menu .el-sub-menu__title {
float: left; float: left;
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
@ -195,7 +184,9 @@ onMounted(() => {
} }
/* 背景色隐藏 */ /* 背景色隐藏 */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover { .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
.topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
background-color: #ffffff !important; background-color: #ffffff !important;
} }

View File

@ -273,15 +273,15 @@ defineExpose({
margin-right: 20px; margin-right: 20px;
} }
/* 修改选中行背景色 */ // /* 修改选中行背景色 */
::v-deep .el-table__body tr.current-row>td { // ::v-deep .el-table__body tr.current-row>td {
background-color: #add7f7 !important; // background-color: #a4beef !important;
} // }
/* 可选:修改悬停颜色 */ // /* 可选:修改悬停颜色 */
::v-deep .el-table__body tr.hover-row>td { // ::v-deep .el-table__body tr.hover-row>td {
background-color: #326ca5 !important; // background-color: #a4beef !important;
} // }
/* 响应式设计 */ /* 响应式设计 */
@media (max-width: 768px) { @media (max-width: 768px) {

View File

@ -8,8 +8,10 @@
<img src="@/assets/images/dark.svg" alt="dark" /> <img src="@/assets/images/dark.svg" alt="dark" />
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check"> <i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" /> focusable="false" class>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
</svg> </svg>
</i> </i>
</div> </div>
@ -18,8 +20,10 @@
<img src="@/assets/images/light.svg" alt="light" /> <img src="@/assets/images/light.svg" alt="light" />
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check"> <i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" /> focusable="false" class>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
</svg> </svg>
</i> </i>
</div> </div>
@ -28,7 +32,7 @@
<div class="drawer-item"> <div class="drawer-item">
<span>主题颜色</span> <span>主题颜色</span>
<span class="comp-style"> <span class="comp-style">
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/> <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
</span> </span>
</div> </div>
<el-divider /> <el-divider />
@ -92,7 +96,7 @@ const showSettings = ref(false);
const theme = ref(settingsStore.theme); const theme = ref(settingsStore.theme);
const sideTheme = ref(settingsStore.sideTheme); const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore); const storeSettings = computed(() => settingsStore);
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]); const predefineColors = ref(["#1F6DD3", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
/** 是否需要topnav */ /** 是否需要topnav */
function topNavChange(val) { function topNavChange(val) {
@ -144,10 +148,12 @@ defineExpose({
color: rgba(0, 0, 0, 0.85); color: rgba(0, 0, 0, 0.85);
line-height: 22px; line-height: 22px;
font-weight: bold; font-weight: bold;
.drawer-title { .drawer-title {
font-size: 14px; font-size: 14px;
} }
} }
.setting-drawer-block-checbox { .setting-drawer-block-checbox {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;

View File

@ -1,11 +1,16 @@
import { createApp } from 'vue' import {
createApp
} from 'vue'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import ElementPlus from 'element-plus' import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import locale from 'element-plus/es/locale/lang/zh-cn' import locale from 'element-plus/es/locale/lang/zh-cn'
import VxeUI from 'vxe-pc-ui'
import 'vxe-pc-ui/lib/style.css'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
import '@/assets/styles/index.scss' // global css import '@/assets/styles/index.scss' // global css
// 先导入 jQuery 并设置全局变量 // 先导入 jQuery 并设置全局变量
@ -20,7 +25,9 @@ import directive from './directive' // directive
// 注册指令 // 注册指令
import plugins from './plugins' // plugins import plugins from './plugins' // plugins
import { download } from '@/utils/request' import {
download
} from '@/utils/request'
// svg图标 // svg图标
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
@ -29,8 +36,18 @@ import elementIcons from '@/components/SvgIcon/svgicon'
import './permission' // permission control import './permission' // permission control
import {useDict, comDict} from '@/utils/dict' import {
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi' useDict,
comDict
} from '@/utils/dict'
import {
parseTime,
resetForm,
addDateRange,
handleTree,
selectDictLabel,
selectDictLabels
} from '@/utils/ruoyi'
// 分页组件 // 分页组件
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
@ -82,7 +99,8 @@ app.use(plugins)
app.use(elementIcons) app.use(elementIcons)
app.component('svg-icon', SvgIcon) app.component('svg-icon', SvgIcon)
directive(app) directive(app)
app.use(VxeUI)
app.use(VXETable)
// 使用element-plus 并且设置全局的大小 // 使用element-plus 并且设置全局的大小
app.use(ElementPlus, { app.use(ElementPlus, {
locale: locale, locale: locale,

View File

@ -1,17 +1,27 @@
import router from './router' import router from './router'
import { ElMessage } from 'element-plus' import {
ElMessage
} from 'element-plus'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth' import {
import { isHttp } from '@/utils/validate' getToken
import { isRelogin } from '@/utils/request' } from '@/utils/auth'
import {
isHttp
} from '@/utils/validate'
import {
isRelogin
} from '@/utils/request'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission'
NProgress.configure({ showSpinner: false }); NProgress.configure({
showSpinner: false
});
const whiteList = ['/login', '/register']; const whiteList = ['/login', '/register', '/report'];
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start() NProgress.start()
@ -19,7 +29,9 @@ router.beforeEach((to, from, next) => {
to.meta.title && useSettingsStore().setTitle(to.meta.title) to.meta.title && useSettingsStore().setTitle(to.meta.title)
/* has token*/ /* has token*/
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({
path: '/'
})
NProgress.done() NProgress.done()
} else if (whiteList.indexOf(to.path) !== -1) { } else if (whiteList.indexOf(to.path) !== -1) {
next() next()
@ -36,12 +48,17 @@ router.beforeEach((to, from, next) => {
router.addRoute(route) // 动态添加可访问路由表 router.addRoute(route) // 动态添加可访问路由表
} }
}) })
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 next({
...to,
replace: true
}) // hack方法 确保addRoutes已完成
}) })
}).catch(err => { }).catch(err => {
useUserStore().logOut().then(() => { useUserStore().logOut().then(() => {
ElMessage.error(err) ElMessage.error(err)
next({ path: '/' }) next({
path: '/'
})
}) })
}) })
} else { } else {

View File

@ -1,4 +1,7 @@
import { createWebHistory, createRouter } from 'vue-router' import {
createWebHistory,
createRouter
} from 'vue-router'
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from '@/layout'
@ -25,17 +28,14 @@ import Layout from '@/layout'
*/ */
// 公共路由 // 公共路由
export const constantRoutes = [ export const constantRoutes = [{
{
path: '/redirect', path: '/redirect',
component: Layout, component: Layout,
hidden: true, hidden: true,
children: [ children: [{
{
path: '/redirect/:path(.*)', path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue') component: () => import('@/views/redirect/index.vue')
} }]
]
}, },
{ {
path: '/login', path: '/login',
@ -47,6 +47,11 @@ export const constantRoutes = [
component: () => import('@/views/register'), component: () => import('@/views/register'),
hidden: true hidden: true
}, },
{
path: '/report',
component: () => import('@/views/checkCode/report'),
hidden: true
},
{ {
path: "/:pathMatch(.*)*", path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'), component: () => import('@/views/error/404'),
@ -61,102 +66,109 @@ export const constantRoutes = [
path: '', path: '',
component: Layout, component: Layout,
redirect: '/index', redirect: '/index',
children: [ children: [{
{
path: '/index', path: '/index',
component: () => import('@/views/index'), component: () => import('@/views/index'),
name: 'Index', name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true } meta: {
title: '首页',
icon: 'dashboard',
affix: true
} }
] }]
}, },
{ {
path: '/user', path: '/user',
component: Layout, component: Layout,
hidden: true, hidden: true,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [{
{
path: 'profile', path: 'profile',
component: () => import('@/views/system/user/profile/index'), component: () => import('@/views/system/user/profile/index'),
name: 'Profile', name: 'Profile',
meta: { title: '个人中心', icon: 'user' } meta: {
title: '个人中心',
icon: 'user'
} }
] }]
} }
] ]
// 动态路由,基于用户权限动态去加载 // 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [ export const dynamicRoutes = [{
{
path: '/system/user-auth', path: '/system/user-auth',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:user:edit'], permissions: ['system:user:edit'],
children: [ children: [{
{
path: 'role/:userId(\\d+)', path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'), component: () => import('@/views/system/user/authRole'),
name: 'AuthRole', name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' } meta: {
title: '分配角色',
activeMenu: '/system/user'
} }
] }]
}, },
{ {
path: '/system/role-auth', path: '/system/role-auth',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:role:edit'], permissions: ['system:role:edit'],
children: [ children: [{
{
path: 'user/:roleId(\\d+)', path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'), component: () => import('@/views/system/role/authUser'),
name: 'AuthUser', name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' } meta: {
title: '分配用户',
activeMenu: '/system/role'
} }
] }]
}, },
{ {
path: '/system/dict-data', path: '/system/dict-data',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:dict:list'], permissions: ['system:dict:list'],
children: [ children: [{
{
path: 'index/:dictId(\\d+)', path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'), component: () => import('@/views/system/dict/data'),
name: 'Data', name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' } meta: {
title: '字典数据',
activeMenu: '/system/dict'
} }
] }]
}, },
{ {
path: '/monitor/job-log', path: '/monitor/job-log',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['monitor:job:list'], permissions: ['monitor:job:list'],
children: [ children: [{
{
path: 'index/:jobId(\\d+)', path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'), component: () => import('@/views/monitor/job/log'),
name: 'JobLog', name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' } meta: {
title: '调度日志',
activeMenu: '/monitor/job'
} }
] }]
}, },
{ {
path: '/tool/gen-edit', path: '/tool/gen-edit',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['tool:gen:edit'], permissions: ['tool:gen:edit'],
children: [ children: [{
{
path: 'index/:tableId(\\d+)', path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'), component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit', name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' } meta: {
title: '修改生成配置',
activeMenu: '/tool/gen'
} }
] }]
} }
] ]
@ -167,7 +179,9 @@ const router = createRouter({
if (savedPosition) { if (savedPosition) {
return savedPosition return savedPosition
} else { } else {
return { top: 0 } return {
top: 0
}
} }
}, },
}); });

View File

@ -1,16 +1,25 @@
import defaultSettings from '@/settings' import defaultSettings from '@/settings'
import { useDynamicTitle } from '@/utils/dynamicTitle' import {
useDynamicTitle
} from '@/utils/dynamicTitle'
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings const {
sideTheme,
showSettings,
topNav,
tagsView,
fixedHeader,
sidebarLogo,
dynamicTitle
} = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
const useSettingsStore = defineStore( const useSettingsStore = defineStore(
'settings', 'settings', {
{
state: () => ({ state: () => ({
title: '', //标题 title: '', //标题
theme: storageSetting.theme || '#409EFF', //主题颜色 theme: storageSetting.theme || '#1F6DD3', //主题颜色
sideTheme: storageSetting.sideTheme || sideTheme, //侧边栏主题 sideTheme: storageSetting.sideTheme || sideTheme, //侧边栏主题
showSettings: showSettings, //是否显示设置按钮 showSettings: showSettings, //是否显示设置按钮
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, //顶部导航 topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, //顶部导航
@ -22,7 +31,10 @@ const useSettingsStore = defineStore(
actions: { actions: {
// 修改布局设置 // 修改布局设置
changeSetting(data) { changeSetting(data) {
const { key, value } = data const {
key,
value
} = data
if (this.hasOwnProperty(key)) { if (this.hasOwnProperty(key)) {
this[key] = value this[key] = value
} }

View File

@ -1,10 +1,18 @@
import { login, logout, getInfo } from '@/api/login' import {
import { getToken, setToken, removeToken } from '@/utils/auth' login,
logout,
getInfo,
loginAnonymous
} from '@/api/login'
import {
getToken,
setToken,
removeToken
} from '@/utils/auth'
import defAva from '@/assets/images/profile.jpg' import defAva from '@/assets/images/profile.jpg'
const useUserStore = defineStore( const useUserStore = defineStore(
'user', 'user', {
{
state: () => ({ state: () => ({
token: getToken(), token: getToken(),
id: '', id: '',
@ -14,6 +22,20 @@ const useUserStore = defineStore(
permissions: [] permissions: []
}), }),
actions: { actions: {
// 单点登录
loginAnonymous(data) {
return new Promise((resolve, reject) => {
loginAnonymous({
username: data.name
}).then(res => {
setToken(res.token)
this.token = res.token
resolve()
}).catch(error => {
reject(error)
})
})
},
// 登录 // 登录
login(userInfo) { login(userInfo) {
const username = userInfo.username.trim() const username = userInfo.username.trim()

View File

@ -63,8 +63,26 @@ function getContrastTextColor(bgColor: string): string {
// 根据亮度返回对比色 // 根据亮度返回对比色
return luminance > 0.5 ? '#333333' : '#FFFFFF'; return luminance > 0.5 ? '#333333' : '#FFFFFF';
} }
// 打印https pdf
function printPDF(pdfUrl: string) {
// 验证URL是否为HTTPS
if (pdfUrl.startsWith('http')) {
console.warn('PDF URL不是HTTPS协议,可能存在安全风险');
}
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = pdfUrl;
document.body.appendChild(iframe);
iframe.onload = () => {
iframe.contentWindow?.print();
setTimeout(() => URL.revokeObjectURL(pdfUrl), 1000); // 释放内存
};
}
// 打印base64 pdf // 打印base64 pdf
function printPDF(base64: string) { function printBase64PDF(base64: string) {
const blob = base64ToBlob(base64); const blob = base64ToBlob(base64);
const pdfUrl = URL.createObjectURL(blob); const pdfUrl = URL.createObjectURL(blob);
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
@ -87,5 +105,8 @@ function base64ToBlob(base64: string) {
} }
export const classCom = { decimalToHexColor, convertHexToNumber, getContrastTextColor, printPDF }
export const classCom = { decimalToHexColor, convertHexToNumber, getContrastTextColor, printBase64PDF, printPDF }

View File

@ -1,6 +1,10 @@
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data' import {
import { getComDicts } from '@/api/liswork/dict/ComDict.js' getDicts
} from '@/api/system/dict/data'
import {
getComDicts
} from '@/api/liswork/dict/ComDict'
/** /**
* 获取字典数据 * 获取字典数据
*/ */
@ -14,7 +18,12 @@ export function useDict(...args) {
res.value[dictType] = dicts; res.value[dictType] = dicts;
} else { } else {
getDicts(dictType).then(resp => { getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass })) res.value[dictType] = resp.data.map(p => ({
label: p.dictLabel,
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass
}))
useDictStore().setDict(dictType, res.value[dictType]); useDictStore().setDict(dictType, res.value[dictType]);
}) })
} }
@ -39,7 +48,9 @@ export function comDict(...args) {
res.value[dictType] = dicts; res.value[dictType] = dicts;
} else { } else {
// 缓存不存在,发起请求并记录 Promise // 缓存不存在,发起请求并记录 Promise
const promise = getComDicts({ zdlb: dictType }).then(resp => { const promise = getComDicts({
zdlb: dictType
}).then(resp => {
const dictList = resp.data.map(p => ({ const dictList = resp.data.map(p => ({
label: p.zdmc, label: p.zdmc,
value: p.zddh, value: p.zddh,

View File

@ -1,16 +1,33 @@
import { createVNode, render } from 'vue'; import {
createVNode,
render
} from 'vue';
import axios from 'axios' import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' import {
import { getToken } from '@/utils/auth' ElNotification,
ElMessageBox,
ElMessage,
ElLoading
} from 'element-plus'
import {
getToken
} from '@/utils/auth'
import errorCode from '@/utils/errorCode' import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi' import {
tansParams,
blobValidate
} from '@/utils/ruoyi'
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import {
saveAs
} from 'file-saver'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
let downloadLoadingInstance; let downloadLoadingInstance;
// 是否显示重新登录 // 是否显示重新登录
export let isRelogin = { show: false }; export let isRelogin = {
show: false
};
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例 // 创建axios实例
@ -106,7 +123,11 @@ service.interceptors.response.use(res => {
if (code === 401) { if (code === 401) {
if (!isRelogin.show) { if (!isRelogin.show) {
isRelogin.show = true; isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
isRelogin.show = false; isRelogin.show = false;
useUserStore().logOut().then(() => { useUserStore().logOut().then(() => {
location.href = '/index'; location.href = '/index';
@ -117,25 +138,43 @@ service.interceptors.response.use(res => {
} }
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) { } else if (code === 500) {
ElMessage({ message: msg, type: 'error' }) ElMessage({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg))
} else if (code === 601) { } else if (code === 601) {
ElMessage({ message: msg, type: 'warning' }) ElMessage({
message: msg,
type: 'warning'
})
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg))
} else if (code === 3) { } else if (code === 3) {
//提示并成功 //提示并成功
ElMessage({ message: msg, type: 'success' }) ElMessage({
message: msg,
type: 'success'
})
return Promise.resolve(res.data) return Promise.resolve(res.data)
} else if (code === 2) { } else if (code === 2) {
//选择是否后续操作 //选择是否后续操作
ElMessageBox.confirm(msg, '系统提示', { confirmButtonText: '是', cancelButtonText: '否', type: 'warning' }).then(() => { ElMessageBox.confirm(msg, '系统提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning'
}).then(() => {
return Promise.resolve(res.data); return Promise.resolve(res.data);
}) })
return Promise.reject('error'); return Promise.reject('error');
} else if (code === 1) { } else if (code === 1) {
//提示并失败 //提示并失败
ElMessage({ message: msg, type: 'error' }) ElNotification({
return Promise.reject(new Error(msg)) title: '提示',
message: msg,
type: 'error',
duration: 0,
})
return Promise.resolve(res.data)
} else if (code === 4) { } else if (code === 4) {
//返回特定值,需要后续处理,表示当前操作需要处理后再调用,problemId是步骤关键参数,需要再次调用时引入 //返回特定值,需要后续处理,表示当前操作需要处理后再调用,problemId是步骤关键参数,需要再次调用时引入
return Promise.resolve(res.data) return Promise.resolve(res.data)
@ -161,7 +200,12 @@ service.interceptors.response.use(res => {
cancelButtonText: '否', cancelButtonText: '否',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
const { url, method, data: data0, params: params0 } = res.data; const {
url,
method,
data: data0,
params: params0
} = res.data;
// 4. 构造新请求参数,携带问题ID(根据后端要求决定传递方式) // 4. 构造新请求参数,携带问题ID(根据后端要求决定传递方式)
const newParams = { const newParams = {
...params0, ...params0,
@ -189,7 +233,9 @@ service.interceptors.response.use(res => {
}); });
}); });
} else if (code !== 200 && code !== 0) { } else if (code !== 200 && code !== 0) {
ElNotification.error({ title: msg }) ElNotification.error({
title: msg
})
return Promise.reject('error') return Promise.reject('error')
} else { } else {
return Promise.resolve(res.data) return Promise.resolve(res.data)
@ -197,7 +243,9 @@ service.interceptors.response.use(res => {
}, },
error => { error => {
console.log('err' + error) console.log('err' + error)
let { message } = error; let {
message
} = error;
if (message == "Network Error") { if (message == "Network Error") {
message = "后端接口连接异常"; message = "后端接口连接异常";
} else if (message.includes("timeout")) { } else if (message.includes("timeout")) {
@ -205,17 +253,28 @@ service.interceptors.response.use(res => {
} else if (message.includes("Request failed with status code")) { } else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常"; message = "系统接口" + message.substr(message.length - 3) + "异常";
} }
ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) ElMessage({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error) return Promise.reject(error)
} }
) )
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) downloadLoadingInstance = ElLoading.service({
text: "正在下载数据,请稍候",
background: "rgba(0, 0, 0, 0.7)",
})
return service.post(url, params, { return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }], transformRequest: [(params) => {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, return tansParams(params)
}],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
responseType: 'blob', responseType: 'blob',
...config ...config
}).then(async (data) => { }).then(async (data) => {

View File

@ -0,0 +1,53 @@
import {
ref,
onMounted
} from 'vue';
import FingerprintJS from '@fingerprintjs/fingerprintjs';
export function useFingerprint() {
const fingerprint = ref('');
const loading = ref(true);
const error = ref(null);
// 初始化FingerprintJS并获取指纹
const getFingerprint = async () => {
try {
loading.value = true;
// 初始化代理
const fp = await FingerprintJS.load({
// 可选配置
monitoring: false,
// 扩展结果数据
extendedResult: true
});
// 获取指纹信息
const result = await fp.get();
// 指纹标识符
fingerprint.value = result.visitorId;
// 可以获取更多详细信息
console.log('指纹详细信息:', result);
return result;
} catch (err) {
error.value = err;
console.error('获取指纹失败:', err);
return null;
} finally {
loading.value = false;
}
};
// 组件挂载时自动获取指纹
onMounted(() => {
getFingerprint();
});
return {
fingerprint,
loading,
error,
getFingerprint
};
}

View File

@ -516,9 +516,8 @@ function adjustTableHeight() {
if (compactForm.value) { if (compactForm.value) {
// 获取高度 // 获取高度
heightForm.value = compactForm.value.offsetHeight; heightForm.value = compactForm.value.offsetHeight;
tableConfig.value.height = `calc(72vh - ${heightForm.value}px)`; // 设置表格容器的高度 tableConfig.value.height = `calc(78vh - ${heightForm.value}px)`; // 设置表格容器的高度
rightTableConfig.value.height = `calc(76vh - ${heightForm.value}px)`; rightTableConfig.value.height = `calc(83vh - ${heightForm.value}px)`;
} }
} }

View File

@ -2,77 +2,89 @@
<div class="app-container"> <div class="app-container">
<div ref="compactForm"> <div ref="compactForm">
<el-row :gutter="10" class="compact-form" v-show="showSearch"> <el-row :gutter="10" class="compact-form" v-show="showSearch">
<el-form :model="queryParams" ref="queryRef" :inline="true" :rules="queryRules"> <el-form :model="queryParams" ref="queryRef" label-width="100px" :rules="queryRules" style="width: 100%;">
<el-row>
<el-col :span="5">
<el-form-item label="日期:" prop="times"> <el-form-item label="日期:" prop="times">
<el-date-picker v-model="queryParams.times" type="daterange" range-separator="-" start-placeholder="开始时间" <el-date-picker v-model="queryParams.times" type="daterange" range-separator="-"
end-placeholder="结束时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /> start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
</el-form-item> </el-form-item>
</el-col>
<el-form-item label="病人状态:" prop="dybz"> <el-col :span="5">
<el-select v-model="queryParams.dybz" placeholder="请选择"> <el-form-item label="打印状态:" prop="dybz">
<el-select v-model="queryParams.dybz" placeholder="请选择" style=" ">
<el-option label="已打印" value="1" /> <el-option label="已打印" value="1" />
<el-option label="未打印" value="0" /> <el-option label="未打印" value="0" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="科室:" prop="ksdh"> <el-form-item label="科室:" prop="ksdh">
<el-select v-model="queryParams.ksdh" placeholder="请选择" filterable clearable> <el-select v-model="queryParams.ksdh" placeholder="请选择" filterable clearable>
<el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="病人类型:" prop="brly"> <el-form-item label="病人类型:" prop="brly">
<el-select v-model="queryParams.brly" placeholder="请选择" style="width: 120px;"> <el-select v-model="queryParams.brly" placeholder="请选择">
<el-option v-for="item in dictData.PT" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in dictData.PT" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="" prop=""> <el-form-item label="" prop="">
<el-checkbox v-model="queryParams.alarmflag" label="仅危急值" @change="alarmHandle" size="large" /> <el-checkbox v-model="queryParams.alarmflag" label="仅危急值" @change="alarmHandle" size="large" />
<el-checkbox v-model="queryParams.jzbz" label="仅急诊" @change="jzbzHandle" size="large" /> <el-checkbox v-model="queryParams.jzbz" label="仅急诊" @change="jzbzHandle" size="large" />
</el-form-item> </el-form-item>
<el-form-item label="姓名" prop="brxm">
<el-col :span="1.5">
<el-input v-model="queryParams.brxm" clearable placeholder="姓名" @keyup.enter="handleQuery"
@clear="handleQuery" />
</el-col> </el-col>
</el-form-item> </el-row>
<el-form-item label="病人号" prop="brdh"> <el-row>
<el-col :span="1.5"> <el-col :span="5">
<el-input v-model="queryParams.brdh" clearable placeholder="病人号" @keyup.enter="handleQuery" <el-form-item label="姓名:" prop="brxm">
<el-input v-model="queryParams.brxm" clearable placeholder="请输入姓名" @keyup.enter="handleQuery"
@clear="handleQuery" /> @clear="handleQuery" />
</el-col>
</el-form-item> </el-form-item>
<el-form-item label="条形码" prop="sqh"> </el-col>
<el-col :span="1.5"> <el-col :span="5">
<el-input v-model="queryParams.sqh" clearable placeholder="条形码" @keyup.enter="handleQuery" <el-form-item label="病人号:" prop="brdh">
<el-input v-model="queryParams.brdh" clearable placeholder="请输入病人号" @keyup.enter="handleQuery"
@clear="handleQuery" /> @clear="handleQuery" />
</el-col>
</el-form-item> </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="5">
<el-form-item label=""> <el-form-item label="">
<el-button icon="search" type="primary" @click="handleQuery"> <el-button icon="search" type="primary" @click="handleQuery">
搜索 搜索
</el-button> </el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
</el-row> </el-row>
</div> </div>
<el-row :gutter="10"> <el-row :gutter="20">
<el-col :span="14" class="right-table"> <el-col :span="14" class="right-table">
<el-row :gutter="10" class="mb8 mt10"> <el-row :gutter="10" class="mb8 mt10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus">打印</el-button> <el-button type="primary" plain icon="Edit" @click="selectStatusRows">勾选所有未打印</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Edit" @click="selectStatusRows">勾选中所有未打印</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain :disabled="single" @click="printPdf">打印单张PDF报告</el-button> <el-button type="primary" plain icon="Upload" @click="printPdf">打印报告</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload">GUID打印报告</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery" <!-- <right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery"
:columns="columns"></right-toolbar> :columns="columns"></right-toolbar> -->
</el-row> </el-row>
<CustomTable ref="tableRefs" :data="tableData" :columns="columns" :config="tableConfig" :loading="loading" <CustomTable ref="tableRefs" :data="tableData" :columns="columns" :config="tableConfig" :loading="loading"
@ -90,21 +102,22 @@
</el-col> </el-col>
<el-col :span="10" class="right-table"> <el-col :span="10" class="right-table">
<div class="title">检验结果</div> <!-- <div class="title">检验结果</div> -->
<div class="mb10"> <div class="mb8 mt10">
<el-button type="primary" @click="openDB">对比历史</el-button> <el-button type="primary" @click="openDB">对比历史</el-button>
<el-button type="primary" @click="copyData('All')">复制全部数据</el-button> <el-select class="ml10" placeholder="请选择复制" style="width: 140px" @change="copyData">
<el-button type="primary" @click="copyData('N')">复制异常数据</el-button> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
<el-button type="primary" @click="copyData('Y')">复制正常数据</el-button> </el-select>
<el-button type="primary">知识库</el-button> <!-- <el-button type="primary">知识库</el-button> -->
</div> </div>
<CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig" :loading="loading" <CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig" :loading="loading"
@row-click="xmrowHandle"> @row-click="xmrowHandle" ref="rightTableRef">
</CustomTable> </CustomTable>
<div class="title mt10">抗生素项目</div> <div class="mt10" v-if="bottomTableData.length > 0"></div>
<CustomTable :data="bottomTableData" :columns="bottomColumns" :config="bottomTableConfig" :loading="loading"> <CustomTable v-if="bottomTableData.length > 0" :data="bottomTableData" :columns="bottomColumns"
:config="bottomTableConfig" :loading="loading">
</CustomTable> </CustomTable>
</el-col> </el-col>
</el-row> </el-row>
@ -115,9 +128,9 @@
<div class="flex-between"> <div class="flex-between">
</div> </div>
<el-table :data="compareTableData" style="width: 100%" @row-click="handleRowClick" highlight-current-row <el-table :data="compareTableData" style="width: 100%" @row-click="handleRowClick" highlight-current-row border
max-height="350px"> max-height="350px">
<el-table-column prop="xmdh" label="项目编号" fixed align="center" width="100px" /> <el-table-column prop="xmdh" label="项目编号" fixed align="center" width="150px" />
<el-table-column prop="xmmc" label="项目名称" fixed align="center" /> <el-table-column prop="xmmc" label="项目名称" fixed align="center" />
<template v-for="(period) in periods" :key="period"> <template v-for="(period) in periods" :key="period">
<el-table-column :label="period" :prop="period" :width="150" align="center" /> <el-table-column :label="period" :prop="period" :width="150" align="center" />
@ -137,18 +150,27 @@ import { reportList, reportPrintPdf, reportResult, reportMedList, reportFsresult
import dayjs from 'dayjs'; import dayjs from 'dayjs';
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 * as echarts from 'echarts'; import * as echarts from 'echarts';
//@ts-ignore //@ts-ignore
import { comDict } from '@/utils/dict' import { comDict } from '@/utils/dict'
const router = useRouter() import { usePrintStore } from '@/store/modules/printStore'
//@ts-ignore
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
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)
const tableRefs = ref() const tableRefs = ref()
const myEcharts = ref() const myEcharts = ref()
const options = ref([
{ label: '复制全部数据', value: 'All' },
{ label: '复制异常数据', value: 'N' },
{ label: '复制正常数据', value: 'Y' },
]
)
interface QueryParams { interface QueryParams {
times: string[]; // 明确类型为字符串元组 times: string[]; // 明确类型为字符串元组
alarmflag: boolean, alarmflag: boolean,
@ -157,26 +179,29 @@ interface QueryParams {
brly: string, brly: string,
brxm: string, brxm: string,
brdh: string, brdh: string,
sqh: string, ch: string,
dybz: string, dybz: string,
sqh: string,
} }
// 提交表单数据 // 提交表单数据
const queryParams = reactive<QueryParams>({ const queryParams = reactive<QueryParams>({
times: ['2025-08-19', '2025-08-19'], times: ['2025-08-19', '2025-08-19'],
ksdh: '', // ksdh: '0203',
ksdh: '0210',
brly: '', brly: '',
sqh: '25080109476',
alarmflag: false, alarmflag: false,
jzbz: false, jzbz: false,
brxm: '', brxm: '',
brdh: '', brdh: '',
sqh: '', ch: '',
dybz: '', dybz: '',
}); });
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const alarmflag = ref('0') const alarmflag = ref()
const jzbz = ref('0') const jzbz = ref()
const showSearch = ref(true); const showSearch = ref(true);
const queryRef = ref() const queryRef = ref()
// 项目点击数据 // 项目点击数据
@ -211,18 +236,19 @@ const rows = ref<any[]>([])
const tableData = ref([]) const tableData = ref([])
// 配置项 // 配置项
const columns = ref<ColumnItem[]>([ const columns = ref<ColumnItem[]>([
{ prop: 'dybz', label: '打印', visible: true, key: 0, slot: 'dybz', }, { prop: 'dybz', label: '打印', align: 'center', visible: true, key: 0, slot: 'dybz', width: '60px' },
{ prop: 'jzbz', label: '急诊', visible: true, key: 1, slot: 'jzbz', }, { prop: 'jzbz', label: '急诊', align: 'center', visible: true, key: 1, slot: 'jzbz', },
{ prop: 'alarmflag', label: '危急值', align: 'center', visible: true, key: 2, slot: 'alarmflag' }, { prop: 'alarmflag', label: '危急值', align: 'center', visible: true, key: 2, slot: 'alarmflag' },
{ prop: 'jyrq', label: '检验日期', align: 'center', visible: true, key: 3, width: '150px' }, { prop: 'jyrq', label: '检验日期', align: 'center', visible: true, key: 3, sortable: true, width: '150px' },
{ prop: 'brdh', label: '病人代号', align: 'center', visible: true, key: 4, width: '110px' }, { prop: 'brdh', label: '病人代号', align: 'center', visible: true, key: 4, width: '110px' },
{ prop: 'ch', label: '床号', align: 'center', visible: true, key: 5, sortable: true, }, { prop: 'brxm', label: '病人姓名', align: 'center', visible: true, key: 4, },
{ prop: 'brxbname', label: '性别', align: 'center', visible: true, key: 11, },
{ prop: 'ch', label: '床号', align: 'center', visible: true, key: 5, },
{ prop: 'yqdl', label: '仪器', align: 'center', visible: true, key: 6, width: '110px' }, { prop: 'yqdl', label: '仪器', align: 'center', visible: true, key: 6, width: '110px' },
{ prop: 'ybh', label: '样本号', align: 'center', visible: true, key: 7, }, { prop: 'ybh', label: '样本号', align: 'center', visible: true, key: 7, },
{ prop: 'yblxname', label: '样本类型', align: 'center', visible: true, key: 8, }, { prop: 'yblxname', label: '样本类型', align: 'center', visible: true, key: 8, },
{ prop: 'jymd', label: '检验目的', align: 'center', visible: true, key: 9, width: '110px' }, { prop: 'jymd', label: '检验目的', align: 'center', visible: true, key: 9, width: '110px' },
{ prop: 'brlyname', label: '病人类型', align: 'center', visible: true, key: 10, }, { prop: 'brlyname', label: '病人类型', align: 'center', visible: true, key: 10, },
{ prop: 'brxbname', label: '性别', align: 'center', visible: true, key: 11, },
{ prop: 'yqmc', label: '仪器名称', align: 'center', visible: true, key: 12, width: '150px' }, { prop: 'yqmc', label: '仪器名称', align: 'center', visible: true, key: 12, width: '150px' },
{ prop: 'yljg', label: '医疗机构', align: 'center', visible: true, key: 13, width: '150px' }, { prop: 'yljg', label: '医疗机构', align: 'center', visible: true, key: 13, width: '150px' },
]) ])
@ -233,6 +259,7 @@ const selectionChange = (selection: any) => {
multiple.value = !selection.length; multiple.value = !selection.length;
} }
const rightTableRef = ref()
const rowHandle = (row: any) => { const rowHandle = (row: any) => {
Info.value = row Info.value = row
const data = { const data = {
@ -243,28 +270,33 @@ const rowHandle = (row: any) => {
reportResult(data).then((res: any) => { reportResult(data).then((res: any) => {
if (res.code == 0) { if (res.code == 0) {
rightTableData.value = res.data rightTableData.value = res.data
rightTableRef.value.setCurrentRow(res.data[0])
xmrowHandle(res.data[0])
} }
}) })
} }
// 打印单张Pdf // 打印单张Pdf
const printPdf = () => { const printPdf = () => {
const data = { const printStore = usePrintStore();
jyrq: rows.value[0].jyrq, const merge = rows.value.map((item: any) => { return `${item.jyrqname.replace(/-/g, '')}_${item.yq.trim()}_${item.ybh}` })
yq: rows.value[0].yq, // const merge = [
ybh: rows.value[0].ybh // "20250819_11_1",
} // "20250819_11_10",
reportPrintPdf(data).then((res: any) => { // "20250819_11_11"
// ]
reportPrintPdf(merge).then((res: any) => {
if (res.code == 0) { if (res.code == 0) {
if (!res.data.base64PDF) return classCom.printBase64PDF(res.data)
classCom.printPDF(res.data.base64PDF) // if (printStore.isConnected) {
// HiprintPrinter.silentPrint([res.data])
// } else {
// classCom.printBase64PDF(res.data)
// }
} }
}) })
} }
// 单元格样式 // 单元格样式
const cellStyleHd = ({ row, column, rowIndex, columnIndex }: { const cellStyleHd = ({ row, column, rowIndex, columnIndex }: {
row: any; row: any;
@ -287,7 +319,7 @@ const cellStyleHd = ({ row, column, rowIndex, columnIndex }: {
const tableConfig = ref( const tableConfig = ref(
{ {
// stripe: true, // 斑马纹 // stripe: true, // 斑马纹
// border: true, // 边框 border: true, // 边框
selection: true, // 多选框 selection: true, // 多选框
index: false, // 序号 index: false, // 序号
height: '', // 高度 height: '', // 高度
@ -310,17 +342,19 @@ const alarmHandle = (val: boolean) => {
if (val) { if (val) {
queryParams.jzbz = false queryParams.jzbz = false
alarmflag.value = '1' alarmflag.value = '1'
jzbz.value = null
} else { } else {
alarmflag.value = '0' alarmflag.value = null
} }
} }
const jzbzHandle = (val: boolean) => { const jzbzHandle = (val: boolean) => {
if (val) { if (val) {
queryParams.alarmflag = false queryParams.alarmflag = false
alarmflag.value = null
jzbz.value = '1' jzbz.value = '1'
} else { } else {
jzbz.value = '0' jzbz.value = null
} }
} }
@ -356,10 +390,10 @@ const rightColumns = ref([
const rightTableConfig = ref( const rightTableConfig = ref(
{ {
// stripe: true, // 斑马纹 // stripe: true, // 斑马纹
// border: true, // 边框 border: true, // 边框
selection: false, // 多选框 selection: false, // 多选框
index: false, // 序号 index: false, // 序号
height: '28vh', // 高度 height: '40vh', // 高度
highlightCurrentRow: true, // 高亮当前行 highlightCurrentRow: true, // 高亮当前行
} }
) )
@ -376,8 +410,18 @@ const xmrowHandle = (row: any) => {
reportMedList(data).then((res: any) => { reportMedList(data).then((res: any) => {
if (res.code == 200) { if (res.code == 200) {
bottomTableData.value = res.rows bottomTableData.value = res.rows
if (res.rows.length > 0) {
nextTick(() => {
rightTableConfig.value.height = `calc(55vh - ${heightForm.value}px)`
rightTableRef.value?.doLayout && rightTableRef.value.doLayout();
});
} else {
nextTick(() => {
rightTableConfig.value.height = `calc(93vh - ${heightForm.value}px)`
rightTableRef.value?.doLayout && rightTableRef.value.doLayout();
});
}
} }
}) })
} }
@ -385,9 +429,7 @@ const periods = ref<string[]>([])
const compareTableData = ref([]) const compareTableData = ref([])
const aaaa = () => {
}
// 历史数据对比 // 历史数据对比
const openDB = () => { const openDB = () => {
// 00507620 // 00507620
@ -603,10 +645,10 @@ const bottomColumns = ref([
const bottomTableConfig = ref( const bottomTableConfig = ref(
{ {
// stripe: true, // 斑马纹 // stripe: true, // 斑马纹
// border: true, // 边框 border: true, // 边框
selection: false, // 多选框 selection: false, // 多选框
index: false, // 序号 index: false, // 序号
height: '28vh', // 高度 height: '37vh', // 高度
highlightCurrentRow: true, // 高亮当前行 highlightCurrentRow: true, // 高亮当前行
} }
) )
@ -626,6 +668,8 @@ onMounted(async () => {
// const start = dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'); //add 时间往后 // const start = dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'); //add 时间往后
// queryParams.times = [start, end]; // queryParams.times = [start, end];
// console.log(queryParams.times); // console.log(queryParams.times);
// 进入页面单点登录
userStore.loginAnonymous({ name: 'admin' })
getList() getList()
adjustTableHeight(); adjustTableHeight();
@ -641,6 +685,7 @@ onMounted(async () => {
const getList = () => { const getList = () => {
if (!queryParams.ksdh) return ElMessage.error('科室代号不能为空');
const data = { const data = {
alarmflag: alarmflag.value, alarmflag: alarmflag.value,
jzbz: jzbz.value, jzbz: jzbz.value,
@ -649,7 +694,7 @@ const getList = () => {
brly: queryParams.brly, brly: queryParams.brly,
brxm: queryParams.brxm, brxm: queryParams.brxm,
brdh: queryParams.brdh, brdh: queryParams.brdh,
sqh: queryParams.sqh, ch: queryParams.ch,
st: '', st: '',
et: '', et: '',
yljg: 1 yljg: 1
@ -676,11 +721,13 @@ watch(showSearch, () => {
function adjustTableHeight() { function adjustTableHeight() {
if (compactForm.value) { if (compactForm.value) {
// 获取高度
heightForm.value = compactForm.value.offsetHeight; heightForm.value = compactForm.value.offsetHeight;
tableConfig.value.height = `calc(80vh - ${heightForm.value}px)`;
rightTableConfig.value.height = `calc(38vh - ${heightForm.value}px)`; nextTick(() => {
bottomTableConfig.value.height = `calc(35vh - ${heightForm.value} }px)`; tableConfig.value.height = `calc(93vh - ${heightForm.value}px)`
tableRefs.value?.doLayout && tableRefs.value.doLayout();
});
} }
} }
@ -688,7 +735,7 @@ function adjustTableHeight() {
<style scoped lang="scss"> <style scoped lang="scss">
.right-table { .right-table {
border: 1px solid #dcdfe6; // border: 1px solid #dcdfe6;
} }
.title { .title {

1061
src/views/index copy.vue Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { computed, reactive, ref, onMounted, nextTick } from "vue"; import { computed, reactive, ref, onMounted, nextTick } from "vue";
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
import { queryComDictListService, addComDictService, updateComDictService, delComDictService } from "../../../api/liswork/dict/ComDict.js"; import { queryComDictListService, addComDictService, updateComDictService, delComDictService } from "../../../api/liswork/dict/ComDict";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();

View File

@ -1,55 +1,28 @@
<template> <template>
<div class="dict-input-wrapper"> <div class="dict-input-wrapper">
<el-input <!-- <el-input v-model="displayValue" :placeholder="placeholder" :clearable="clearable"
v-model="displayValue" :disabled="isDictLoading || disabled" @blur="handleBlur" @clear="handleClear" @dblclick="handleDblClick">
:placeholder="placeholder"
:clearable="clearable"
:disabled="isDictLoading || disabled"
@blur="handleBlur"
@clear="handleClear"
@dblclick="handleDblClick"
>
<template #prefix> <template #prefix>
<el-icon v-if="isDictLoading" size="16"><Loading /></el-icon> <el-icon v-if="isDictLoading" size="16">
<Loading />
</el-icon>
</template> </template>
<template #suffix> <template #suffix>
<el-icon @click="openDictSelector" size="16" class="select-icon"> <el-icon @click="openDictSelector" size="16" class="select-icon">
<ArrowDown /> <ArrowDown />
</el-icon> </el-icon>
</template> </template>
</el-input> </el-input> -->
<!-- 字典选择弹窗 --> <!-- 字典选择弹窗 -->
<el-dialog <el-dialog v-model="dialogVisible" :title="dialogTitle" width="30vw" @close="handleDialogClose">
v-model="dialogVisible" <el-input v-model="searchKey" placeholder="搜索(支持名称、编码、简拼)..." class="mb10" clearable @clear="filterDictData"
:title="dialogTitle" @input="filterDictData" />
width="500px"
@close="handleDialogClose"
>
<el-input
v-model="searchKey"
placeholder="搜索(支持名称、编码、简拼)..."
class="mb-4"
clearable
@clear="filterDictData"
@input="filterDictData"
/>
<el-table <el-table :data="filteredDictData" height="300px" border @row-click="selectItem" @row-dblclick="selectItem">
:data="filteredDictData" <el-table-column prop="value" label="编码" align="center" width="150" />
height="300px" <el-table-column prop="label" label="名称" align="center" show-overflow-tooltip />
border <el-table-column prop="pinyin" label="简拼" align="center" width="100" /> <!-- 显示简拼便于调试 -->
@row-click="selectItem"
@row-dblclick="selectItem"
>
<el-table-column prop="value" label="编码" width="100" />
<el-table-column prop="label" label="名称" width="200" />
<el-table-column prop="pinyin" label="简拼" width="150" /> <!-- 显示简拼便于调试 -->
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button size="small" type="primary" @click="selectItem(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table> </el-table>
</el-dialog> </el-dialog>
</div> </div>
@ -66,9 +39,9 @@ const props = defineProps({
dictType: { type: String, required: true }, dictType: { type: String, required: true },
placeholder: { type: String, default: '请输入或双击选择' }, placeholder: { type: String, default: '请输入或双击选择' },
clearable: { type: Boolean, default: true }, clearable: { type: Boolean, default: true },
disabled: {type: Boolean, default: false}, disabled: { type: Boolean, default: false },
fetchDict: {type: Function, required: true}, fetchDict: { type: Function, required: true },
dialogTitle: {type: String, default: '选择字典项'}, dialogTitle: { type: String, default: '选择字典项' },
loadOnMount: { type: Boolean, default: true } loadOnMount: { type: Boolean, default: true }
}); });
@ -192,7 +165,7 @@ watch(() => props.modelValue, (newVal) => {
labelValue.value = matched?.label || newVal; labelValue.value = matched?.label || newVal;
emit('update:labelValue', labelValue.value); emit('update:labelValue', labelValue.value);
} }
}, {immediate: true}); }, { immediate: true });
// 组件挂载时加载字典 // 组件挂载时加载字典
onMounted(async () => { onMounted(async () => {

View File

@ -1,133 +1,120 @@
<template> <template>
<div class="compact-form"> <div class="labpat_box">
<el-col :span="24" :xs="24"> <div class="flex-between mb5">
<el-form :model="labPat" label-width="100px" class="compact-form" label-position="left"> <el-input placeholder="条码号" class="mr10" style="width:100%" />
<el-form-item label="病人来源" label-width="70px"> <el-button type="primary">打印条码</el-button>
<el-col :span="16"> </div>
<dict-input v-model="labPat.brly" :dict-type="'PT'" placeholder="请输入病人来源" :fetch-dict="fetchDict" <el-form :model="labPat" label-width="70px" class="compact-form" label-position="right"
@update:labelValue="(label) => labPat.brlyLabel = label" dialog-title="选择病人来源" :disabled="labPat.jgbz && labPat.jgbz != 0">
@blur="handleFieldChange" <div :class="['sh_box', getColor(labPat.jgbz)]" v-if="labPat.jgbz != 0">
@keyup.enter="handleFieldChange" <div>{{ getLableType(labPat.jgbz) }}</div>
@select="handleDictSelect" </div>
/> <el-form-item label="病人来源">
<el-col :span="14">
<el-select v-model="labPat.brly" placeholder="请选择" style="width: 100%;" filterable
@change="handleFieldChange">
<el-option v-for="item in dictData.PT" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="10">
<el-switch v-model="jzbzSwitch" active-text="急诊" active-color="#FD0101" @change="handleFieldChange"></el-switch> <el-switch v-model="jzbzSwitch" active-text="急诊" active-color="#FD0101"
@change="handleFieldChange"></el-switch>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item label="病 历 号" label-width="70px"> <el-form-item label="病 历 号">
<el-input v-model="labPat.brdh" placeholder="请输入病历号" <el-input v-model="labPat.brdh" placeholder="请输入病历号" @blur="handleFieldChange"
@blur="handleFieldChange"
@keyup.enter="handleFieldChange"></el-input> @keyup.enter="handleFieldChange"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="姓 名" label-width="70px"> <el-form-item label="姓 名">
<el-input v-model="labPat.brxm" placeholder="请输入姓名" <el-input v-model="labPat.brxm" placeholder="请输入姓名" @blur="handleFieldChange"
@blur="handleFieldChange"
@keyup.enter="handleFieldChange"></el-input> @keyup.enter="handleFieldChange"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="性 别" label-width="70px"> <el-form-item label="性 别">
<el-col :span="8"> <el-select v-model="labPat.brxb" placeholder="请选择" style="width: 100%;" @change="handleFieldChange">
<dict-input v-model="labPat.brxb" placeholder="性别" :dict-type="'SX'" :fetch-dict="fetchDict" <el-option v-for="item in dictData.SX" :key="item.value" :label="item.label" :value="item.value" />
@update:labelValue="(label) => labPat.brxbLabel = label" </el-select>
@blur="handleFieldChange" </el-form-item>
@keyup.enter="handleFieldChange" <el-form-item label="年龄">
@select="handleDictSelect" <el-row :gutter="5">
/> <el-col :span="14">
<el-input v-model="labPat.nl" placeholder="年龄" @blur="handleFieldChange" @keyup.enter="handleFieldChange"
style="width:100%"></el-input>
</el-col> </el-col>
<el-col :span="4"><div>年龄</div></el-col> <el-col :span="10">
<el-col :span="6"> <el-select v-model="labPat.nldw" placeholder="请选择" style="width: 100%;" filterable
<el-input v-model="labPat.nl" placeholder="年龄" @change="handleFieldChange">
@blur="handleFieldChange" <el-option v-for="item in dictData.AU" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="送检科室">
<el-select v-model="labPat.ksdh" placeholder="请选择" style="width: 100%;" filterable @change="handleFieldChange">
<el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="床 号">
<el-input v-model="labPat.ch" placeholder="请输入床号" @blur="handleFieldChange"
@keyup.enter="handleFieldChange"></el-input> @keyup.enter="handleFieldChange"></el-input>
</el-col>
<el-col :span="6">
<dict-input v-model="labPat.nldw" placeholder="年月" :dict-type="'AU'" :fetch-dict="fetchDict"
@update:labelValue="(label) => labPat.nldwLabel = label"
@blur="handleFieldChange"
@keyup.enter="handleFieldChange"
@select="handleDictSelect"
/>
</el-col>
</el-form-item> </el-form-item>
<el-form-item label="送检科室" label-width="70px"> <el-form-item label="样本类型">
<dict-input v-model="labPat.ksdh" placeholder="请输入科室" :dict-type="'DP'" :fetch-dict="fetchDict" <el-select v-model="labPat.yblx" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable>
@update:labelValue="(label) => labPat.ksdhLabel = label" <el-option v-for="item in dictData.BT" :key="item.value" :label="item.label" :value="item.value" />
@blur="handleFieldChange" </el-select>
@keyup.enter="handleFieldChange"
@select="handleDictSelect"
/>
</el-form-item> </el-form-item>
<el-form-item label="床 号" label-width="70px"> <el-form-item label="申请医生">
<el-input v-model="labPat.ch" placeholder="请输入床号" <el-select v-model="labPat.sjys" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable>
@blur="handleFieldChange" <el-option v-for="item in dictData.SRD" :key="item.value" :label="item.label" :value="item.value" />
@keyup.enter="handleFieldChange" </el-select>
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="样本类型" label-width="70px"> <el-form-item label="上机时间">
<dict-input v-model="labPat.yblx" placeholder="请输入样本类型" :dict-type="'BT'" :fetch-dict="fetchDict" <el-date-picker v-model="labPat.sqsj" type="datetime" placeholder="上机时间" format="YYYY-MM-DD HH:mm:ss"
@update:labelValue="(label) => labPat.yblxLabel = label" value-format="YYYY-MM-DD HH:mm:ss" @blur="handleFieldChange"
@blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-date-picker>
@keyup.enter="handleFieldChange"
@select="handleDictSelect"
/>
</el-form-item> </el-form-item>
<el-form-item label="申请医生" label-width="70px"> <el-form-item label="采样时间">
<dict-input v-model="labPat.sjys" placeholder="请输入申请医生" :dict-type="'SRD'" :fetch-dict="fetchDict" <el-date-picker v-model="labPat.cyrq" type="datetime" placeholder="采样时间" format="YYYY-MM-DD HH:mm:ss"
@update:labelValue="(label) => labPat.sjysLabel = label" value-format="YYYY-MM-DD HH:mm:ss" @blur="handleFieldChange"
@blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-date-picker>
@keyup.enter="handleFieldChange"
@select="handleDictSelect"
/>
</el-form-item> </el-form-item>
<el-form-item label="上机时间" label-width="70px"> <el-form-item label="报告时间">
<el-date-picker v-model="labPat.sqsj" type="datetime" placeholder="上机时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" <el-date-picker v-model="labPat.dysj" type="datetime" placeholder="报告时间" format="YYYY-MM-DD HH:mm:ss"
@blur="handleFieldChange" value-format="YYYY-MM-DD HH:mm:ss" @blur="handleFieldChange"
@keyup.enter="handleFieldChange" @keyup.enter="handleFieldChange"></el-date-picker>
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="采样时间" label-width="70px"> <el-form-item label="临床诊断">
<el-date-picker v-model="labPat.cyrq" type="datetime" placeholder="采样时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" <el-input v-model="labPat.zd" placeholder="请输入临床诊断" @blur="handleFieldChange"
@blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input>
@keyup.enter="handleFieldChange"
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="报告时间" label-width="70px"> <el-form-item label="备 注">
<el-date-picker v-model="labPat.dysj" type="datetime" placeholder="报告时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" <el-input v-model="labPat.bz" placeholder="请输入备注" @blur="handleFieldChange"
@blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input>
@keyup.enter="handleFieldChange"
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="临床诊断" label-width="70px"> <el-form-item label="检验医生">
<el-input v-model="labPat.zd" placeholder="请输入临床诊断" @blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input> <el-input v-model="labPat.yhdh" placeholder="请输入检验医生" @blur="handleFieldChange"
@keyup.enter="handleFieldChange"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="备 注" label-width="70px"> <el-form-item label="审核医生">
<el-input v-model="labPat.bz" placeholder="请输入备注" @blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input> <el-input v-model="labPat.hdys" placeholder="请输入审核医生" @blur="handleFieldChange"
@keyup.enter="handleFieldChange"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="检验医生" label-width="70px"> <el-form-item label="审核状态">
<el-input v-model="labPat.yhdh" placeholder="请输入检验医生" @blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input>
</el-form-item>
<el-form-item label="审核医生" label-width="70px">
<el-input v-model="labPat.hdys" placeholder="请输入审核医生" @blur="handleFieldChange" @keyup.enter="handleFieldChange"></el-input>
</el-form-item>
<el-form-item label="审核状态" >
<el-radio-group v-model="labPat.jgbz"> <el-radio-group v-model="labPat.jgbz">
<el-radio :label="0">未审核</el-radio> <el-radio :label="'0'">未审核</el-radio>
<el-radio :label="2">已审核</el-radio> <el-radio :label="'2'">已审核</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="compact-form">
<el-button type="primary" @click="saveMain">保存</el-button>
<el-button @click="resetMain">重置</el-button>
</div>
</el-col>
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import DictInput from './DictInput.vue'; import { ref, reactive, watch, toRaw, computed, onMounted } from 'vue';
import {changepatcolumn, updateLabPat} from "@/api/liswork/work/LisWork.js"; import { changepatcolumn, updateLabPat } from "@/api/liswork/work/LisWork";
//@ts-ignore
import { comDict } from '@/utils/dict'
const loading = ref(true); const loading = ref(true);
const props = defineProps({ const props = defineProps({
labPat: { labPat: {
@ -135,109 +122,122 @@ const props = defineProps({
required: true required: true
}, },
//结果主键 //结果主键
labPatKey:{ labPatKey: {
type: Array, type: Object,
default: () => [] default: () => { }
}
});
// 计算属性:将布尔值转换为 0/1 绑定到 labPat.jzbz
const jzbzSwitch = computed({
get() {
// 后端返回的 0/1 转换为布尔值(供开关显示)
return props.labPat.jzbz == "1";
}, },
set(checked) { dictData: {
// 开关的 true/false 转换为 1/0 存到实体中 type: Object,
props.labPat.jzbz = checked ? "1" : "0"; default: () => { }
} }
}); });
watch(() => props.labPat, (newVal) => {
lastValue.value = JSON.stringify(newVal);
})
// 计算属性:将布尔值转换为 0/1 绑定到 labPat.jzbz
const jzbzSwitch = ref(0);
const emit = defineEmits(['update:labPat']); const emit = defineEmits(['update:labPat']);
// 通过inject获取全局方法 const lastValue = ref('');
const comDict = inject('comDict');
const lastValue = ref(JSON.stringify(props.labPat));
const prevFieldState = ref(null); // 记录当前修改的字段及旧值 { field: '字段名', oldValue: 旧值 }
/** /**
* 通用字段变化处理(失焦、回车触发) * 通用字段变化处理(失焦、回车触发)
*/ */
const handleFieldChange = async () => { const handleFieldChange = async () => {
await triggerApiUpdate(); // 将当前labPat转为字符串用于比较
const currentValue = JSON.stringify(props.labPat);
// 对比与上一次保存的值是否有变化
if (currentValue !== lastValue.value) {
// 找出变化的字段
const oldObj = JSON.parse(lastValue.value);
const newObj = props.labPat;
const changedField = getChangedField(oldObj, newObj);
console.log('changedField==>', changedField);
console.log('oldObj, newObj==>', oldObj, newObj);
if (changedField) {
// 这里可以添加实际的更新逻辑,比如调用接口
try {
changepatcolumn({ ...props.labPatKey, column: changedField?.field, value: changedField?.newValue }).then((res: any) => {
});
} catch (error) {
console.error('更新失败:', error);
// 失败时可以恢复旧值
emit('update:labPat', oldObj);
lastValue.value = JSON.stringify(oldObj);
}
}
}
}; };
// 完善字段变化检测方法
const getChangedField = (oldObj: { [key: string]: any }, newObj: { [key: string]: any }) => {
const oldKeys = Object.keys(oldObj);
for (const key of oldKeys) {
const oldVal = oldObj[key];
const newVal = newObj[key];
if (oldVal instanceof Date && newVal instanceof Date) {
if (oldVal.getTime() !== newVal.getTime()) {
return { field: key, oldValue: oldVal, newValue: newVal };
}
} else if (oldVal !== newVal) {
return { field: key, oldValue: oldVal, newValue: newVal };
}
}
const newKeys = Object.keys(newObj);
for (const key of newKeys) {
if (!(key in oldObj)) {
return { field: key, oldValue: undefined, newValue: newObj[key] };
}
}
return null;
};
const getLableType = (type: string) => {
switch (type) {
case '1':
return '初审';
case '2':
return '已审核';
case '3':
return '初报';
case '4':
return '二报';
case '5':
return '锁定';
default:
return '';
}
}
const getColor = (type: string) => {
switch (type) {
case '1':
return 'cs_color';
case '2':
return 'sh_color';
case '3':
return 'cb_color';
case '4':
return 'eb_color';
case '5':
return 'sd_color';
default:
return 'default';
}
}
/** /**
* 字典选择完成处理(弹窗选择后触发) * 字典选择完成处理(弹窗选择后触发)
*/ */
const handleDictSelect = async () => { const handleDictSelect = async () => {
// 字典选择后直接触发更新 // 字典选择后直接触发更新
await triggerApiUpdate();
};
/**
* 对比新旧对象,找出修改的字段及旧值
* @param oldObj 旧对象
* @param newObj 新对象
* @returns { field: string, oldValue: any } | null
*/
const getChangedField = (oldObj, newObj) => {
const oldKeys = Object.keys(oldObj);
for (const key of oldKeys) {
if (newObj[key] !== oldObj[key]) {
return { field: key, oldValue: oldObj[key], newValue: newObj[key] };
}
}
return null;
};
/**
* 触发API更新的核心方法
*/
const triggerApiUpdate = async () => {
const currentLabPat = toRaw(props.labPat);
const currentValue = JSON.stringify(currentLabPat);
const oldValue = lastValue.value;
// 只有值发生变化时才调用API
if (currentValue !== lastValue.value) {
// 2. 找出修改的字段及旧值(用于回滚)
const oldObj = JSON.parse(oldValue);
const changedField = getChangedField(oldObj, currentLabPat);
prevFieldState.value = changedField; // 记录修改前的状态
console.log('prevFieldState:', prevFieldState.value)
console.log('labPatKey:', props.labPatKey)
try {
loading.value = true;
changepatcolumn({...props.labPatKey,column:changedField?.field,value:changedField?.newValue}).then(response => {
console.log('response:', response)
console.log('表单更新成功');
lastValue.value = currentValue;
loading.value = false;
}) .catch(error => {
console.error('表单更新失败', error);
loading.value = false;
if (prevFieldState.value) {
const { field, oldValue,newValue } = prevFieldState.value;
// 回滚字段值(通过emit触发父组件更新,避免直接修改props)
emit('update:labPat', {
...toRaw(props.labPat),
[field]: oldValue
});
} else {
// 若未找到具体字段,整体回滚
emit('update:labPat', JSON.parse(oldValue));
}
// 回滚后更新缓存
lastValue.value = oldValue;
}); };
} catch (error) {
console.error('更新接口调用失败:', error);
}finally {
loading.value = false;
prevFieldState.value = null; // 清空临时记录
}
}
};
// 手动保存
const saveMain = async () => {
await triggerApiUpdate();
};
// 重置方法 // 重置方法
const resetMain = () => { const resetMain = () => {
const resetData = { const resetData = {
@ -257,20 +257,104 @@ const resetMain = () => {
lastValue.value = JSON.stringify(resetData); lastValue.value = JSON.stringify(resetData);
}; };
// 字典获取方法(适配子组件) // interface DictData {
const fetchDict = async (dictType) => { // PT?: Array<any>;
const dictRefs = await comDict(dictType); // AU?: Array<any>;
// 根据实际字典结构返回数组(需与子组件匹配) // SX?: Array<any>;
return dictRefs[dictType]?.value || []; // DP?: Array<any>;
}; // BT?: Array<any>;
// SRD?: Array<any>;
// [key: string]: any[] | undefined; // 添加索引签名以支持动态访问
// }
// // 字典数据存储
// const dictData = ref<DictData>({});
// onMounted(async () => {
// // 加载病人来源字典
// const dictRefs = await comDict('PT', 'AU', 'SX', 'DP', 'BT', 'SRD');
// // 从 ref 中获取实际数据
// dictData.value = {
// PT: toRaw(dictRefs.PT.value) || [],
// AU: toRaw(dictRefs.AU.value) || [],
// SX: toRaw(dictRefs.SX.value) || [],
// DP: toRaw(dictRefs.DP.value) || [],
// BT: toRaw(dictRefs.BT.value) || [],
// SRD: toRaw(dictRefs.SRD.value) || [],
// };
// });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.compact-form .el-form-item { .labpat_box {
margin-bottom: 10px; height: calc(90vh - 95px);
display: flex;
flex-direction: column;
overflow: hidden;
} }
.compact-form .el-form-item__label { .flex-between {
height: 32px;
}
.compact-form {
flex: 1;
position: relative;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.el-form-item {
margin-bottom: 5px;
}
.el-form-item__label {
padding-bottom: 5px; padding-bottom: 5px;
}
// pointer-events: none;
}
.sh_box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0);
z-index: 9;
display: flex;
justify-content: center;
align-items: center;
writing-mode: vertical-rl;
font-weight: 500;
font-size: 137px;
// pointer-events: none;
}
.cs_color {
color: rgba(40, 189, 187, 0.44);
}
.sh_color {
color: rgba(238, 8, 8, 0.44);
}
.cb_color {
color: rgba(255, 165, 0, 0.44);
}
.eb_color {
color: rgba(0, 128, 0, 0.44);
}
.sd_color {
color: rgba(128, 128, 128, 0.44);
}
.default {
color: rgba(40, 189, 187, 0.44);
} }
</style> </style>

View File

@ -1,94 +1,68 @@
<template> <template>
<div class="table-container"> <div class="table-container">
<el-input <vxe-table :data="labPatList" :loading="loading" border height="auto" class="mytable-style"
v-model="searchText" :row-config="{ isHover: true, height: 30 }" show-overflow @current-change="handleRowClick" ref="tableRef"
placeholder="输入条码号搜索" :current-row="currentRow" highlight-current-row round row-id="ybh">
class="mb-4" <vxe-column field="finish" title="完成" width="60" align="center">
@keyup.enter="search"
>
<template #append>
<el-button @click="search">搜索</el-button>
</template>
</el-input>
<el-table
:data="labPatList"
stripe
class="card-content"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
@row-click="handleRowClick"
style="width: 100%"
border
>
<el-table-column prop="finish" label="完成" width="70">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.finish === 1 ? '已完成' : '未完成' }}</span> <span>{{ scope.row.finish == 1 ? '✅' : '❌' }}</span>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="jgbz" label="审核" width="25" align="center" header-align="center" > <vxe-column field="jgbz" title="审核" width="60" align="center">
<template #default="scope"> <template #default="scope">
<el-checkbox :model-value="scope.row.jgbz === '2'" disabled> <el-checkbox v-model="scope.row.jgbz" true-value="2" disabled />
</el-checkbox>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="alarmflag" label="报警" width="25"> <vxe-column field="alarmflag" title="报警" width="60" align="center">
<template #default="scope"> <template #default="scope">
<el-checkbox :model-value="scope.row.alarmflag === '1'" disabled> <el-checkbox :model-value="scope.row.alarmflag == '1'" disabled />
</el-checkbox>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="brly" label="类型" width="50" show-overflow-tooltip></el-table-column> <vxe-column field="brly" title="类型" width="60" align="center">
<el-table-column prop="autojgbz" label="自审" width="25" > <template #default="scope">
{{ formatDict(scope.row.brly, 'PT') }}
</template>
</vxe-column>
<vxe-column field="autojgbz" title="自审" width="60" align="center">
<template #default="scope"> <template #default="scope">
<el-checkbox :model-value="scope.row.autojgbz === '1'" disabled> <el-checkbox :model-value="scope.row.autojgbz === '1'" disabled>
</el-checkbox> </el-checkbox>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="ybh" label="样本号" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="ybh" title="样本号" width="120" align="center" />
<el-table-column prop="brxm" label="病人姓名" width="100" show-overflow-tooltip></el-table-column> <vxe-column field="brxm" title="病人姓名" width="120" align="center" />
<el-table-column prop="brdh" label="病历号" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="brdh" title="病历号" width="120" align="center" />
<el-table-column prop="ch" label="床号" width="80" show-overflow-tooltip ></el-table-column> <vxe-column field="ch" title="床号" width="60" align="center" />
<el-table-column prop="brxb" label="病人性别" width="80" show-overflow-tooltip ></el-table-column> <vxe-column field="brxb" title="病人性别" width="100" align="center" />
<el-table-column prop="nl" label="年" width="40" show-overflow-tooltip ></el-table-column> <vxe-column field="nl" title="年" width="60" align="center" />
<el-table-column prop="nldw" label="龄" width="30" show-overflow-tooltip ></el-table-column> <vxe-column field="nldw" title="龄" width="60" align="center" />
<el-table-column prop="ksdh" label="科室" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="ksdh" title="科室" width="60" align="center" />
<el-table-column prop="yblx" label="标本" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="yblx" title="标本" width="60" align="center" />
<el-table-column prop="dybz" label="打印" width="25" show-overflow-tooltip > <vxe-column field="dybz" title="打印" width="60" show-overflow align="center">
<template #default="scope"> <template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled> <el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox> </el-checkbox>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="fslx" label="发送" width="25"> <vxe-column field="fslx" title="发送" width="60" align="center">
<template #default="scope"> <template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled> <el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox> </el-checkbox>
</template> </template>
</el-table-column> </vxe-column>
<el-table-column prop="shcs" label="次数" width="25" show-overflow-tooltip ></el-table-column> <vxe-column field="shcs" title="次数" show-overflow width="60" align="center" />
<el-table-column prop="sqh" label="申请号/条码" width="100" show-overflow-tooltip></el-table-column> <vxe-column field="sqh" title="申请号/条码" width="150" show-overflow align="center" />
<el-table-column prop="jymd" label="检验目的" width="100" show-overflow-tooltip></el-table-column> <vxe-column field="jymd" title="检验目的" show-overflow width="120" align="center" />
<el-table-column prop="yhdh" label="检验医生" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="yhdh" title="检验医生" show-overflow width="120" align="center" />
<el-table-column prop="yljg" label="送检医院" width="80" show-overflow-tooltip></el-table-column> <vxe-column field="yljg" title="送检医院" show-overflow width="120" align="center" />
<el-table-column prop="jyrq" label="检验日期" v-if="false"></el-table-column>
<el-table-column prop="yq" label="仪器" v-if="false"></el-table-column> </vxe-table>
</el-table>
<el-pagination
class="mt-4"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-sizes="[5, 10, 15, 20]"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
const props = defineProps({ const props = defineProps({
labPatList: { labPatList: {
type: Array, type: Array,
@ -97,110 +71,74 @@ const props = defineProps({
total: { total: {
type: Number, type: Number,
default: 0 default: 0
},
loading: {
type: Boolean,
default: true
},
dictData: {
type: Object,
default: () => { }
} }
}) })
const emits = defineEmits(['select', 'search', 'page-change']) const tableRef = ref<any>();
const emits = defineEmits(['select', 'search',])
const searchText = ref('') const currentRow = ref<any>(null);
const pageSize = ref(15) const handleRowClick = ({ row }: { row: any }) => {
const currentPage = ref(1)
const search = () => {
emits('search', searchText.value, currentPage.value, pageSize.value)
}
const handleRowClick = (row) => {
emits('select', row) emits('select', row)
} }
const setCurrentRow = (row: any) => {
tableRef.value.setCurrentRow(row)
const handleSizeChange = (val) => { nextTick(() => {
pageSize.value = val tableRef.value.scrollToRow(row)
emits('page-change', currentPage.value, pageSize.value) })
} }
const handleCurrentChange = (val) => {
currentPage.value = val
emits('page-change', currentPage.value, pageSize.value)
}
onMounted(() => {
emits('page-change', currentPage.value, pageSize.value) // 字典格式化方法
}) const formatDict = (v: string, dictType: string) => {
// 设置列头颜色 return props.dictData[dictType]?.find((item: any) => item.value == v)?.label;
const headerCellStyle = ({ row, column, rowIndex, columnIndex }) => {
return {
// backgroundColor: '#A6CAF0', // 浅蓝色
color: '#0050b3', // 文字颜色
fontWeight: 'bold' // 文字加粗
};
}; };
//设置内容格式
const cellStyle = ({ row, column, rowIndex, columnIndex }) => {
// console.log('检查单元格样式:', row, column); // 调试输出
// 使用 === 比较时注意值的类型(数字 vs 字符串)
if (column.property === 'finish' && row.status === '1') {
return {
backgroundColor: '#006400', // 使用驼峰式写法(可选)
fontWeight: 'bold',
color: 'white', // 添加文字颜色,确保可读性
height: '25px',
lineHeight: '25px'
};
}
if (column.property === 'jgbz' && row.status === '2') { // 导出方法
return { defineExpose({
backgroundColor: '#FF69B4', setCurrentRow,
fontWeight: 'bold', tableRef,
height: '25px', });
lineHeight: '25px'
};
}
if (column.property === 'alarmflag' && row.status === '1') {
return {
backgroundColor: '#FF0000',
fontWeight: 'bold',
color: 'white',
height: '25px',
lineHeight: '25px'
};
}
return {
height: '25px',
lineHeight: '25px',
padding: '0 0px'
}; // 默认不设置样式
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.compact-form .el-form-item { .table-container {
padding: 10px; height: calc(90vh - 250px);
margin-bottom: 10px; display: flex;
} flex-direction: column;
.compact-form .el-form-item__label {
padding-bottom: 5px;
}
.card-content {
max-height: 620px; /* 设置最大高度 */
overflow-y: auto; /* 超出高度时显示垂直滚动条 */
padding: 10px; /* 保持与卡片默认一致的内边距 */
}
.clickable:hover {
cursor: pointer; /* 鼠标悬停时显示手型 */
transition: all 0.3s; /* 平滑过渡效果 */
color: blue;
}
.card-content.el-table__row.current-row {
background-color: #e6f4ff !important;
color: #0050b3 !important; /* 文字颜色 */
} }
.card-content.el-table__row.current-row:hover { .vxe-table--border tbody tr,
background-color: #d6e8ff !important; .vxe-table--border td,
.vxe-table--border th {
border-color: #f00 !important;
/* 设置边框颜色为红色 */
}
/* 2. 表头区域边框 */
::v-deep .vxe-header--row th {
border-color: #1F6DD3 !important;
}
/* 3. 内容区域边框 */
::v-deep .vxe-body--row td {
border-color: #409eff !important;
}
.mytable-style {
flex: 1;
} }
</style> </style>

View File

@ -0,0 +1,193 @@
<template>
<div class="table-container">
<el-table :data="labPatList" class="card-content" :cell-style="cellStyle" @row-click="handleRowClick" border
height="100%" highlight-current-row ref="tableRef" v-loading="loading" :row-style="{ height: '30px' }"
:row-key="(row: any) => row.ybh">
<el-table-column prop="finish" label="完成" align="center" width="60px">
<template #default="scope">
<span>{{ scope.row.finish === 1 ? '✅' : '❌' }}</span>
</template>
</el-table-column>
<el-table-column prop="jgbz" label="审核" align="center" width="60px">
<template #default="scope">
<el-checkbox v-model="scope.row.jgbz" true-value="2" disabled />
</template>
</el-table-column>
<el-table-column prop="alarmflag" label="报警" align="center" width="60px">
<template #default="scope">
<el-checkbox :model-value="scope.row.alarmflag == '1'" disabled />
</template>
</el-table-column>
<el-table-column prop="brly" label="类型" align="center" width="60px">
<template #default="scope">
{{ formatDict(scope.row.brly, 'PT') }}
</template>
</el-table-column>
<el-table-column prop="autojgbz" label="自审" align="center" width="60px">
<template #default="scope">
<el-checkbox :model-value="scope.row.autojgbz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="ybh" label="样本号" align="center" />
<el-table-column prop="brxm" label="病人姓名" align="center" />
<el-table-column prop="brdh" label="病历号" align="center" width="120" />
<el-table-column prop="ch" label="床号" align="center" />
<el-table-column prop="brxb" label="病人性别" align="center" />
<el-table-column prop="nl" label="年" align="center" />
<el-table-column prop="nldw" label="龄" align="center" />
<el-table-column prop="ksdh" label="科室" align="center" />
<el-table-column prop="yblx" label="标本" align="center" />
<el-table-column prop="dybz" label="打印" align="center" show-overflow-tooltip>
<template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="fslx" label="发送" align="center">
<template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="shcs" label="次数" align="center" show-overflow-tooltip />
<el-table-column prop="sqh" label="申请号/条码" width="150" show-overflow-tooltip />
<el-table-column prop="jymd" label="检验目的" show-overflow-tooltip align="center" width="120px" />
<el-table-column prop="yhdh" label="检验医生" align="center" show-overflow-tooltip />
<el-table-column prop="yljg" label="送检医院" align="center" show-overflow-tooltip />
</el-table>
<el-pagination class="mt10 page-box" background layout="total, sizes, prev, pager, next, jumper" :total="total"
:page-sizes="[10, 20, 30, 50]" v-model:current-page="currentPage" v-model:page-size="pageSize"
@size-change="handleSizeChange" @current-change="handleCurrentChange">
</el-pagination>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
const props = defineProps({
labPatList: {
type: Array,
default: () => []
},
total: {
type: Number,
default: 0
},
loading: {
type: Boolean,
default: true
},
dictData: {
type: Object,
default: () => { }
}
})
const tableRef = ref()
const emits = defineEmits(['select', 'search', 'page-change'])
const pageSize = ref(50)
const currentPage = ref(1)
const searchText = ref('')
const setCurrentRow = (index: number, row: any) => {
tableRef.value.setCurrentRow(row)
nextTick(() => {
tableRef.value.setScrollTop(32 * index)
})
}
const doLayout = () => {
tableRef.value?.doLayout();
};
const handleSizeChange = (val: number) => {
pageSize.value = val
emits('page-change', currentPage.value, pageSize.value)
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
emits('page-change', currentPage.value, pageSize.value)
}
// 字典格式化方法
const formatDict = (v: string, dictType: string) => {
return props.dictData[dictType]?.find((item: any) => item.value == v)?.label;
};
// 导出方法
defineExpose({
setCurrentRow,
doLayout,
tableRef,
});
const search = () => {
// emits('search', searchText.value, currentPage.value, pageSize.value)
}
const handleRowClick = (row: any) => {
emits('select', row)
}
onMounted(() => {
})
//设置内容格式
const cellStyle = ({ row, column }: { row: any, column: any }) => {
// console.log('检查单元格样式:', row, column); // 调试输出
// 使用 === 比较时注意值的类型(数字 vs 字符串)
if (column.property === 'finish' && row.status === '1') {
return {
backgroundColor: '#006400', // 使用驼峰式写法(可选)
fontWeight: 'bold',
color: 'white', // 添加文字颜色,确保可读性
height: '25px',
lineHeight: '25px'
};
}
if (column.property === 'jgbz' && row.status === '2') {
return {
backgroundColor: '#FF69B4',
fontWeight: 'bold',
height: '25px',
lineHeight: '25px'
};
}
if (column.property === 'alarmflag' && row.status === '1') {
return {
backgroundColor: '#FF0000',
fontWeight: 'bold',
color: 'white',
height: '25px',
lineHeight: '25px'
};
}
return {
height: '25px',
lineHeight: '25px',
padding: '0 0px'
}; // 默认不设置样式
};
</script>
<style scoped lang="scss">
.table-container {
height: calc(90vh - 250px);
display: flex;
flex-direction: column;
}
.card-content {
flex: 1;
}
.page-box {
height: 32px;
}
</style>

View File

@ -1,84 +1,429 @@
<template> <template>
<div class="compact-form"> <div class="result-container">
<el-table <div class="flex-between mb10">
:data="tableData" <div>
border <el-button class="btn_green" @click="handleCheck(1)">初审</el-button>
stripe <el-button class="btn_green" v-if="labPat.jgbz != 2" @click="handleCheck(2)">审核</el-button>
highlight-current-row <el-button class="btn_green" v-if="labPat.jgbz == 2" @click="handleCheck(3)">取消审核</el-button>
@selection-change="handleSelectionChange" <el-button type="primary" plain @click="openItemDictDialog">新增</el-button>
class="custom-table-container" <el-button type="primary" plain @click="handleBatchDelete">删除</el-button>
height="640px" </div>
style="width: 100%; --table-row-height: 32px;" <div>
:header-cell-style="{ backgroundColor: '#f5f7fa' }" <el-button type="primary" plain icon="Files" @click="printHandle">打印</el-button>
> <el-button type="primary" plain icon="View" @click="previewHandle">预览</el-button>
<el-table-column </div>
type="index" </div>
prop="id" <el-table :data="tableData" border ref="tableRef" highlight-current-row class="custom-table-container" height="100%"
label="" @row-click="handleRowClick">
width="45" <el-table-column type="index" prop="id" label="" width="45" :index="getRowIndex"></el-table-column>
:index="getRowIndex" <!-- <el-table-column type="selection" width="40" align="center"></el-table-column> -->
></el-table-column> <el-table-column prop="xmdh" label="项目" show-overflow-tooltip>
<el-table-column type="selection" width="30"></el-table-column>
<el-table-column prop="jyrq" label="检验日期" v-if="false"></el-table-column>
<el-table-column prop="yq" label="仪器" v-if="false"></el-table-column>
<el-table-column prop="ybh" label="样本号" v-if="false"></el-table-column>
<el-table-column prop="xmdh" label="项目" width="60" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="xmmc" label="名称" width="100" show-overflow-tooltip> <el-table-column prop="xmmc" label="名称" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="csjg" label="结果" width="100" > <el-table-column prop="csjg" label="结果">
<template #default="scope"> <template #default="scope">
<el-input <el-input v-model="scope.row.csjg" size="small" class="full-width-input" :disabled="readonly"
v-model="scope.row.csjg" @blur="handleCsjgEnter(scope.row)" @keyup.enter="handleCsjgEnter(scope.row)"></el-input>
size="small"
class="full-width-input"
:disabled="readonly"
@blur="handleCsjgBlur(scope.row)"
@keyup.enter="handleCsjgEnter(scope.row)"
></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="od" label="OD" width="60" v-if="false"> <el-table-column prop="refs" label="参考值" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.od" size="small" class="full-width-input"></el-input>
</template>
</el-table-column> </el-table-column>
<el-table-column prop="cutoff" label="SCO" width="60" v-if="false"> <el-table-column prop="dw" label="单位" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.cutoff" size="small" class="full-width-input"></el-input>
</template>
</el-table-column>
<el-table-column prop="refs" label="参考值" width="100" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="dw" label="单位" width="100" show-overflow-tooltip >
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- <div class="flex-between mt10">
<div>
<el-button class="btn_green" @click=" ">初报</el-button>
<el-button class="btn_green" @click=" ">二报</el-button>
<el-button class="btn_green" @click=" ">锁定</el-button>
</div>
<div>
</div>
</div> -->
<!-- 新增明细 -->
<ItemInput ref="itemDictRef" v-model="selectedItemValue" :label-value.sync="selectedItemLabel"
:dict-type="'LAB_ITEM'" :fetch-dict="fetchLabItemDict" :dialog-title="'选择检验项目'" :disabled="false"
:load-on-mount="false" :table-data.sync="labResuts" class="hidden-dict-input" @select="handleItemSelect" />
<!-- 校验用户 -->
<el-dialog v-model="checkShow" title="用户身份验证" width="500" :close-on-click-modal="false" :draggable="true"
@close="resetForm">
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto">
<el-form-item label="用户工号:" prop="yhdh">
<el-input v-model.trim="ruleForm.yhdh" placeholder="请输入用户工号" maxlength="20" @keyup.enter="submitForm" />
</el-form-item>
<el-form-item label="密码:" prop="mm">
<el-input v-model.trim="ruleForm.mm" placeholder="请输入密码" type="mm" maxlength="20" show-password
@keyup.enter="submitForm" />
</el-form-item>
<el-form-item>
<div style="text-align: center; width: 100%;">
<el-button type="primary" @click="submitForm()"> 确认 </el-button>
<el-button @click="resetForm">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
<!-- 危急值结果上报 -->
<el-dialog v-model="showuploadwarning" title="危急值结果上报" width="500" :close-on-click-modal="false" :draggable="true"
@close="warningResetForm">
<div class="red-static-text">{{ warningMsgdata }}</div>
<el-form ref="warningFormRef" style="max-width: 600px" :model="warningForm" :rules="wrules" label-width="auto">
<el-form-item label="上报方式:" prop="uploadTp">
<el-radio-group v-model="warningForm.uploadTp" size="default">
<el-radio value="1">系统通知</el-radio>
<el-radio value="2">电话通知</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="warningForm.uploadTp == 2">
<el-form-item label="电话通知人:" prop="userId">
<el-input v-model.trim="warningForm.userId" class="form-input" placeholder="请输入医生工号" @keyup.enter=" " />
</el-form-item>
<el-form-item label="联系电话:" prop="phone">
<el-input v-model="warningForm.phone" class="form-input" placeholder="请输入联系电话" @keyup.enter=" " />
</el-form-item>
</template>
<el-form-item>
<div style="text-align: center; width: 100%;">
<el-button type="primary" @click="handleWarningSuccess()"> 确认 </el-button>
<el-button @click="warningResetForm">取消</el-button>
</div>
</el-form-item>
</el-form>
</el-dialog>
<!-- 操作原因 -->
<el-dialog v-model="unCheckshow" title="操作原因" width="500" :close-on-click-modal="false" :draggable="true"
@close="unconfirmlogCancel">
<div class="dialog-body mb10">
<p class="reason-text mb5">{{ reasonText }}</p>
<el-form ref="uncheckFormRef" style="max-width: 600px" :model="uncheckForm" :rules="rules" label-width="auto">
<template v-if="tableKey.problemId == 4">
<el-input type="textarea" v-model="uncheckForm.reason" class="reason-input" placeholder="请输入操作原因..."
:rows="4"></el-input>
</template>
<template v-if="tableKey.problemId == 6">
<el-form-item label="用户工号:" prop="yhdh">
<el-input v-model.trim="uncheckForm.yhdh" placeholder="请输入用户工号" maxlength="20"
@keyup.enter="submitForm" />
</el-form-item>
<el-form-item label="密码:" prop="mm">
<el-input v-model.trim="uncheckForm.mm" placeholder="请输入密码" type="mm" maxlength="20" show-password
@keyup.enter="submitForm" />
</el-form-item>
</template>
</el-form>
</div>
<div style="text-align: center; width: 100%;">
<el-button type="primary" @click="unconfirmlogConfirm()"> 确认 </el-button>
<el-button @click="unconfirmlogCancel">取消</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, toRefs, watch, nextTick, onMounted } from "vue"; import { ref, toRefs, watch, nextTick, onMounted } from "vue";
import { deleteresult, changeresult } from "@/api/liswork/work/LisWork.js"; import { check1, check2, uncheck2, unconfirmlog, checkuser, reglimit, queryXmInfo, deleteresult, changeresult, newresult, printsample } from "@/api/liswork/work/LisWork";
import { ElMessageBox, ElMessage } from "element-plus"; import { ElMessageBox, ElMessage } from "element-plus";
import ItemInput from "@/views/liswork/work/components/ItemInput.vue";
import { log } from "vxe-pc-ui";
// 组件属性定义 // 组件属性定义
const props = defineProps({ const props = defineProps({
tableData: { type: Array, default: () => [] }, tableData: { type: Array, default: () => [] },
tableKey: { type: Array, default: () => [] }, tableKey: { type: Object, default: () => { } },
readonly: { type: Boolean, default: false }, readonly: { type: Boolean, default: false },
hasChanges: { type: Boolean, default: false } hasChanges: { type: Boolean, default: false },
labPat: { type: Object, required: true },
}); });
watch(() => props.tableData, (newVal) => {
selectedRows.value = [];
});
const warningForm = ref({
userId: '',
phone: '',
uploadTp: '1'
})
const uncheckForm = ref({
reason: '',
yhdh: '',
mm: '',
})
const wrules = ref({
userId: [{ required: true, message: "请输入用户工号", trigger: "blur" },
{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" }
],
phone: [{ required: true, message: "请输入联系电话", trigger: "blur" },
{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }
]
})
const unCheckshow = ref(false);
const inputValue = ref('');
const reasonText = ref('');
const warningMsgdata = ref('');
const showuploadwarning = ref(false);
const checkuserid = ref()
const checkShow = ref(false);
const ruleForm = ref({
yhdh: '',
mm: ''
});
const rules = ref(
{
yhdh: [{ required: true, message: "请输入用户工号", trigger: "blur" },
{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" }
],
mm: [{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }
]
}
);
const itemDictRef = ref(); // DictInput组件引用
const selectedItemValue = ref(""); // 选择的项目编码xmdh
const selectedItemLabel = ref(""); // 选择的项目名称xmmc
const labResuts = ref([])
const tableRef = ref();
const fetchLabItemDict = async (dictType) => {
// 日志1:确认方法被调用
try {
// 1. 验证必要参数(如当前仪器yq)
if (!tableKey.value.yq) {
console.error("[fetchLabItemDict] 缺少必要参数:yq(仪器)");
ElMessage.error("请先选择仪器");
return [];
}
// 日志2:打印请求参数
const requestParams = {
pageNum: 1,
pageSize: 1000, // 加载足够多的项目
yq: tableKey.value.yq, // 当前选中的仪器
};
// 2. 调用API(确保queryXmInfo是正确的API方法)
const response = await queryXmInfo(requestParams);
// 日志3:打印原始响应
// 3. 验证响应格式
if (!response) {
throw new Error("API返回为空");
}
if (response.code !== "0") { // 假设0为成功状态码
throw new Error(`API返回错误:${response.msg || "未知错误"}`);
}
if (!Array.isArray(response.data)) {
throw new Error(`API返回data不是数组,实际为:${typeof response.data}`);
}
// 4. 格式化数据(适配DictInput的{value, label}结构)
const formattedData = response.data.map((item, index) => {
// 验证项目数据完整性
if (!item.xmdh) {
console.warn(`[fetchLabItemDict] 第${index}条数据缺少xmdh(项目编码):`, item);
}
if (!item.xmmc) {
console.warn(`[fetchLabItemDict] 第${index}条数据缺少xmmc(项目名称):`, item);
}
return {
value: item.xmdh || `未知编码_${index}`, // 确保value存在
label: item.xmmc || `未知名称_${index}`, // 确保label存在
dw: item.dw || "", // 单位(可选)
refs: item.refs || "", // 参考值(可选)
};
});
// 日志4:打印格式化后的数据量
return formattedData;
} catch (error) {
// 日志5:捕获并显示错误
console.error(`[fetchLabItemDict] 加载失败:`, error.message);
ElMessage.error(`项目字典加载失败:${error.message}`);
return []; // 失败时返回空数组,避免弹窗报错
}
};
const handleCheck = (checkType) => {
// yhdh = checkuserid.value
switch (checkType) {
case 1: // 初审
check1({ ...tableKey.value, yhdh: 'admin' }).then(res => {
emits("changeStatus", props.labPat);
})
break;
case 2: // 审核
tableKey.value.problemId = 0
shHandleCheck()
break;
case 3: // 取消审核
tableKey.value.problemId = 0
unShHandle()
break;
default:
break;
}
};
const shHandleCheck = () => {
check2({ ...props.tableKey, yhdh: 'admin' }).then(res => {
emits("changeStatus", props.labPat);
if (res.code == "4" && res.problemId === 4) {
tableKey.value.problemId = res.problemId;
warningMsgdata.value = res.msg;
showuploadwarning.value = true;
}
})
};
const warningFormRef = ref(null);
const warningResetForm = () => {
warningForm.value.userId = '';
warningForm.value.phone = '';
warningForm.value.uploadTp = '1';
warningFormRef.value.resetFields();
showuploadwarning.value = false;
};
// 处理危急值登记
const handleWarningSuccess = async () => {
if (warningForm.value.uploadTp == 2) {
const valid = await warningFormRef.value.validate().catch(() => { });
if (!valid) return false;
}
reglimit({ ...tableKey.value, qrr: warningForm.value.userId, tzfs: warningForm.value.uploadTp, phone: warningForm.value.phone }).then(response => {
showuploadwarning.value = false;
if (response.code == 0) {
shHandleCheck();
}
if (response.code == 4) {
ElMessageBox.confirm(response.msg, '系统提示', { confirmButtonText: '是', cancelButtonText: '否', type: 'warning' }).then(() => {
tableKey.value.problemId = tableKey.value.problemId + 1;
handleWarningSuccess();
}).catch(() => {
shHandleCheck();
});
}
})
};
const unShHandle = () => {
uncheck2({ ...tableKey.value, yhdh: 'admin' }).then(res => {
if (res.code == "4") {
tableKey.value.problemId = res.problemId;
reasonText.value = res.msg;
unCheckshow.value = true;
}
emits("changeStatus", props.labPat);
})
};
const uncheckFormRef = ref();
// 取消审核原因
const unconfirmlogConfirm = async () => {
if (tableKey.value.problemId == 4) {
if (!uncheckForm.value.inputValue) return ElMessage.warning("请输入操作原因")
} else if (tableKey.value.problemId == 6) {
const valid = await uncheckFormRef.value.validate().catch(() => { });
if (!valid) return false;
}
tableKey.value.problemId = 0
unconfirmlog({ ...tableKey.value, ...uncheckForm.value }).then(response => {
if (response.code == "0") {
unCheckshow.value = false;
unShHandle();
}
})
};
const unconfirmlogCancel = () => {
unCheckshow.value = false;
uncheckForm.value.reason = '';
uncheckForm.value.yhdh = '';
uncheckForm.value.mm = '';
uncheckFormRef.value.resetFields();
}
const ruleFormRef = ref(null);
const submitForm = () => {
ruleFormRef.value.validate((valid) => {
if (valid) {
checkuser(ruleForm.value).then(res => {
if (res.code == 0) {
checkuserid.value = ruleForm.value.yhdh
checkShow.value = false;
ElMessage.success("验证成功");
}
})
} else {
return false;
}
});
}
const resetForm = () => {
checkShow.value = false;
ruleForm.value.userId = '';
ruleForm.value.mm = '';
ruleFormRef.value.resetFields();
};
// 2. 打开项目选择弹窗
const openItemDictDialog = () => {
if (itemDictRef.value && typeof itemDictRef.value.openDictSelector === 'function') {
itemDictRef.value.openDictSelector(); // 确保方法存在再调用
} else {
console.warn("DictInput组件未加载完成或方法未暴露");
}
};
// 3. 处理项目选择结果:传递给labresult子组件新增行
const handleItemSelect = async (selectedItem) => {
// 1. 构造新增行数据
const newRow = {
id: `temp_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
xmdh: selectedItem.value,
xmmc: selectedItem.label,
dw: selectedItem.dw || "",
refs: selectedItem.refs || "",
jyrq: tableKey.value.jyrq,
yq: tableKey.value.yq,
ybh: tableKey.value.ybh,
};
addRowFromDict(newRow);
// ElMessage.success(`已新增项目:${newRow.xmmc}`);
};
// 事件定义 // 事件定义
const emits = defineEmits([ const emits = defineEmits([
'add',
'save',
'delete',
'batch-delete',
'fetchLabResults', 'fetchLabResults',
'delfalg', 'changeStatus',
'update:tableData'
]); ]);
// 解构props // 解构props
@ -117,93 +462,26 @@ const getRowUniqueKey = (row) => {
return row.id || row.xmdh; return row.id || row.xmdh;
}; };
/**
* csjg输入框失焦触发保存
*/
const handleCsjgBlur = async (row) => {
if (isEnterHandled.value) return;
await saveCsjgChange(row);
};
/** /**
* csjg输入框回车触发保存 * csjg输入框回车触发保存
*/ */
const handleCsjgEnter = async (row) => { const handleCsjgEnter = async (row) => {
isEnterHandled.value = true;
await saveCsjgChange(row); await saveCsjgChange(row);
nextTick(() => { // nextTick(() => {
const input = document.activeElement; // const input = document.activeElement;
if (input.tagName === "INPUT") input.blur(); // if (input.tagName === "INPUT") input.blur();
}); // });
setTimeout(() => {
isEnterHandled.value = false;
}, 100);
}; };
/**
* 核心:保存csjg修改(调用API + 失败回滚)
*/
const saveCsjgChange = async (row) => {
const uniqueKey = getRowUniqueKey(row);
// 防止重复提交
if (isSaving.value[uniqueKey]) return;
const oldCsjg = originalCsjgMap.value[uniqueKey] || "";
let newCsjg = row.csjg || "";
// 标准化处理
newCsjg = String(newCsjg).trim();
// 调试日志
console.log(`
行${uniqueKey} 比较:
缓存旧值:"${oldCsjg}"(类型:${typeof oldCsjg})
当前新值:"${newCsjg}"(类型:${typeof newCsjg})
是否相等:${oldCsjg === newCsjg}
`);
// 无变化或只读状态,直接返回
if (oldCsjg === newCsjg || readonly.value) {
console.log("值未变化,无需保存");
return;
}
try {
// 设置提交中标记
isSaving.value[uniqueKey] = true;
const requestParams = {
...row,
csjg: newCsjg
};
console.log('提交参数:', requestParams);
changeresult(requestParams).then(response => {
originalCsjgMap.value[uniqueKey] = newCsjg;
emits("update:hasChanges", true);
ElMessage.success("结果保存成功");
}) .catch(error => {
ElMessage.error(error.message || "保存接口调用失败,已恢复原结果");
row.csjg = oldCsjg;
});
} catch (error) {
console.error('保存失败:', error);
ElMessage.error(error.message || "保存接口调用失败,已恢复原结果");
// 回滚值
row.csjg = oldCsjg;
} finally {
// 清除提交中标记
isSaving.value[uniqueKey] = false;
}
};
/** /**
* 处理选择变化 * 处理选择变化
*/ */
const handleSelectionChange = (val) => { const handleRowClick = (row) => {
selectedRows.value = val; selectedRows.value = [row];
emits('delfalg', val.length === 0);
}; };
/** /**
@ -259,11 +537,10 @@ const addRowFromDict = async (rowData) => {
const newRow = { ...rowData, csjg: "" }; const newRow = { ...rowData, csjg: "" };
// 4. 添加到表格数据 // 4. 添加到表格数据
tableData.value.push(newRow); tableData.value.unshift(newRow);
console.log("子组件新增行:", newRow); console.log("子组件新增行:", newRow);
// 3. emit同步给父组件(更新父组件的labResuts)
emit('update:tableData', [...tableData.value]);
// 4. 同步缓存 // 4. 同步缓存
await nextTick(); await nextTick();
updateOriginalCsjgMap(); updateOriginalCsjgMap();
@ -271,13 +548,83 @@ const addRowFromDict = async (rowData) => {
// 5. 自动聚焦 // 5. 自动聚焦
nextTick(() => { nextTick(() => {
const lastInput = document.querySelector(`.el-table__row:last-child .csjg-input .el-input__inner`); selectedRows.value = [newRow];
if (lastInput) lastInput.focus(); tableRef.value.setCurrentRow(newRow);
nextTick(() => {
tableRef.value.setScrollTop(30 * tableRef.value.length)
})
// const lastInput = document.querySelector(`.el-table__row:last-child .csjg-input .el-input__inner`);
// if (lastInput) lastInput.focus();
}); });
console.log(`成功新增项目行:`, newRow); console.log(`成功新增项目行:`, newRow);
return true; return true;
}; };
/**
* 核心:保存csjg修改(调用API + 失败回滚)
*/
const saveCsjgChange = async (row) => {
const uniqueKey = getRowUniqueKey(row);
// 防止重复提交
// if (isSaving.value[uniqueKey]) return;
const oldCsjg = originalCsjgMap.value[uniqueKey] || "";
let newCsjg = row.csjg || "";
// 标准化处理
newCsjg = String(newCsjg).trim();
// 调试日志
console.log(`
行${uniqueKey} 比较:
缓存旧值:"${oldCsjg}"(类型:${typeof oldCsjg})
当前新值:"${newCsjg}"(类型:${typeof newCsjg})
是否相等:${oldCsjg === newCsjg}
`);
// 无变化或只读状态,直接返回
if (oldCsjg === newCsjg || readonly.value) {
console.log("值未变化,无需保存");
return;
}
// 设置提交中标记
// isSaving.value[uniqueKey] = true;
const requestParams = {
...row,
csjg: newCsjg
};
console.log('提交参数:', requestParams);
if (row.id) {
newresult(requestParams).then(response => {
originalCsjgMap.value[uniqueKey] = newCsjg;
ElMessage.success("结果保存成功");
emits('fetchLabResults');
}).catch(error => {
emits('fetchLabResults');
// 清除提交中标记
// isSaving.value[uniqueKey] = false;
});
} else {
changeresult(requestParams).then(response => {
originalCsjgMap.value[uniqueKey] = newCsjg;
ElMessage.success("结果保存成功");
emits('fetchLabResults');
}).catch(error => {
emits('fetchLabResults');
// 清除提交中标记
// isSaving.value[uniqueKey] = false;
});
}
};
/** /**
* 保存数据 * 保存数据
*/ */
@ -289,23 +636,16 @@ const handleSave = () => {
} }
}; };
/**
* 删除行
*/
const handleDelete = (row) => {
emits("delete", row);
tableData.value = tableData.value.filter((item) => item.id !== row.id);
updateOriginalCsjgMap();
};
/** /**
* 批量删除 * 批量删除
*/ */
const handleBatchDelete = async () => { const handleBatchDelete = () => {
if (selectedRows.value.length === 0) return; if (selectedRows.value.length === 0) return ElMessage.warning("请先选择要删除的记录");
const arr = selectedRows.value.filter(row => !row.id);
try { ElMessageBox.confirm(
await ElMessageBox.confirm(
`确定要删除选中的 ${selectedRows.value.length} 条记录吗?`, `确定要删除选中的 ${selectedRows.value.length} 条记录吗?`,
'提示', '提示',
{ {
@ -313,34 +653,35 @@ const handleBatchDelete = async () => {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
} }
); ).then(() => {
if (arr.length == 0) {
await deleteresult(selectedRows.value); const index = tableData.value.findIndex(item => item.xmdh === selectedRows.value[0].xmdh);
emits('fetchLabResults'); if (index > -1) tableData.value.splice(index, 1);
selectedRows.value = [];
} else {
deleteresult(arr).then(res => {
if (res.code == 0) {
ElMessage.success('删除成功'); ElMessage.success('删除成功');
} catch (error) {
if (error !== 'cancel') { // 排除用户取消的情况
ElMessage.error('删除失败: ' + (error.message || '未知错误'));
}
}
};
/**
* 重置表格的修改状态
*/
const resetChange = () => {
originalData.value = JSON.parse(JSON.stringify(props.tableData));
updateOriginalCsjgMap();
emits("update:hasChanges", false);
};
/**
* 刷新数据
*/
const fetchLabResults = () => {
emits('fetchLabResults'); emits('fetchLabResults');
}
})
}
}).catch(() => {
});
}; };
const printHandle = () => {
printsample(tableKey.value).then(res => {
if (res.code == 0) {
// ElMessage.success("打印成功");
}
})
};
const previewHandle = () => {
// emits('previewHandle', props.tableKey);
};
/** /**
* 获取行号 * 获取行号
*/ */
@ -367,52 +708,44 @@ watch(
} }
); );
// 暴露方法给父组件
defineExpose({
handleAdd,
handleDelete,
handleSave,
handleBatchDelete,
resetChange,
addRowFromDict,
});
</script> </script>
<style scoped> <style scoped lang="scss">
.custom-table-container /deep/ .el-table__cell { .result-container {
height: calc(90vh - 95px);
display: flex;
flex-direction: column;
overflow: hidden;
}
.flex-between {
height: 32px;
}
.custom-table-container {
flex: 1;
:deep(.el-table__cell) {
padding: 0 !important; padding: 0 !important;
}
} }
.custom-table-container /deep/ .el-table__row {
height: var(--table-row-height) !important;
}
.full-width-input /deep/ .el-input__inner {
.full-width-input .el-input__inner {
width: 100%; width: 100%;
height: calc(var(--table-row-height) - 2px); height: calc(var(--table-row-height) - 2px);
padding: 0 8px; /* 增加内边距提升输入体验 */ padding: 0 8px;
/* 增加内边距提升输入体验 */
border: 0px solid #dcdfe6; border: 0px solid #dcdfe6;
border-radius: 0; border-radius: 0;
box-sizing: border-box; box-sizing: border-box;
background-color: transparent; background-color: transparent;
} }
.full-width-input /deep/ .el-input__inner:focus { .full-width-input .el-input__inner:focus {
border-color: #409eff; border-color: #409eff;
box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2); box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
outline: none; outline: none;
} }
.el-table__body-wrapper {
overflow: visible !important;
}
.new-row-enter-active {
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style> </style>

View File

@ -0,0 +1,870 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="compact-form">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="delResultFlag" @click="handleDelete"
v-hasPermi="['system:dict:remove']">删除明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="changeResultFlag" @click="handleUpdate"
v-hasPermi="['system:dict:edit']">保存</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" @click="handleCheck0"
v-hasPermi="['system:dict:import']">初报</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleCheck"
v-hasPermi="['system:dict:import']">初审</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">取消初审</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleCheck"
v-hasPermi="['system:dict:import']">审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Upload" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">取消审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">预览</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">打印</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Upload" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">上一个</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleUnCheck"
v-hasPermi="['system:dict:export']">下一个</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="compact-form">
<el-form :model="queryParams" ref="queryRef" :inline="true" :rules="rules">
<el-form-item class="compact-form" label="" label-width="0px" prop="ybh">
<el-col :span="1.5">样本编号</el-col>
<el-col :span="4.5">
<el-input v-model="queryParams.ybh" clearable placeholder="样本编号" prefix-icon="Search"
@keyup.enter="handleQueryYbh" @clear="handleQueryYbh" />
</el-col>
<el-col :span="1.5">
<div>检验日期</div>
</el-col>
<el-col :span="3.5">
<el-date-picker v-model="queryParams.jyrq" type="date" label-width="20px" placeholder="检验日期"
value-format="YYYY-MM-DD" :clearable="false" @change="handleQuery"></el-date-picker>
</el-col>
<el-col :span="1.5">
<div>部门组别</div>
</el-col>
<el-col :span="3.5">
<!-- <Ddlb v-model="queryParams.instrGroup" :options="instrGroupOptions" placeholder="请选择部门组别"
@change="handleinstrGroupChange" :value="setInstrGroup" label-width="40px" /> -->
<el-select v-model="queryParams.instrGroup" placeholder="请选择" style="width:140px"
@change="handleinstrGroupChange">
<el-option v-for="item in instrGroupOptions" :key="item.zddh" :label="item.zdmc" :value="item.zddh" />
</el-select>
</el-col><el-col :span="1">
<div>仪器</div>
</el-col>
<el-col :span="4">
<Ddlb v-model="queryParams.yq" :options="instrOptions" placeholder="请选择仪器" @change="handleYqChange"
:value="setInstr" />
</el-col>
</el-form-item>
</el-form>
</el-row>
<el-row :gutter="5">
<el-col :span="5" :xs="24">
<div class="compact-form">
<LabPatForm v-model:labPat="labPat" :labPatKey="queryParams" ref="labPatRef" @save="savelabPat"
@reset="resetlabPat" />
</div>
</el-col>
<el-col :span="8" :xs="24">
<div class="compact-form">
<LabResultForm ref="labResultRef" :tableData="labResuts" :tableKey="queryParams"
:is-data-refreshed="isDataRefreshed" @add="addLabResult" @delete="deleteLabResults" @edit="editLabResult"
@delfalg="setdelResultFlag" @changefalg="setchangeResultFlag" @fetchLabResults="fetchLabResults" />
</div>
</el-col>
<el-col :span="11" :xs="24">
<div class="compact-form">
<LabPatListForm :labPatList="labPatList" :labPatKey="queryParams" :total="total" @select="selectJob"
@search="searchJobs" @page-change="pageChange" />
</div>
</el-col>
<ItemInput ref="itemDictRef" v-model="selectedItemValue" :label-value.sync="selectedItemLabel"
:dict-type="'LAB_ITEM'" :fetch-dict="fetchLabItemDict" :dialog-title="'选择检验项目'" :disabled="false"
:load-on-mount="false" :table-data.sync="labResuts" class="hidden-dict-input" @select="handleItemSelect" />
</el-row>
<!-- 引入弹窗组件 -->
<unconfirmlog v-model:visible="dialogVisible" :reason-text="currentReason" @onConfirm="unconfirmlogConfirm"
@onCancel="unconfirmlogCancel" />
<CheckUser v-model:visible="showCheckUser" :title="checktitle"
:validation-rules="{ userIdMinLength: 4, passwordMinLength: 6 }" @onConfirm="handleAuthSuccess"
@onCancel="handleAuthCancel" />
<uploadwarning v-model:visible="showuploadwarning" :msgdata="warningMsgdata" :showRules=1
@onupload="handleWarningSuccess" @onCancel="handleWarningCancel" />
</div>
</template>
<script setup>
import {
getDate,
queryLabPatList,
queryLabResults,
updateLabPat,
queryLabPat,
check2, uncheck2, unconfirmlog, checkuser, reglimit, queryXmInfo
} from "../../../api/liswork/work/LisWork";
import { querylabInstrList, querylabInstrListByLisgroup } from "../../../api/liswork/dict/LabInstr";
import { getComDicts, queryComDictListService } from "../../../api/liswork/dict/ComDict";
import LabPatForm from './components/labpat.vue'
import LabResultForm from './components/labresult.vue'
import LabPatListForm from './components/labpatlist.vue'
import Unconfirmlog from './components/unconfirmlog.vue'
import Ddlb from './components/ddlb.vue'
import CheckUser from './components/CheckUser.vue'
import uploadwarning from './components/uploadwarning.vue'
import UCKDictInput from './components/DictInput.vue';
import { ElMessage, ElMessageBox } from "element-plus";
import ItemInput from "@/views/liswork/work/components/ItemInput.vue";
const showuploadwarning = ref(false);
const single = ref(true);
const delResultFlag = ref(true);
const changeResultFlag = ref(true);
const loading = ref(true);
const instrGroupOptions = ref([]);
const instrOptions = ref([]);
const labPatList = ref([])
const patdata = reactive({
form: {},
labPat: {
// jyrq: '',
// yq: '',
// ybh: '',
// brly: '',
// jzbz: '',
// brdh: '',
// brxm: '',
// brxb: '',
// nl: null,
// nldw: '1',
// ksdh: '',
// ch: '',
// sjys: '',
// sqsj: '',
// cyrq: '',
// dysj: '',
// zd: '',
// bz: '',
// yhdh: '',
// hdys: '',
// jgbz: '0',
// yblx: ''
},
rules: {}
});
const { labPat } = toRefs(patdata);
const labResuts = ref([])
const total = ref(0)
const currentLabResults = ref([])
const searchText = ref('')
const pageSize = ref(10)
const currentPage = ref(1)
const setInstrGroup = ref('')
const setInstr = ref('')
const yq0 = ref('')
const jyrq0 = ref('')
const ybh0 = ref('')
const checktitle = ref('操作人身份验证')
const nextstepname = ref('')
const warningMsgdata = ref('')
const checkuserid = ref('')
const querydata = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
jyrq: formatDate(new Date()),
yq: '1',
ybh: '1',
days: 1,
instrGroup: '02',
problemId: 0,
yhdh: '46'
},
rules: {
ybh: [],
jyrq: [],
instrGroup: [],
yq: [],
}
,
});
// 父组件:新增“数据刷新标记”,初始为false
const isDataRefreshed = ref(false);
// 新增:控制标记的重置(避免持续触发子组件缓存更新)
const resetDataRefreshFlag = () => {
// 延迟重置,确保子组件有足够时间接收标记并更新缓存
setTimeout(() => {
isDataRefreshed.value = false;
}, 100);
};
const { queryParams, form, rules } = toRefs(querydata);
//子组件labResult.vue
const labResultRef = ref(null);
//子组件labPatRef.vue
const labPatRef = ref(null);
// 弹窗控制状态
const dialogVisible = ref(false);
const showCheckUser = ref(false);
// 弹窗显示的原因文本
const currentReason = ref('');
// 存储需要传递给API的参数
const apiParams = ref(null);
// 处理弹窗确认(获取返回值后调用API)
const unconfirmlogConfirm = async (inputValue) => {
try {
console.log('用户输入的操作原因:', inputValue);
// 调用API,携带用户输入的原因和之前保存的参数
loading.value = true;
unconfirmlog({ ...queryParams.value, reason: inputValue }).then(response => {
console.log('response:', response)
loading.value = false;
if (response.code == "0") {
runnextstepname();
}
}).catch(error => {
console.error('审核失败', error);
loading.value = false;
});
// API调用成功后的处理
console.log('操作成功:', response);
// 可以添加成功提示、刷新数据等逻辑
} catch (error) {
console.error('操作失败:', error);
// 错误处理
} finally {
// 重置参数
apiParams.value = null;
}
};
// 处理弹窗取消
const unconfirmlogCancel = () => {
console.log('用户取消了操作');
// 重置参数
apiParams.value = null;
cancalnextstepname();
};
// 处理验证成功
const handleAuthSuccess = async (userInfo) => {
console.log('验证信息:', userInfo);
// 调用后端接口进行实际验证
loading.value = true;
checkuser({ yhdh: userInfo.userId, mm: userInfo.password }).then(response => {
console.log('response:', response)
loading.value = false;
console.log('userInfo:', userInfo);
if (response.code == "0") {
checkuserid.value = userInfo.userId;
runnextstepname();
}
}).catch(error => {
console.error('验证接口调用失败', error);
loading.value = false;
checkuserid.value = '';
});
};
// 处理取消验证
const handleAuthCancel = () => {
console.log('用户取消了验证');
// 执行取消后的操作
cancalnextstepname();
};
// 处理危急值登记
const handleWarningSuccess = async (userInfo) => {
console.log('处理危急值登记:', userInfo);
// 调用后端接口进行实际验证
loading.value = true;
reglimit({ ...queryParams.value, qrr: userInfo.userId, tzfs: userInfo.uploadTp, phone: userInfo.phone }).then(response => {
console.log('危急值response:', response.code);
loading.value = false;
if (response.code == "0") {
runnextstepname();
}
if (response.code == "4") {
ElMessageBox.confirm(response.msg, '系统提示', { confirmButtonText: '是', cancelButtonText: '否', type: 'warning' }).then(() => {
queryParams.value.problemId = queryParams.value.problemId + 1;
handleWarningSuccess(userInfo);
}).catch(() => {
runnextstepname();
});
}
}).catch(error => {
console.error('处理危急值登记接口调用失败', error);
loading.value = false;
});
};
// 处理取消验证
const handleWarningCancel = () => {
console.log('用户取消了登记');
// 执行取消后的操作
cancalnextstepname();
};
// 页面载入时获取基本默认值参数信息,后面有扩展需要都放这里
const loadYqConfig = async () => {
// 使用 .value 获取响应式数据的值
jyrq0.value = '2025-08-19';//formatDate(new Date());
yq0.value = 'COBAS';
ybh0.value = '1';
querydata.queryParams.jyrq = jyrq0.value;
querydata.queryParams.yq = 46;
querydata.queryParams.ybh = ybh0.value;
querydata.queryParams.instrGroup = '02';
}
// 加载部门下拉列表
function loadinstrGroupOptions() {
loading.value = true;
getComDicts({ zdlb: 'GROUP' }).then(response => {
instrGroupOptions.value = response.data
loading.value = false;
setInstrGroup.value = querydata.queryParams.instrGroup;
});
loadYqConfig();
}
// 部门组别变化时更新仪器下拉框
const handleinstrGroupChange = (value) => {
queryParams.value.instrGroup = value
// 清空仪器选择并重新加载
// queryParams.value.yq = '';
instrOptions.value = [];
// 加载对应组别的仪器数据
loadinstrOptions();
}
// 仪器变化时的处理函数
const handleYqChange = (value) => {
if (value === null || value === '') {
queryParams.value.yq = yq0.value;
setInstr.value = yq0.value;
} else {
yq0.value = value;
queryParams.value.yq = value;
handleQuery();
}
}
// 加载仪器下拉列表
const loadinstrOptions = () => {
loading.value = true;
querylabInstrListByLisgroup({ pageNum: 1, pageSize: 100, lisgroup: queryParams.value.instrGroup })
.then(response => {
if (response.rows) {
instrOptions.value = toRaw(response.rows).map(item => ({
id: item.yq.trimEnd(),
text: item.yqmc
}))
loading.value = false;
setInstr.value = querydata.queryParams.yq;
} else {
throw new Error('仪器响应数据为空');
}
})
.catch(error => {
console.error('仪器数据加载失败', error);
loading.value = false;
});
// console.error('aaa', queryParams.value);
//console.log('test22:', instrOptions.value);
}
//获取服务器时间
const serverDate = computed(() => {
return formatDate(new Date());
});
//切换仪器或检验日期后重置页码
function resetData() {
queryParams.value = {
pageNum: 1,
pageSize: 15,
}
searchText.value = ''
currentPage.value = 1
pageSize.value = 15
}
//载入样本列表(右边labpatlist.vue组件)
const fetchlabPatList = async (search = '', page = 1, size = 15) => {
loading.value = true;
queryParams.value.pageSize = size;
queryParams.value.pageNum = page;
queryLabPatList(queryParams.value).then(response => {
labPatList.value = response.rows;
total.value = response.total;
loading.value = false;
}).catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//载入样本编辑表单(左边labpat.vue组件)
const fetchLabPat = async () => {
loading.value = true;
queryLabPat(queryParams.value).then(response => {
labPat.value = response.data;
loadingPatLabel();
loading.value = false;
}).catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//载入样本结果表单(中间labresult.vue组件)
const fetchLabResults = async () => {
loading.value = true;
// console.log('test', queryParams.value);
queryLabResults({ jyrq: queryParams.value.jyrq, yq: queryParams.value.yq, ybh: queryParams.value.ybh }).then(response => {
// console.log('RESULT:', response);
labResuts.value = response.data;
loading.value = false;
// 关键:设置“数据刷新标记”为true(删除后数据变化,需更新子组件缓存)
isDataRefreshed.value = true;
// 数据刷新后重置标记
resetDataRefreshFlag();
}).catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//保存样本表单
const savelabPat = async (labPat) => {
// 实际项目中这里应该是API调用
console.log('保存主表:', labPat)
if (!labPat.id) {
// 新增
labPat.id = Date.now()
labPatList.value.unshift({ ...labPat })
} else {
// 更新
const index = labPatList.value.findIndex(item => item.id === labPat.id)
if (index !== -1) {
labPatList.value.splice(index, 1, { ...labPat })
}
}
alert('保存成功')
}
//重置结果表单
const resetlabPat = () => {
labResuts.value = []
currentLabResults.value = []
}
//新增结果列
const addLabResult = async (LabResult) => {
// 实际项目中这里应该是API调用
console.log('添加明细:', LabResult)
if (!LabResult.xh) {
LabResult.xh = currentLabResults.value.length + 1
}
const index = currentLabResults.value.findIndex(item => item.xh === LabResult.xh)
if (index !== -1) {
currentLabResults.value.splice(index, 1, { ...LabResult })
} else {
currentLabResults.value.push({ ...LabResult })
}
alert('添加成功')
}
//删除结果列
const deleteLabResults = async (xhList) => {
// 实际项目中这里应该是API调用
console.log('删除明细:', xhList)
currentLabResults.value = currentLabResults.value.filter(item => !xhList.includes(item.xh))
alert('删除成功')
}
//修改结果列
const editLabResult = (LabResult) => {
console.log('编辑明细:', LabResult)
// 在表单中填充数据
// 这里不需要做任何事情,因为组件已经绑定了数据
}
//样本列表点击切换样本信息,同步载入左边样本信息表单和中间结果表单
const selectJob = async (job) => {
// 复制选中的job数据到当前编辑状态
// Object.assign(labPat.value, {...job})
queryParams.value.jyrq = job.jyrq;
queryParams.value.yq = job.yq;
queryParams.value.ybh = job.ybh;
checkQuery()
isDataRefreshed.value = true;
//await nextTick();
// 获取对应的明细数据
await fetchLabPat();
await fetchLabResults();
resetDataRefreshFlag();
}
//样本列表检索时刷新列表信息
const searchJobs = async (text, page, size) => {
searchText.value = text
currentPage.value = page
pageSize.value = size
loadlabPatList()
}
//样本列表翻页时刷新列表信息
const pageChange = async (page, size) => {
currentPage.value = page
pageSize.value = size
loadlabPatList()
}
//刷新样本列表方法封装
const loadlabPatList = async () => {
await fetchlabPatList(searchText.value, currentPage.value, pageSize.value)
}
/** 查询服务器时间 */
function getdate() {
getDate().then(response => {
serverDate.value = formatDate(new Date(response.data));
})
}
//日期格式化
function formatDate(date) {
const year = date.getFullYear();
// 月份是从0开始的,所以要加1
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day} 00:00:00`;
}
const setdelResultFlag = (value) => {
delResultFlag.value = value;
}
const setchangeResultFlag = (value) => {
changeResultFlag.value = value;
}
//新增
const handleAdd = () => {
// labResultRef.value?.handleAdd();
openItemDictDialog();
};
function handleUpdate() {
}
function handleDelete() {
labResultRef.value?.handleBatchDelete();
}
function loadingPatLabel() {
// labPatRef.value?.loadingLabel();
}
function handleCheck0() {
}
function handleCheck() {
queryParams.value.problemId = 0;
nextstepname.value = 'handleCheck2';
queryParams.value.yhdh = checkuserid.value
if (queryParams.value.yhdh == '') {
checktitle.value = '审核人工号密码验证'
showCheckUser.value = true;
} else {
handleCheck2();
}
}
function handleCheck2() {
loading.value = true;
console.log('queryParams:', queryParams.value)
queryParams.value.yhdh = checkuserid.value
check2(queryParams.value).then(response => {
loading.value = false;
if (response.code == "4" && response.problemId === 4) {
console.log('queryParams1111:', queryParams.value)
nextstepname.value = 'handleCheck2';
queryParams.value.problemId = response.problemId;
// 保存需要传递给API的参数
apiParams.value = queryParams;
// 设置弹窗显示的原因文本
warningMsgdata.value = response.msg;
// 显示弹窗
showuploadwarning.value = true;
}
console.log('response:', response)
}).catch(error => {
console.error('审核失败', error);
loading.value = false;
});
}
function handleUnCheck() {
queryParams.value.problemId = 0;
nextstepname.value = 'handleuncheck2';
queryParams.value.yhdh = checkuserid.value
if (queryParams.value.yhdh == '') {
checktitle.value = '取消审核人工号密码验证'
showCheckUser.value = true;
} else {
handleuncheck2();
}
}
function handleuncheck2() {
loading.value = true;
queryParams.value.yhdh = checkuserid.value
uncheck2(queryParams.value).then(response => {
loading.value = false;
if (response.code == "4") {
nextstepname.value = 'handleuncheck2';
queryParams.value.problemId = response.problemId;
// 保存需要传递给API的参数
apiParams.value = queryParams;
// 设置弹窗显示的原因文本
currentReason.value = response.msg;
// 显示弹窗
dialogVisible.value = true;
}
}).catch(error => {
console.error('解除审核失败', error);
loading.value = false;
});
}
function handlePrint() {
}
function handleQueryDetail() {
}
//切换仪器或检验日期时载入
const handleQuery = async () => {
if (checkQuery() === false) return false
// resetData();
await loadlabPatList();
//暂时默认打开1号样本,后面由参数控制
querydata.queryParams.ybh = '1';
isDataRefreshed.value = true;
await handleQueryYbh();
resetDataRefreshFlag();
}
//切换样本号时载入
const handleQueryYbh = async () => {
if (checkQuery() === false) return false
isDataRefreshed.value = true;
await fetchLabPat();
await fetchLabResults();
resetDataRefreshFlag();
}
//主键空置拦截
function checkQuery() {
if (querydata.queryParams.jyrq === '' || querydata.queryParams.jyrq === null) {
querydata.queryParams.jyrq = jyrq0.value
return false;
}
if (querydata.queryParams.yq === '' || querydata.queryParams.yq === null) {
querydata.queryParams.yq = yq0.value
return false;
}
if (querydata.queryParams.ybh === '' || querydata.queryParams.ybh === null) {
querydata.queryParams.ybh = ybh0.value
return false;
}
jyrq0.value = querydata.queryParams.jyrq
yq0.value = querydata.queryParams.yq
ybh0.value = querydata.queryParams.ybh
}
function runnextstepname() {
if (nextstepname.value != '') {
switch (nextstepname.value) {
case 'handleuncheck2':
handleuncheck2();
break;
case 'handleCheck2':
handleCheck2();
break;
default:
// 当所有case都不匹配时执行(可选)
}
}
}
function cancalnextstepname() {
if (nextstepname.value != '') {
switch (nextstepname.value) {
case 'handleuncheck2':
ElMessage({ message: '取消操作,撤销审核失败!', type: 'error' })
break;
case 'handleCheck2':
ElMessage({ message: '取消操作,审核失败!', type: 'error' })
break;
default:
// 当所有case都不匹配时执行(可选)
}
}
}
// -------------------------- 项目选择相关变量 --------------------------
const itemDictRef = ref(null); // DictInput组件引用
const selectedItemValue = ref(""); // 选择的项目编码xmdh
const selectedItemLabel = ref(""); // 选择的项目名称xmmc
// -------------------------- 1. 项目字典API调用方法(传递给DictInput) --------------------------
/**
* 项目字典加载方法:适配DictInput的fetchDict参数格式
* @param {String} dictType - 父组件传递的dict-type(无实际意义,可忽略)
* @returns {Promise<Array>} - 项目字典数据(格式需匹配DictInput要求:{value, label})
*/
// 父组件中:完善的项目字典加载方法
const fetchLabItemDict = async (dictType) => {
// 日志1:确认方法被调用
try {
// 1. 验证必要参数(如当前仪器yq)
if (!queryParams.value.yq) {
console.error("[fetchLabItemDict] 缺少必要参数:yq(仪器)");
ElMessage.error("请先选择仪器");
return [];
}
// 日志2:打印请求参数
const requestParams = {
pageNum: 1,
pageSize: 1000, // 加载足够多的项目
yq: queryParams.value.yq, // 当前选中的仪器
};
// 2. 调用API(确保queryXmInfo是正确的API方法)
const response = await queryXmInfo(requestParams);
// 日志3:打印原始响应
// 3. 验证响应格式
if (!response) {
throw new Error("API返回为空");
}
if (response.code !== "0") { // 假设0为成功状态码
throw new Error(`API返回错误:${response.msg || "未知错误"}`);
}
if (!Array.isArray(response.data)) {
throw new Error(`API返回data不是数组,实际为:${typeof response.data}`);
}
// 4. 格式化数据(适配DictInput的{value, label}结构)
const formattedData = response.data.map((item, index) => {
// 验证项目数据完整性
if (!item.xmdh) {
console.warn(`[fetchLabItemDict] 第${index}条数据缺少xmdh(项目编码):`, item);
}
if (!item.xmmc) {
console.warn(`[fetchLabItemDict] 第${index}条数据缺少xmmc(项目名称):`, item);
}
return {
value: item.xmdh || `未知编码_${index}`, // 确保value存在
label: item.xmmc || `未知名称_${index}`, // 确保label存在
dw: item.dw || "", // 单位(可选)
refs: item.refs || "", // 参考值(可选)
};
});
// 日志4:打印格式化后的数据量
return formattedData;
} catch (error) {
// 日志5:捕获并显示错误
console.error(`[fetchLabItemDict] 加载失败:`, error.message);
ElMessage.error(`项目字典加载失败:${error.message}`);
return []; // 失败时返回空数组,避免弹窗报错
}
};
// -------------------------- 2. 打开项目选择弹窗 --------------------------
const openItemDictDialog = () => {
if (itemDictRef.value && typeof itemDictRef.value.openDictSelector === 'function') {
itemDictRef.value.openDictSelector(); // 确保方法存在再调用
} else {
console.warn("DictInput组件未加载完成或方法未暴露");
}
};
// -------------------------- 3. 处理项目选择结果:传递给labresult子组件新增行 --------------------------
const handleItemSelect = async (selectedItem) => {
// 1. 构造新增行数据
const newRow = {
id: `temp_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
xmdh: selectedItem.value,
xmmc: selectedItem.label,
dw: selectedItem.dw || "",
refs: selectedItem.refs || "",
jyrq: queryParams.value.jyrq,
yq: queryParams.value.yq,
ybh: queryParams.value.ybh,
};
// 2. 调用子组件方法新增行(仅一次)
if (labResultRef.value && typeof labResultRef.value.addRowFromDict === 'function') {
await labResultRef.value.addRowFromDict(newRow);
ElMessage.success(`已新增项目:${newRow.xmmc}`);
}
};
//打开页面需要先初始化的内容都放这里
onMounted(() => {
loadYqConfig();
loadinstrGroupOptions(); // 页面加载时获取部门组别数据
loadinstrOptions();
loadlabPatList();
})
</script>
<style scoped lang="scss">
.compact-form .el-form-item {
padding: 5px;
margin-bottom: 5px;
}
.compact-form .el-form-item__label {
padding-bottom: 0px;
}
.card-content {
max-height: 450px;
/* 设置最大高度 */
overflow-y: auto;
/* 超出高度时显示垂直滚动条 */
padding: 5px;
/* 保持与卡片默认一致的内边距 */
}
.clickable:hover {
cursor: pointer;
/* 鼠标悬停时显示手型 */
transition: all 0.3s;
/* 平滑过渡效果 */
color: blue;
}
.hidden-dict-input {
/* 定位到屏幕左侧外,视觉上不可见 */
position: absolute;
left: -9999px;
top: auto;
/* 防止占用布局空间 */
width: 1px;
height: 1px;
/* 防止意外聚焦 */
outline: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -336,7 +336,7 @@ onMounted(() => {
$lighterBlue: #409EFF; $lighterBlue: #1F6DD3;
.container { .container {
position: relative; position: relative;