tab栏模块

This commit is contained in:
wyuu 2025-10-22 17:36:25 +08:00
parent c65531f531
commit d16d7364bd
17 changed files with 1210 additions and 97 deletions

View File

@ -225,3 +225,35 @@ export function delSample(query?: Object) {
params: query
})
}
// 获取标本流转信息
export function reqinfo(query?: Object) {
return request({
url: '/lisworkpageinfo/reqinfo',
method: 'get',
params: query
})
}
// 获取检验结果图片信息
export function resultgraph(query?: Object) {
return request({
url: '/lisworkpageinfo/resultgraph',
method: 'get',
params: query
})
}
// 获取组合项目模板
export function inputmdl(query?: Object) {
return request({
url: '/lisworkpageinfo/inputmdl',
method: 'get',
params: query
})
}
// 获取复查结果
export function redoResult(query?: Object) {
return request({
url: '/lisworkpageinfo/redo',
method: 'get',
params: query
})
}

BIN
src/assets/images/xjt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/assets/images/yjt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -160,6 +160,10 @@
padding: 0 !important;
}
.el-checkbox {
height: 100% !important;
}
/* 修改选中行背景色 */
.el-table__body tr.current-row>td {
background-color: #a4beef !important;

View File

@ -0,0 +1,189 @@
<template>
<div class="tab-container">
<el-icon class="scroll-btn left" @click="scrollAndSelect('left')" :disabled="!hasLeftScroll">
<ArrowLeft />
</el-icon>
<div class="tab-wrapper">
<div class="tab-list" ref="tabListRef" @scroll="handleScroll">
<div :class="['text', activeTab === item.value ? 'active' : '']" v-for="item in tabList" :key="item.value"
@click="handleTabClick(item.value)" ref="tabItems">
{{ item.label }}
</div>
</div>
</div>
<el-icon class="scroll-btn right" @click="scrollAndSelect('right')" :disabled="!hasRightScroll">
<ArrowRight />
</el-icon>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
// 定义 tabList 元素的类型
interface TabItem {
label: string;
value: string | number;
}
const props = defineProps<{
tabList: TabItem[];
activeTab: string | number;
}>();
const emit = defineEmits(['update:activeTab']);
const tabListRef = ref();
const tabItems = ref<HTMLDivElement[]>([]);
const hasLeftScroll = ref(false);
const hasRightScroll = ref(false);
const handleScroll = () => {
if (tabListRef.value) {
const { scrollLeft, scrollWidth, clientWidth } = tabListRef.value;
hasLeftScroll.value = scrollLeft > 5;
hasRightScroll.value = scrollLeft < scrollWidth - clientWidth - 5;
}
};
const handleTabClick = (value: any) => {
emit('update:activeTab', value);
scrollToActiveTab();
};
// 滚动到当前激活的Tab
const scrollToActiveTab = () => {
if (!tabListRef.value) return;
const activeIndex = props.tabList.findIndex((item: any) => item.value === props.activeTab);
if (activeIndex === -1) return;
const activeTabElement = tabItems.value[activeIndex];
if (activeTabElement) {
const scrollContainer = tabListRef.value;
const containerRect = scrollContainer.getBoundingClientRect();
const tabRect = activeTabElement.getBoundingClientRect();
// 如果Tab不在可见区域内,则滚动到可见区域
if (tabRect.left < containerRect.left || tabRect.right > containerRect.right) {
scrollContainer.scrollTo({
left: tabRect.left - containerRect.left + scrollContainer.scrollLeft - 100,
behavior: 'smooth'
});
}
}
};
// 滚动并选中
const scrollAndSelect = (direction: 'left' | 'right') => {
if (!tabListRef.value || tabItems.value.length === 0) return;
const currentIndex = props.tabList.findIndex((item: any) => item.value === props.activeTab);
let newIndex;
if (direction === 'left') {
// 向左滚动并选中前一个Tab
newIndex = currentIndex > 0 ? currentIndex - 1 : 0;
} else {
// 向右滚动并选中后一个Tab
newIndex = currentIndex < props.tabList.length - 1 ? currentIndex + 1 : props.tabList.length - 1;
}
emit('update:activeTab', props.tabList[newIndex].value);
};
// 监听activeTab变化,确保滚动到可视区域
watch(() => props.activeTab, scrollToActiveTab);
// 初始化时检查滚动状态
setTimeout(() => {
handleScroll();
}, 0);
</script>
<style scoped lang="scss">
.tab-container {
position: relative;
display: flex;
align-items: center;
width: 100%;
}
.scroll-btn {
width: 30px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
cursor: pointer;
z-index: 10;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
transition: opacity 0.3s;
&.left {
position: absolute;
left: 0;
top: 0;
border-radius: 12px 0 0 12px;
border: 1px solid #fff;
}
&.right {
position: absolute;
right: 0;
top: 0;
border-radius: 0 12px 12px 0;
border: 1px solid #fff;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.tab-wrapper {
flex: 1;
overflow: hidden;
padding: 0 30px; // 给箭头留出空间
}
.tab-list {
display: flex;
gap: 1px;
white-space: nowrap;
margin-bottom: 1px;
background-color: #fff;
cursor: pointer;
overflow-x: auto;
scrollbar-width: none; // 隐藏滚动条
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none; // 隐藏滚动条
}
}
.text {
font-weight: 400;
font-size: 16px;
color: #1F6DD3;
width: 90px;
height: 48px;
line-height: 48px;
text-align: center;
flex-shrink: 0;
cursor: pointer;
}
.active {
background: #e9f1fb;
color: #1F6DD3;
font-weight: 600;
font-family: SourceHanSansCN, SourceHanSansCN;
}
</style>

View File

@ -42,7 +42,6 @@
<el-row :gutter="10">
<el-col :span="16" class="right-table">
<div class="title"> 条码生成 </div>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="printHandle">生成条码</el-button>
@ -71,8 +70,8 @@
<el-col :span="1.5">
<el-button type="warning" plain>清单打印</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery"
:columns="columns"></right-toolbar>
<!-- <right-toolbar v-model:showSearch="showSearch" @updateColumns="updateColumns" @queryTable="handleQuery"
:columns="columns"></right-toolbar> -->
</el-row>
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig" :loading="loading"
@ -101,8 +100,8 @@
</el-col>
<el-col :span="8" class="right-table">
<div class="title"> 项目 </div>
<!-- <el-button type="danger" class="mb10">拆分选中项目</el-button> -->
<!-- <div class="title"> 项目 </div> -->
<el-button type="primary" plain class="mb8">拆分选中项目</el-button>
<CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig">
<template #jjzt="{ row }">
{{ row.jjzt == '1' ? '计价' : '无' }}
@ -114,6 +113,20 @@
</el-col>
</el-row>
<!-- 样本流程 -->
<div class="time-line-box">
<div v-for="(item, i) in timelineItems" :key="i" class="time-line-item">
<div v-if="i != 0 && item.time" class="timeline-connector">
<img class="timg" src="@/assets/images/yjt.png" alt="">
</div>
<div class="timeline-content">
<div :class="['index', lastTimeIndex == i ? 'active' : '']">{{ i + 1 }}</div>
<div class="timeline-title">{{ item.title }}</div>
<div class="timeline-time">{{ item.time }}</div>
</div>
</div>
</div>
<!-- 采样登记 -->
<el-dialog v-model="dialogVisible" :title="title" draggable :close-on-click-modal="false" width="70vw"
@ -292,8 +305,43 @@ const rowClassNameHd = ({ row, rowIndex }: {
}) => {
return 'custom-row-class'; // 返回自定义类名
};
// 时间线数据
const timelineItems = ref(
[
{ title: '检验申请', time: '' },
{ title: '标本采集', time: '' },
{ title: '上机检验', time: '' },
{ title: '标本送检', time: '' },
// { title: '检验接收', time: '' },
{ title: '标本签收', time: '' },
{ title: '结果审核', time: '' },
]
);
// 计算最后一个有时间的节点索引
const lastTimeIndex = computed(() => {
const lastIndex = timelineItems.value.findLastIndex(item => item.time);
if (lastIndex === -1) {
return -1
} else {
return lastIndex;
}
});
// 点击行
const rowHandle = (row: any, column: TableColumnCtx<any>, event: Event) => {
const rowHandle = (row: any) => {
timelineItems.value =
[
{ title: '检验申请', time: row.sqsj },
{ title: '标本采集', time: row.cysj },
{ title: '上机检验', time: row.zxsj },
{ title: '标本送检', time: row.bbsjsj },
// { title: '检验接收', time: row.jssj},
{ title: '标本签收', time: row.jssj },
{ title: '结果审核', time: row.confirmtime },
]
getRightTable(row)
};
@ -336,7 +384,7 @@ const rightColumns = ref([
// 表格配置
const rightTableConfig = ref({
// stripe: true, // 斑马纹
border: false, // 边框
border: true, // 边框
height: '', // 高度
highlightCurrentRow: true, // 高亮当前行
})
@ -361,12 +409,13 @@ const cyColumns = ref([
])
// 表格配置
const cyTableConfig = ref({
// stripe: true, // 斑马纹
border: false, // 边框
border: true, // 边框
height: '100%', // 高度
highlightCurrentRow: true, // 高亮当前行
})
// 采样登记
const openBlock = (value: string) => {
if (sqhs.value.length > 0) {
@ -522,8 +571,8 @@ function adjustTableHeight() {
if (compactForm.value) {
// 获取高度
heightForm.value = compactForm.value.offsetHeight;
tableConfig.value.height = `calc(78vh - ${heightForm.value}px)`; // 设置表格容器的高度
rightTableConfig.value.height = `calc(83vh - ${heightForm.value}px)`;
tableConfig.value.height = `calc(71vh - ${heightForm.value}px)`; // 设置表格容器的高度
rightTableConfig.value.height = `calc(71vh - ${heightForm.value}px)`;
}
}
@ -566,8 +615,11 @@ const getList = () => {
if (response.code == 0) {
loading.value = false;
tableData.value = response.data
getRightTable(response.data[0])
tableRef.value.setCurrentRow(response.data[0])
if (tableData.value.length > 0) {
rowHandle(response.data[0])
getRightTable(response.data[0])
tableRef.value.setCurrentRow(response.data[0])
}
}
})
}
@ -617,13 +669,77 @@ const printPdf = () => {
}
.right-table {
border: 1px solid #dcdfe6;
border: 1px solid #E1E9F7;
padding-top: 5px;
}
.title {
border-bottom: 1px solid #dcdfe6;
border-bottom: 1px solid #E1E9F7;
margin-bottom: 10px;
padding: 10px;
font-weight: 700;
}
.time-line-box {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: 10px;
background-color: #f0f3f8;
padding: 10px 40px;
}
.time-line-item {
display: flex;
align-items: center;
justify-content: space-around;
width: 16.6%;
// padding: 0 30px;
.timeline-content {
display: flex;
flex-direction: column;
align-items: center;
.index {
width: 40px;
height: 40px;
line-height: 40px;
background: #E1E9F7;
border-radius: 50%;
text-align: center;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 18px;
color: #4767A3;
}
.active {
background-color: #4096ff;
box-shadow: 0 0 0 4px rgba(64, 150, 255, 0.2);
color: #FFF;
}
.timeline-title {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 16px;
color: #4767A3;
}
.timeline-time {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 14px;
color: #4767A3;
}
}
.timg {
width: 50px;
}
}
</style>

View File

@ -4,7 +4,7 @@
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig" @row-click="rowHandle"
:enableColumnDrag="true" @column-drag-end="handleColumnDragEnd">
<template #jjzt="{ row }">
<el-checkbox v-model="row.jjzt" true-value="1" disabled />
<el-checkbox :model-value="row.jjzt == '1'" readonly />
</template>
<template #zt="{ row }">
{{ formatDict(row.zt, 'ST') }}
@ -48,12 +48,7 @@ const props = defineProps({
})
watch(() => props.queryParams, (newValue) => {
console.log('==>', newValue);
nextTick(() => {
getInfoList()
})
}, { immediate: true })
// 解构 props
const { queryParams, dictData, } = toRefs(props);
@ -105,6 +100,10 @@ const getInfoList = () => {
})
}
watch(() => props.queryParams, (newValue) => {
getInfoList()
}, { immediate: true })
const getMxInfo = (row: any) => {
const data = {
yq: queryParams.value.yq,
@ -126,7 +125,6 @@ onMounted(() => {
const rowHandle = (row: any) => {
console.log(row)
getMxInfo(row)
}

View File

@ -1,6 +1,6 @@
<template>
<div>
临床意义
<div class="clinical_box">
<!-- 临床意义 -->
</div>
</template>
@ -8,4 +8,9 @@
</script>
<style scoped></style>
<style scoped lang="scss">
.clinical_box {
height: 82vh;
overflow-y: auto;
}
</style>

View File

@ -1,11 +1,66 @@
<template>
<div>
组合项目
<div class="bination_box">
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig">
<template #lxsr="{ row }">
<el-checkbox :model-value="row.lxsr == '1'" readonly />
</template>
<template #zdsr="{ row }">
<el-checkbox :model-value="row.zdsr == '1'" readonly />
</template>
</CustomTable>
</div>
</template>
<script setup lang="ts">
import { ref, watch, toRefs, onMounted, computed } from 'vue'
import { inputmdl } from "@/api/liswork/work/LisWork";
import CustomTable from '@/components/elTable/index.vue'
const props = defineProps({
// 主键
queryParams: {
type: Object,
default: {}
},
})
const { queryParams } = toRefs(props);
const tableData = ref([])
const columns = ref([
{ prop: 'mbmc', label: '批输入模板', visible: true, align: 'center', },
{ prop: 'yblx', label: '标本类型', visible: true, align: 'center', width: 80 },
{ prop: 'lxsr', label: '连续输入', visible: true, align: 'center', width: 80, slot: 'lxsr' },
{ prop: 'zdsr', label: '自动输入', visible: true, align: 'center', width: 80, slot: 'zdsr' },
{ prop: 'qsybh', label: '自动起始号', visible: true, align: 'center', width: 100 },
{ prop: 'jsybh', label: '自动结束号', visible: true, align: 'center', width: 100 },
{ prop: 'srbh', label: '输入编号', visible: true, align: 'center', width: 100 },
])
// 左侧表格配置
const tableConfig = ref({
border: true, // 边框
height: '', // 高度
highlightCurrentRow: true, // 高亮当前行
})
const getCombination = () => {
inputmdl(queryParams.value).then((res: any) => {
if (res.code == 0) {
tableData.value = res.data || []
}
})
};
watch(() => props.queryParams, (newValue) => {
getCombination()
}, { immediate: true })
// 解构 props
</script>
<style scoped></style>
<style scoped lang="scss">
.bination_box {
height: 82vh;
overflow-y: auto;
}
</style>

View File

@ -78,7 +78,7 @@ const tableColumns = ref([
{ field: 'finish', title: '完', width: 35, align: 'center', slotName: 'finish', resizable: true },
{ field: 'jgbz', title: '审', width: 40, align: 'center', slotName: 'jgbz', resizable: true },
{ field: 'alarmflag', title: '警', width: 40, align: 'center', slotName: 'alarmflag', resizable: true },
{ field: 'brly', title: '类型', width: 50, align: 'center', slotName: 'brly', resizable: true },
{ field: 'brly', title: '类型', width: 70, align: 'center', slotName: 'brly', resizable: true },
{ field: 'autojgbz', title: '自', width: 40, align: 'center', slotName: 'autojgbz', resizable: true },
{ field: 'ybh', title: '样本号', width: 80, align: 'center', resizable: true },
{ field: 'brxm', title: '病人姓名', width: 80, align: 'center', resizable: true },

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="h_box">
<div>
<el-table :data="tableData" style="width: 100%" @row-click="handleRowClick" highlight-current-row border
max-height="350px">
@ -15,7 +15,7 @@
</template>
<script setup lang="ts">
import { onMounted, ref, toRefs, computed } from 'vue';
import { onMounted, ref, toRefs, computed, watch } from 'vue';
import * as echarts from 'echarts';
import { historyInfo } from '@/api/liswork/work/LisWork'
import { get } from '@vueuse/core';
@ -32,17 +32,16 @@ const props = defineProps({
const { queryParams } = toRefs(props);
const tableData = ref([])
const periods = ref<string[]>([])
onMounted(() => {
getList()
})
const getList = () => {
historyInfo(queryParams.value).then((res: any) => {
tableData.value = res.data
})
}
watch(() => props.queryParams, (newValue) => {
getList()
}, { immediate: true })
const handleRowClick = () => { }
const myEcharts = ref()
@ -144,4 +143,9 @@ const getEcharts = (data: any) => {
</script>
<style scoped></style>
<style scoped lang="scss">
.h_box {
height: 82vh;
overflow-y: auto;
}
</style>

View File

@ -47,11 +47,7 @@
<el-col :span="11">
<div>
<div class="tab-list">
<div :class="['text', activeTab == item.value ? 'active' : '']" v-for="item in tabList"
@click="activeTab = item.value">{{ item.label }}
</div>
</div>
<tabView :tabList="tabList" v-model:activeTab="activeTab" />
<div class="box-shadow">
<template v-if="activeTab == 1">
<div class="flex-between mb10">
@ -89,7 +85,7 @@
</template>
<!-- 结果复查 -->
<template v-if="activeTab == 4">
<Reexamination />
<Reexamination :queryParams="queryParams" />
</template>
<!-- 收费信息 -->
<template v-if="activeTab == 5">
@ -97,15 +93,19 @@
</template>
<!-- 组合项目 -->
<template v-if="activeTab == 6">
<Combination />
<Combination :queryParams="queryParams" />
</template>
<!-- 临床意义 -->
<template v-if="activeTab == 7">
<Clinical />
<Clinical :queryParams="queryParams" />
</template>
<!-- 样本流转 -->
<template v-if="activeTab == 8">
<Sample />
<Sample :queryParams="queryParams" :dictData="dictData" />
</template>
<!-- 结果图形 -->
<template v-if="activeTab == 9">
<ResultGraph :queryParams="queryParams" />
</template>
</div>
</div>
@ -122,6 +122,7 @@
import { ref, nextTick, onMounted, toRaw } from 'vue';
import LabPat from './components/labpat.vue';
import SelectTable from '@/components/SelectTable/index.vue';
import tabView from '@/components/tabViews/index.vue';
// @ts-ignore
import iFrame from "@/components/iFrame/index.vue";
// @ts-ignore
@ -140,14 +141,15 @@ import Charge from './charge/index.vue'
import Combination from './combination/index.vue'
import Clinical from './clinical/index.vue'
import Sample from './sample/index.vue'
import ResultGraph from './resultGraph/index.vue'
import { getNextNumber, getPreviousNumber } from "@/utils/getNextNumber";
import { get, set } from '@vueuse/core';
const previewShow = ref(false);
const lbFlag = ref(false);
const queryParams = ref({
jyrq: '2025-08-19',
yq: '46',
jyrq: '2025-08-20',
yq: '33',
ybh: '1',
instrGroup: '02',
problemId: 0,
@ -159,6 +161,7 @@ const wjs = ref(0)
const activeTab = ref(1)
const tabList = ref([
{ label: '标本列表', value: 1 },
{ label: '结果图形', value: 9 },
{ label: '历史对照', value: 2 },
{ label: '其他结果', value: 3 },
{ label: '结果复查', value: 4 },
@ -229,8 +232,10 @@ const instrGroupFields = ref([
const loadinstrGroupOptions = () => {
getComDicts({ zdlb: 'GROUP' }).then((res: any) => {
instrGroupOptions.value = res.data
instrGroupOptions.value.push({ zddh: 'ALL', zdmc: '所有仪器' })
queryParams.value.instrGroup = '02'
queryParams.value.yq = '46'
// queryParams.value.yq = '46'
queryParams.value.yq = '33'
getYqConfig()
});
}
@ -292,6 +297,7 @@ const handleQuery = () => {
queryParams.value.ybh = lastRow.value.ybh
return;
}
queryParams.value = { ...queryParams.value };
// 查找匹配的行
const matchedIndex = labPatList.value.findIndex((item: any) => item.ybh == queryParams.value.ybh);
const row = labPatList.value[matchedIndex]
@ -557,40 +563,7 @@ onMounted(async () => {
/* 保持与卡片默认一致的内边距 */
}
.tab-list {
display: flex;
justify-content: space-between;
margin-bottom: 1px;
border: 1px solid #fff;
border-radius: 12px;
background-color: #fff;
cursor: pointer;
.text {
font-weight: 400;
font-size: 16px;
color: #1F6DD3;
width: 110px;
height: 48px;
line-height: 48px;
text-align: center;
&:first-child {
border-radius: 12px 0 0 12px;
}
&:last-child {
border-radius: 0 12px 12px 0;
}
}
.active {
background: #e9f1fb;
color: #1F6DD3;
font-weight: 600;
font-family: SourceHanSansCN, SourceHanSansCN;
}
}
.button-group {
display: flex;

View File

@ -3,7 +3,7 @@
<el-row :gutter="5">
<el-col :span="8">
<el-table :data="tableData" style="width: 100%" @row-click="handleRowClick" highlight-current-row border
height="81vh" show-overflow-tooltip>
height="82vh" show-overflow-tooltip>
<el-table-column prop="yq" label="仪器名称" align="center" width="120">
<template #default="scope">
{{ formatyq(scope.row.yq) }}
@ -19,7 +19,7 @@
</el-col>
<el-col :span="16">
<el-table :data="tableData" style="width: 100%" @row-click="handleRowClick" highlight-current-row border
height="81vh">
height="82vh">
<el-table-column prop="xmmc" label="项目名称" fixed align="center" />
<template v-for="(period) in periods" :key="period">
<el-table-column :label="period" :prop="period" :width="150" align="center" />
@ -56,12 +56,15 @@ const props = defineProps({
const { queryParams, dictData, instrOptions } = toRefs(props);
const tableData = ref([]);
const periods = ref<string[]>([])
onMounted(() => {
const getOthersInfo = () => {
otherinstr(queryParams.value).then((res: any) => {
console.log(res)
tableData.value = res.data
})
})
}
watch(() => props.queryParams, (newValue) => {
getOthersInfo()
}, { immediate: true })
const handleRowClick = (row: any) => { }
@ -77,8 +80,9 @@ const formatyq = (v: string) => {
<style scoped lang="scss">
.others-box {
height: 81.5vh;
height: 82vh;
overflow-y: auto;
overflow-x: hidden;
:deep(.el-table__cell) {
padding: 0 !important;

View File

@ -1,11 +1,43 @@
<template>
<div>
结果复查
<div class="reex_box">
<!-- 结果复查 -->
</div>
</template>
<script setup lang="ts">
import { ref, watch, toRefs, onMounted, computed } from 'vue'
import { redoResult } from "@/api/liswork/work/LisWork";
const props = defineProps({
// 主键
queryParams: {
type: Object,
default: {}
},
})
// 解构 props
const { queryParams } = toRefs(props);
const getReexamination = () => {
redoResult(queryParams.value).then((res: any) => {
if (res.code == 0) {
}
})
};
watch(() => queryParams.value, () => {
getReexamination();
}, { immediate: true })
</script>
<style scoped></style>
<style scoped lang="scss">
.reex_box {
height: 82vh;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<div class="result_box">
<el-image v-for="url in resultImgs" class="img_box" :src="url" :max-scale="7" :min-scale="0.2"
:preview-src-list="resultImgs" show-progress :initial-index="4" />
</div>
</template>
<script setup lang="ts">
import { ref, watch, toRefs, onMounted, computed } from 'vue'
import { resultgraph } from "@/api/liswork/work/LisWork";
const props = defineProps({
// 主键
queryParams: {
type: Object,
default: {}
},
})
// 解构 props
const { queryParams } = toRefs(props);
const resultImgs = ref([])
const getResultGraph = () => {
resultgraph(queryParams.value).then((res: any) => {
if (res.code == 0) {
resultImgs.value = res.data?.map((item: any) => { return "data:image/png;base64," + item })
}
})
};
watch(() => props.queryParams, (newValue) => {
getResultGraph()
}, { immediate: true })
</script>
<style scoped lang="scss">
.result_box {
height: 82vh;
overflow-y: auto;
background-color: #fffbf0;
.img_box {
// box-shadow: ;
margin-right: 20px;
}
}
</style>

View File

@ -1,11 +1,401 @@
<template>
<div>
样本流转
<div class="report-container">
<div class="report-content">
<div class="timeline-container">
<div class="timeline-content">
<!-- 检验前 -->
<div class="box1">
<div class="title-tips">检验前</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">1</div>
<div class="timeline-item-box">
<div class="timeline-item-title">检验申请:{{ info.sqsj }} | {{ formatDict(info.sqys, 'SRD') }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">2</div>
<div class="timeline-item-box">
<div class="timeline-item-title">医嘱执行:{{ info.zxsj }} | {{ formatDict(info.zxys, 'SRD') }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">3</div>
<div class="timeline-item-box">
<div class="timeline-item-title">标本采集:{{ info.cysj }} | {{ formatDict(info.cyr, 'SRD') }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">4</div>
<div class="timeline-item-box">
<div class="timeline-item-title">标本送检:{{ info.bbsjsj }} | {{ formatDict(info.bbsjr, 'SRD') }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">5</div>
<div class="timeline-item-box">
<div class="timeline-item-title">标本接收:{{ info.jssj }} | {{ formatys(info.jsys) }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
</div>
<!-- 检验中 -->
<div class="box2">
<div class="title-tips">检验中</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">6</div>
<div class="timeline-item-box">
<div class="timeline-item-title">上机检验:{{ info.sjsj }}| {{ formatys(info.cp_jyz) }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">7</div>
<div class="timeline-item-box">
<div class="timeline-item-title">结果审核:{{ info.confirmdt }} | {{ formatys(info.cp_hdz) }}</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
</div>
<!-- 检验后 -->
<div class="box3">
<div class="title-tips">检验后</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">8</div>
<div class="timeline-item-box">
<div class="timeline-item-title">标本存放: {{ info.inlibtime }} | {{ info.inlibman }} | {{ info.libpos }}
</div>
</div>
</div>
<div class="timeline-item-tail">
<img class="jimg" src="@/assets/images/xjt.png" alt="">
</div>
</div>
<div class="timeline-item">
<div class="timeline-item-content">
<div class="index">9</div>
<div class="timeline-item-box">
<div class="timeline-item-title">标本处理: {{ info.outlibtime }} | {{ info.outliman }} </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, toRefs, onMounted, computed } from 'vue'
import { reqinfo, } from "@/api/liswork/work/LisWork";
// @ts-ignore
import { listUser } from '@/api/system/user.js'
const props = defineProps({
// 主键
queryParams: {
type: Object,
default: {}
},
dictData: {
type: Object,
default: () => { }
},
})
// 解构 props
const { queryParams, dictData, } = toRefs(props);
const info: any = ref({})
const userList = ref<{ [key: string]: any }>([])
// 时间线数据
const timelineItems = ref(
[
{ title: '检验申请', name: '离散', time: '2025-09-02 08:37:23' },
{ title: '标本采集', time: '2025-09-02 09:15:47' },
{ title: '标本送检', time: '2025-09-02 10:03:12' },
{ title: '检验接收', time: '2025-09-02 10:45:36' },
{ title: '标本签收', time: '2025-09-02 10:55:36' },
{ title: '上机检验', time: '' },
{ title: '结果审核', time: '' },
]
);
// 计算最后一个有时间的节点索引
const lastTimeIndex = computed(() => {
const lastIndex = timelineItems.value.findLastIndex(item => item.time);
if (lastIndex === -1) {
return -1
} else {
return lastIndex;
}
});
const getInfo = () => {
reqinfo(queryParams.value).then((res: any) => {
info.value = res.data[0]
})
}
watch(() => props.queryParams, (newValue) => {
getInfo()
}, { immediate: true })
onMounted(() => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res: any) => {
userList.value = res.rows;
});
})
const formatDict = (v: string, dictType: string) => {
return dictData.value[dictType]?.find((item: any) => item.value == v)?.label || v
}
const formatys = (v: string) => {
return userList.value.find((item: any) => item.userName == v)?.nickName || v
}
</script>
<style scoped></style>
<style scoped>
.report-container {
height: 82vh;
overflow-y: auto;
}
/* 区块样式 */
.section {
margin-bottom: 10px;
border: 1px solid #e0e0e0;
}
.section-title {
background-color: #a6caf0;
padding: 3px 10px;
font-size: 14px;
font-weight: bold;
border-bottom: 1px solid #e0e0e0;
}
/* 信息行样式 */
.info-row {
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.info-label {
padding: 4px 10px;
background-color: #fafafa;
font-size: 13px;
color: #333;
text-align: left;
}
.info-value {
padding: 4px 10px;
font-size: 13px;
color: #666;
text-align: left;
word-break: break-all;
}
/* 签名区域样式 */
.signature-section {
border: 1px solid #e0e0e0;
}
.signature-content {
padding: 10px;
}
.signature-content pre {
margin: 0;
font-family: monospace;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-all;
}
.timeline-container {
width: 100%;
box-sizing: border-box;
}
.timeline-content {
width: 100%;
.timeline-item {
/* .img_box {
width: 52px;
} */
.jimg {
width: 52px;
height: 20px;
}
.timeline-item-content {
display: flex;
align-items: center;
.timeline-item-box {
display: flex;
align-items: center;
&>div {
margin-left: 20px;
}
}
.index {
width: 40px;
height: 40px;
line-height: 40px;
background: #E1E9F7;
border-radius: 50%;
text-align: center;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 18px;
color: #4767A3;
}
.active_bg {
background: #1F6DD3;
color: #fff;
}
.active_nbg {
background: #F6F8FB;
color: #9D9EA1;
}
.timeline-item-title {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 16px;
color: #4767A3;
}
.timeline-item-time {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 14px;
color: #4767A3;
}
.a_text {
color: #9D9EA1;
}
}
.timeline-item-tail {
height: 30px;
padding-left: 5px;
margin: 5px 0;
.jimg {
width: 30px;
height: 100%;
}
}
}
}
.box1 {
padding-left: 40px;
background-color: rgba(186, 198, 248, 0.5);
border-radius: 10px;
position: relative;
}
.box2 {
padding-left: 40px;
background-color: rgba(226, 157, 157, 0.5);
border-radius: 10px;
position: relative;
}
.box3 {
padding-left: 40px;
background-color: rgba(208, 241, 203, 0.5);
border-radius: 10px;
position: relative;
}
.title-tips {
writing-mode: vertical-rl;
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 5px;
color: #1f6dd3;
font-size: 20px;
font-family: SourceHanSansCN, SourceHanSansCN;
}
</style>

View File

@ -0,0 +1,257 @@
<template>
<div class="report-container">
<div class="report-content">
<!-- 检验前信息 -->
<div class="section">
<div class="section-title">检验前</div>
<el-row class="info-row">
<el-col :span="6" class="info-label">申请医生:</el-col>
<el-col :span="6" class="info-value">{{ formatDict(info.sqys, 'SRD') }}</el-col>
<el-col :span="6" class="info-label">申请时间:</el-col>
<el-col :span="6" class="info-value">{{ info.sqsj }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">医嘱执行:</el-col>
<el-col :span="6" class="info-value">{{ info.zxys }}</el-col>
<el-col :span="6" class="info-label">执行时间:</el-col>
<el-col :span="6" class="info-value">{{ info.zxsj }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">采 集 人:</el-col>
<el-col :span="6" class="info-value">{{ info.cyr }}</el-col>
<el-col :span="6" class="info-label">采样时间:</el-col>
<el-col :span="6" class="info-value">{{ info.cysj }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">标本送出人:</el-col>
<el-col :span="6" class="info-value">{{ info.bbsjr }}</el-col>
<el-col :span="6" class="info-label">标本送出时间:</el-col>
<el-col :span="6" class="info-value">{{ info.bbsjsj }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">标本签收人:</el-col>
<el-col :span="6" class="info-value">{{ formatys(info.jsys) }}</el-col>
<el-col :span="6" class="info-label">签收时间:</el-col>
<el-col :span="6" class="info-value">{{ info.jssj }}</el-col>
</el-row>
</div>
<!-- 检验中信息 -->
<div class="section">
<div class="section-title">检验中</div>
<el-row class="info-row">
<el-col :span="6" class="info-label">检验人员:</el-col>
<el-col :span="6" class="info-value">{{ info.cp_jyz }} </el-col>
<el-col :span="6" class="info-label">上机时间:</el-col>
<el-col :span="6" class="info-value"> </el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">审核人员:</el-col>
<el-col :span="6" class="info-value"> {{ info.cp_hdz }}</el-col>
<el-col :span="6" class="info-label">审核时间:</el-col>
<el-col :span="6" class="info-value"> </el-col>
</el-row>
</div>
<!-- 检验后信息 -->
<div class="section">
<div class="section-title">检验后</div>
<el-row class="info-row">
<el-col :span="6" class="info-label">标本存放人:</el-col>
<el-col :span="6" class="info-value">{{ info.inlibman }}</el-col>
<el-col :span="6" class="info-label">存放时间:</el-col>
<el-col :span="6" class="info-value">{{ info.inlibtime }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">存放位置:</el-col>
<el-col :span="6" class="info-value">{{ info.libpos }}</el-col>
<el-col :span="6" class="info-label">标本处理人:</el-col>
<el-col :span="6" class="info-value">{{ info.outlibman }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">标本处理时间:</el-col>
<el-col :span="18" class="info-value">{{ info.outlibtime }}</el-col>
</el-row>
</div>
<!-- 患者信息 -->
<div class="section">
<div class="section-title">患者信息</div>
<el-row class="info-row">
<el-col :span="6" class="info-label">病人电话:</el-col>
<el-col :span="6" class="info-value">{{ info.phone }}</el-col>
<el-col :span="6" class="info-label">联系人电话:</el-col>
<el-col :span="6" class="info-value">{{ info.phone1 }}</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">身份证号:</el-col>
<el-col :span="6" class="info-value">{{ info.sfzh }}</el-col>
<el-col :span="6" class="info-label">联系地址:</el-col>
<el-col :span="6" class="info-value">{{ info.addr }}</el-col>
</el-row>
</div>
<!-- 电子签名信息 -->
<div class="section">
<div class="section-title">电子签名信息</div>
<el-row class="info-row">
<el-col :span="6" class="info-label">签名证书: </el-col>
<el-col :span="6">
<el-button type="primary" plain size="small">验证签名</el-button>
<el-button type="primary" plain size="small">导出签名值</el-button>
</el-col>
<el-col :span="6">
</el-col>
<el-col :span="6">
</el-col>
</el-row>
<el-row class="info-row">
<el-col :span="6" class="info-label">签名时间:</el-col>
<el-col :span="18" class="info-value">{{ info.tss }} </el-col>
</el-row>
</div>
<!-- 电子签名值 -->
<div class="section signature-section">
<div class="section-title">电子签名值</div>
<div class="signature-content">
<el-scrollbar height="120px">
<pre>{{ info.snresult }} </pre>
</el-scrollbar>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, toRefs, onMounted } from 'vue'
import { reqinfo, } from "@/api/liswork/work/LisWork";
// @ts-ignore
import { listUser } from '@/api/system/user.js'
const props = defineProps({
// 主键
queryParams: {
type: Object,
default: {}
},
dictData: {
type: Object,
default: () => { }
},
})
watch(() => props.queryParams, (newValue) => {
console.log('==>', newValue);
}, { immediate: true })
// 解构 props
const { queryParams, dictData, } = toRefs(props);
const info: any = ref({})
const userList = ref<{ [key: string]: any }>([])
const getInfo = () => {
reqinfo(queryParams.value).then((res: any) => {
info.value = res.data[0]
})
}
getInfo()
onMounted(() => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res: any) => {
userList.value = res.rows;
});
})
const formatDict = (v: string, dictType: string) => {
return dictData.value[dictType]?.find((item: any) => item.value == v)?.label
}
const formatys = (v: string) => {
return userList.value.find((item: any) => item.userName == v)?.nickName
}
</script>
<style scoped>
.report-container {
height: 82vh;
}
/* 区块样式 */
.section {
margin-bottom: 10px;
border: 1px solid #e0e0e0;
}
.section-title {
background-color: #a6caf0;
padding: 3px 10px;
font-size: 14px;
font-weight: bold;
border-bottom: 1px solid #e0e0e0;
}
/* 信息行样式 */
.info-row {
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.info-label {
padding: 4px 10px;
background-color: #fafafa;
font-size: 13px;
color: #333;
text-align: left;
}
.info-value {
padding: 4px 10px;
font-size: 13px;
color: #666;
text-align: left;
word-break: break-all;
}
/* 签名区域样式 */
.signature-section {
border: 1px solid #e0e0e0;
}
.signature-content {
padding: 10px;
}
.signature-content pre {
margin: 0;
font-family: monospace;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-all;
}
/* 响应式调整 */
@media (max-width: 768px) {
.vertical-text {
display: none;
}
.report-content {
margin-left: 0;
}
.info-label,
.info-value {
font-size: 12px;
padding: 6px 8px;
}
}
</style>