|
|
@ -1,15 +1,46 @@ |
|
|
|
<template> |
|
|
|
<div id="regular-container"> |
|
|
|
<!-- 添加加载蒙层 --> |
|
|
|
<!-- 加载蒙层 --> |
|
|
|
<div class="loading-overlay" v-if="isLoading"> |
|
|
|
<div class="loading-content"> |
|
|
|
<div class="loading-spinner"></div> |
|
|
|
<span class="loading-text">正在加载耗材...</span> |
|
|
|
<div class="loading-progress"> |
|
|
|
<div class="progress-bar" :style="{ width: `${loadingProgress}%` }"></div> |
|
|
|
<span class="loading-text">正在扫描耗材...</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<!-- 扫描结果弹窗 --> |
|
|
|
<div v-if="showScanResults" class="scan-results-overlay"> |
|
|
|
<div class="scan-results-container"> |
|
|
|
<div class="scan-results-header"> |
|
|
|
<span class="title">扫描结果</span> |
|
|
|
</div> |
|
|
|
<div class="scan-results-content"> |
|
|
|
<table class="scan-results-table"> |
|
|
|
<thead> |
|
|
|
<tr> |
|
|
|
<th>通道</th> |
|
|
|
<th>项目名称</th> |
|
|
|
<th>批次号</th> |
|
|
|
<th>状态</th> |
|
|
|
</tr> |
|
|
|
</thead> |
|
|
|
<tbody> |
|
|
|
<tr v-for="report in formattedReports" :key="report.channel" :class="{ 'error-row': report.isError }"> |
|
|
|
<td>通道{{ report.channel + 1 }}</td> |
|
|
|
<td>{{ report.projName || '--' }}</td> |
|
|
|
<td>{{ report.lotId || '--' }}</td> |
|
|
|
<td :class="report.isError ? 'error-text' : 'success-text'"> |
|
|
|
{{ report.message }} |
|
|
|
</td> |
|
|
|
</tr> |
|
|
|
</tbody> |
|
|
|
</table> |
|
|
|
</div> |
|
|
|
<div class="scan-results-footer"> |
|
|
|
<button class="confirm-btn" @click="handleConfirmScan">确认</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!--耗材页面 --> |
|
|
|
<div class="main-top"> |
|
|
|
<div class="plate-area"> |
|
|
@ -25,7 +56,7 @@ |
|
|
|
<!--缓冲液区域小--> |
|
|
|
<div class="buffer-little-title"> |
|
|
|
<div class="line"></div> |
|
|
|
<div class="content">缓冲液区(小)</div> |
|
|
|
<div class="content">缓冲液(小)</div> |
|
|
|
</div> |
|
|
|
<div class="ball-area"> |
|
|
|
<MainComponent v-for="item in bufferLittles" :key="item" class="ball-grid" :projectName="item.projShortName" |
|
|
@ -38,7 +69,7 @@ |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
import { MoveLiquidArea, SpttingPlates, MainComponent } from '../components' |
|
|
|
import { ref, onMounted, onActivated, onBeforeUnmount } from 'vue' |
|
|
|
import { ref, onMounted, onActivated, onBeforeUnmount, watch } from 'vue' |
|
|
|
import { scanConsumables, updateTipsNum } from '../../../services/Index/index' |
|
|
|
import { useConsumablesStore, useEmergencyStore } from '../../../store' |
|
|
|
import { useDeviceStore } from '../../../store/index' |
|
|
@ -51,6 +82,7 @@ import { |
|
|
|
import { createWebSocket } from '../../../websocket/socket' |
|
|
|
import type { ConsumablesStateMessage, SensorStateMessage, EmergencyPosStateMessage } from '../../../websocket/socket'; |
|
|
|
import { getServerInfo } from '../../../utils/getServerInfo' |
|
|
|
import { formatScanReports } from '../../../utils/errorHandler' |
|
|
|
import { ElMessage } from 'element-plus' |
|
|
|
const { wsUrl } = getServerInfo('/api/v1/app/ws/state') |
|
|
|
const socket = createWebSocket(wsUrl) |
|
|
@ -62,8 +94,6 @@ const deviceStore = useDeviceStore() |
|
|
|
const currentTemperature = ref(40); |
|
|
|
// 废料区状态 |
|
|
|
const wasteStatus = ref(false) |
|
|
|
// 添加加载进度状态 |
|
|
|
const loadingProgress = ref(0) |
|
|
|
// 父组件状态 |
|
|
|
const isLoad = ref(false) |
|
|
|
const isLoading = ref(false) |
|
|
@ -84,6 +114,17 @@ const moveLiquids = ref<LiquidState[]>([ |
|
|
|
tipNum: 0, |
|
|
|
}, |
|
|
|
]) |
|
|
|
// 新增状态 |
|
|
|
const showScanResults = ref(false) |
|
|
|
interface ScanReport { |
|
|
|
channel: number |
|
|
|
code: string |
|
|
|
message: string |
|
|
|
isError: boolean |
|
|
|
projName: string | null |
|
|
|
lotId: string | null |
|
|
|
} |
|
|
|
const formattedReports = ref<ScanReport[]>([]) |
|
|
|
//是否加载 |
|
|
|
const isAlreadyLoad = ref(false) |
|
|
|
// 临时状态管理小球激活数量 |
|
|
@ -143,32 +184,18 @@ const bufferLittles = ref<BufferLittle[]>([ |
|
|
|
const bufferBig = ref<BottleGroup[]>([]) |
|
|
|
//急诊区状态 |
|
|
|
const emergencyInfo = ref(emergencyStore.$state.emergencyInfo || {}) |
|
|
|
// 获取耗材数据 |
|
|
|
const GetLoadConsumables = async () => { |
|
|
|
isLoading.value = true |
|
|
|
try { |
|
|
|
const res = await scanConsumables() |
|
|
|
moveLiquids.value = res.data.consumableState.tips |
|
|
|
plates.value = res.data.consumableState.reactionPlateGroup |
|
|
|
bufferLittles.value = res.data.consumableState.littBottleGroup |
|
|
|
bufferBig.value = res.data.consumableState.larBottleGroup |
|
|
|
tempTipNum.value = [...moveLiquids.value.map((liquid) => liquid.tipNum)] |
|
|
|
isLoad.value = true |
|
|
|
isLoading.value = false |
|
|
|
isAlreadyLoad.value = true |
|
|
|
consumableStore.setConsumablesData(res.data.consumableState) |
|
|
|
} catch (error) { |
|
|
|
// 加载失败 |
|
|
|
isLoading.value = false |
|
|
|
//是否处理扫描结果 |
|
|
|
const isHandleScan = ref(false) |
|
|
|
// 确认扫描结果 |
|
|
|
const handleConfirmScan = () => { |
|
|
|
showScanResults.value = false |
|
|
|
isHandleScan.value = true |
|
|
|
if (formattedReports.value.some(report => report.isError)) { |
|
|
|
isLoad.value = false |
|
|
|
isAlreadyLoad.value = false |
|
|
|
ElMessage.warning('存在错误,请检查耗材') |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
// const getEmergencyInfo = async () => { |
|
|
|
// const res = await isTubeExist() |
|
|
|
// emergencyInfo.value = res.data.tube |
|
|
|
// emergencyStore.setInfo(res.data.tube) |
|
|
|
// } |
|
|
|
//使用websocket保证数据的实时性 |
|
|
|
const startWebSocket = () => { |
|
|
|
socket.connect() |
|
|
@ -188,7 +215,7 @@ const handleSensorState = (data: SensorStateMessage['data']) => { |
|
|
|
|
|
|
|
//处理耗材状态 |
|
|
|
const handleConsumablesState = (data: ConsumablesStateMessage['data']) => { |
|
|
|
if (isAlreadyLoad.value) { |
|
|
|
if (isAlreadyLoad.value && isHandleScan.value) { |
|
|
|
consumableStore.setConsumablesData(data) |
|
|
|
moveLiquids.value = data.tips |
|
|
|
plates.value = data.reactionPlateGroup as ReactionPlate[] |
|
|
@ -236,40 +263,53 @@ onActivated(() => { |
|
|
|
} |
|
|
|
}) |
|
|
|
// 子组件传值 |
|
|
|
//存储耗材数据 |
|
|
|
const consumablesData = ref<ConsumablesStateMessage['data'] | null>(null) |
|
|
|
// 修改加载耗材的处理函数 |
|
|
|
const handleIsLoad = async () => { |
|
|
|
isLoading.value = true |
|
|
|
loadingProgress.value = 0 |
|
|
|
|
|
|
|
// 创建进度动画 |
|
|
|
const startTime = Date.now() |
|
|
|
const duration = 3000 // 30秒 |
|
|
|
try { |
|
|
|
const res = await scanConsumables() |
|
|
|
isLoading.value = false |
|
|
|
|
|
|
|
const updateProgress = () => { |
|
|
|
const elapsed = Date.now() - startTime |
|
|
|
const progress = Math.min((elapsed / duration) * 100, 100) |
|
|
|
loadingProgress.value = progress |
|
|
|
// 格式化扫描结果 |
|
|
|
formattedReports.value = formatScanReports(res.data.scanReports) |
|
|
|
console.log('🚀 ~ handleIsLoad ~ formattedReports:', formattedReports.value) |
|
|
|
// 显示扫描结果弹窗 |
|
|
|
showScanResults.value = true |
|
|
|
consumablesData.value = res.data.consumableState |
|
|
|
// 更新状态 |
|
|
|
if (res.data.consumableState) { |
|
|
|
|
|
|
|
if (progress < 100) { |
|
|
|
requestAnimationFrame(updateProgress) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
requestAnimationFrame(updateProgress) |
|
|
|
|
|
|
|
// 等待3秒 |
|
|
|
await new Promise(resolve => setTimeout(resolve, duration)) |
|
|
|
|
|
|
|
// 发送请求 |
|
|
|
try { |
|
|
|
await GetLoadConsumables() |
|
|
|
} catch (error) { |
|
|
|
console.error('加载耗材失败:', error) |
|
|
|
} finally { |
|
|
|
isLoading.value = false |
|
|
|
loadingProgress.value = 0 |
|
|
|
isAlreadyLoad.value = false |
|
|
|
ElMessage.error('加载耗材失败') |
|
|
|
} |
|
|
|
} |
|
|
|
watch(isHandleScan, (newVal) => { |
|
|
|
if (newVal) { |
|
|
|
// 确认后更新状态 |
|
|
|
if (consumablesData.value) { |
|
|
|
moveLiquids.value = consumablesData.value.tips |
|
|
|
plates.value = consumablesData.value.reactionPlateGroup as ReactionPlate[] |
|
|
|
bufferLittles.value = consumablesData.value.littBottleGroup as BufferLittle[] |
|
|
|
bufferBig.value = consumablesData.value.larBottleGroup as BottleGroup[] |
|
|
|
tempTipNum.value = [...moveLiquids.value.map((liquid) => liquid.tipNum)] |
|
|
|
isLoad.value = true |
|
|
|
isAlreadyLoad.value = true |
|
|
|
consumableStore.setConsumablesData(consumablesData.value) |
|
|
|
// 清空临时数据 |
|
|
|
consumablesData.value = null |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 取消时清空临时数据 |
|
|
|
consumablesData.value = null |
|
|
|
} |
|
|
|
}) |
|
|
|
const handleIsUnload = () => { |
|
|
|
isLoad.value = !isLoad.value |
|
|
|
isLoading.value = false |
|
|
@ -326,13 +366,13 @@ const handleIsUnload = () => { |
|
|
|
bufferBig.value = [] |
|
|
|
} |
|
|
|
const updateTipNum = async ({ index, tipNum }: { index: number; tipNum: number }) => { |
|
|
|
// 更新临时状态 |
|
|
|
// 新临时状态 |
|
|
|
tempTipNum.value[index] = tipNum |
|
|
|
console.log('🚀 ~ updateTipNum ~ tempTipNum:', tempTipNum.value) |
|
|
|
//调用接口 |
|
|
|
try { |
|
|
|
if (deviceStore.status === 'IDLE') { |
|
|
|
await updateTipsNum({ group: `GROUP${index}`, num: tipNum }) |
|
|
|
await updateTipsNum({ group: `CG`${ index }`, num: tipNum }) |
|
|
|
} else { |
|
|
|
ElMessage.error('设备正在工作,无法修改数值') |
|
|
|
} |
|
|
@ -360,7 +400,7 @@ const updateTipNum = async ({ index, tipNum }: { index: number; tipNum: number } |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
z-index: 9999; |
|
|
|
z-index: 9998; |
|
|
|
} |
|
|
|
|
|
|
|
.loading-content { |
|
|
@ -458,5 +498,95 @@ const updateTipNum = async ({ index, tipNum }: { index: number; tipNum: number } |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.scan-results-overlay { |
|
|
|
position: fixed; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
width: 100vw; |
|
|
|
height: 100vh; |
|
|
|
background: rgba(0, 0, 0, 0.7); |
|
|
|
backdrop-filter: blur(4px); |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
z-index: 10000; |
|
|
|
pointer-events: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.scan-results-container { |
|
|
|
background: white; |
|
|
|
border: 2px solid #ff0000; |
|
|
|
border-radius: 8px; |
|
|
|
width: 800px; |
|
|
|
padding: 20px; |
|
|
|
position: relative; |
|
|
|
z-index: 10001; |
|
|
|
pointer-events: auto; |
|
|
|
|
|
|
|
.scan-results-header { |
|
|
|
text-align: center; |
|
|
|
margin-bottom: 20px; |
|
|
|
|
|
|
|
.title { |
|
|
|
color: #ff0000; |
|
|
|
font-size: 32px; |
|
|
|
font-weight: bold; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.scan-results-content { |
|
|
|
.scan-results-table { |
|
|
|
width: 100%; |
|
|
|
border-collapse: collapse; |
|
|
|
margin: 20px 0; |
|
|
|
|
|
|
|
th, |
|
|
|
td { |
|
|
|
padding: 15px; |
|
|
|
text-align: left; |
|
|
|
font-size: 24px; |
|
|
|
border-bottom: 1px solid #eee; |
|
|
|
} |
|
|
|
|
|
|
|
th { |
|
|
|
color: #ff0000; |
|
|
|
font-weight: bold; |
|
|
|
background-color: #fff; |
|
|
|
} |
|
|
|
|
|
|
|
.error-row { |
|
|
|
background-color: rgba(255, 0, 0, 0.05); |
|
|
|
} |
|
|
|
|
|
|
|
.error-text { |
|
|
|
color: #ff0000; |
|
|
|
} |
|
|
|
|
|
|
|
.success-text { |
|
|
|
color: #4caf50; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.scan-results-footer { |
|
|
|
margin-top: 20px; |
|
|
|
text-align: center; |
|
|
|
|
|
|
|
.confirm-btn { |
|
|
|
background: #ff0000; |
|
|
|
color: white; |
|
|
|
border: none; |
|
|
|
border-radius: 25px; |
|
|
|
padding: 10px 40px; |
|
|
|
font-size: 24px; |
|
|
|
cursor: pointer; |
|
|
|
|
|
|
|
&:hover { |
|
|
|
background: darken(#ff0000, 10%); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |