Browse Source

完善耗材页面急诊

relver
zhangjiming 7 months ago
parent
commit
f52a1a84b2
  1. 11
      src/pages/Index/Index.vue
  2. 9
      src/pages/Index/Regular/Emergency.vue
  3. 38
      src/pages/Index/Regular/Running.vue
  4. 287
      src/pages/Index/components/Consumables/MoveLiquidArea.vue
  5. 5
      src/store/modules/consumables.ts
  6. 24
      src/store/modules/device.ts
  7. 36
      src/store/modules/emergency.ts
  8. 1
      src/store/modules/settingTestTube.ts
  9. 7
      src/store/modules/testTube.ts
  10. 17
      src/websocket/socket.ts

11
src/pages/Index/Index.vue

@ -237,6 +237,7 @@ import { User } from '../../types/Index'
import {
useConsumablesStore,
useDeviceStore,
useEmergencyStore,
useSettingTestTubeStore,
useTestTubeStore,
} from '../../store'
@ -245,6 +246,7 @@ import type {
AppEventMessage,
ConsumablesStateMessage,
DeviceWorkStateMessage,
EmergencyPosStateMessage,
FooterMessageState,
IncubationPlateStateMessage,
OptScanModuleStateMessage,
@ -273,6 +275,7 @@ const deviceStore = useDeviceStore()
const runningStore = useRunningStore()
const settingTubeStore = useSettingTestTubeStore()
const tubeRackStore = useTestTubeStore()
const emergencyStore = useEmergencyStore()
//
const user = ref<User>(
@ -393,6 +396,10 @@ const handleConsumablesState = (data: ConsumablesStateMessage['data']) => {
consumableStore.setConsumablesData(data)
}
const handleEmergencyPosState = (data: EmergencyPosStateMessage['data']) => {
emergencyStore.setInfo(data.tube)
}
const getProjectList = async () => {
const res = await getProjectInfo()
if (res.success) {
@ -436,6 +443,10 @@ onMounted(() => {
'OptScanModuleState',
handleOptScanModuleStateMessage,
)
wsState.subscribe<EmergencyPosStateMessage>(
'EmergencyPosState',
handleEmergencyPosState,
)
wsState.connect()
getProjectList()

9
src/pages/Index/Regular/Emergency.vue

@ -190,10 +190,13 @@ const selectBloodType = (item: { key: BloodType }) => {
//
onMounted(() => {
if (!emergencyStore.emergencyInfo) {
return
}
if (
deviceStore.deviceState.workState === 'PAUSE' &&
emergencyStore.emergencyInfo &&
emergencyStore.emergencyInfo!.state === 'RESOURCE_IS_READY'
deviceStore.deviceState.workState === 'IDLE' ||
(deviceStore.deviceState.workState === 'PAUSE' &&
emergencyStore.emergencyInfo!.state === 'TO_BE_PROCESSED')
) {
emergencyPosition.value = {
sampleBarcode: emergencyStore.emergencyInfo.sampleBarcode,

38
src/pages/Index/Regular/Running.vue

@ -19,7 +19,7 @@
<!-- 光学模组 -->
<div class="scan-main">
<span style="font-weight: 600; font-size: 18px; display: block">
光学模组
检测模组
</span>
<div
v-if="
@ -53,7 +53,7 @@
style="border-radius: 50%"
/>
<span v-if="runningStore.optScanModuleState?.state === 'SCANNING'"
>扫描</span
>检测</span
>
<span v-else>{{ runningStore.optScanModuleState.userid }}</span>
<span style="font-size: 14px">{{
@ -88,9 +88,9 @@
>
<span>急诊</span>
</div>
<div class="emergency-tube" :class="{ config: !canSetEmergency }">
<div class="emergency-tube">
<tube-item
:tube="canSetEmergency ? undefined : emergencyStore.emergencyInfo"
:tube="showEmergencyTube ? emergencyStore.emergencyInfo : undefined"
:showNum="false"
/>
</div>
@ -186,11 +186,34 @@ const settingTubeStore = useSettingTestTubeStore()
const emergencyStore = useEmergencyStore() //
const router = useRouter()
const showEmergencyTube = computed(() => {
if (!emergencyStore.emergencyInfo) {
return false
}
return (
emergencyStore.emergencyInfo.projIds.length > 0 &&
!(
emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE' ||
emergencyStore.emergencyInfo.state === 'ERROR'
)
)
})
const canSetEmergency = computed(() => {
if (!emergencyStore.emergencyInfo) {
return false
}
return (
emergencyStore.emergencyInfo &&
(emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE')
deviceStore.deviceState.workState === 'IDLE' ||
(deviceStore.deviceState.workState === 'WORKING' &&
(emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE' ||
emergencyStore.emergencyInfo.state === 'ERROR')) ||
(deviceStore.deviceState.workState === 'PAUSE' &&
(emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'TO_BE_PROCESSED' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE' ||
emergencyStore.emergencyInfo.state === 'ERROR'))
)
})
//
@ -280,7 +303,6 @@ watch(
.emergency-button {
width: 80px;
height: 80px;
background: #ff6b6b;
border-radius: 20px;
display: flex;
align-items: center;

287
src/pages/Index/components/Consumables/MoveLiquidArea.vue

@ -80,21 +80,22 @@
</div>
<div class="emergency-controller">
<div class="controller">
<div
class="emergency-ball"
:class="{ active: isActive }"
@click="showEmergencyInfo(emergencyStore.emergencyInfo)"
>
1
</div>
<span class="emergency-desc">
{{
emergencyStore.emergencyInfo
? emergencyStateDesc[emergencyStore.emergencyInfo.state] ||
'未知'
: '--'
}}
</span>
</div>
<button
class="controller-button"
@click="addEmergency"
:style="`background-color:${emergencyStatus ? '#c84141' : '#b4b4b4'}`"
:disabled="!emergencyStatus"
:style="`background-color:${canSetEmergency ? '#c84141' : '#b4b4b4'}`"
:disabled="!canSetEmergency"
>
添加急诊
急诊
</button>
</div>
</div>
@ -106,7 +107,7 @@
</div>
<button
class="id-button"
@click="openTableModal"
@click="openIdCardTableModal"
:disabled="!consumableStore.isIdCardInserted"
>
<span class="button-text">
@ -136,20 +137,6 @@
</button>
</div>
</div>
<InitWarn v-if="showEmergencyModal" :visible="showEmergencyModal" title="检查耗材" message="请将耗材加载完整后再添加"
icon="/src/assets/update-pin-icon.svg" confirmText="确认" @confirm="showEmergencyModal = false" />
<InitWarn
v-if="showModal"
:visible="showModal"
title="提示"
message="请及时清理废料箱"
icon="/src/assets/Warn.svg"
confirmText="确定"
@close="showModal = false"
@confirm="handleConfirm"
/>
<!--缓冲液大-->
<div class="buffer-area">
<div class="buffer-title">
@ -170,40 +157,57 @@
</div>
</div>
</div>
<IdCardInfo v-model="visible" v-if="visible"></IdCardInfo>
<InitWarn
v-if="showEmergencyModal"
:visible="showEmergencyModal"
title="检查耗材"
message="请将耗材加载完整后再添加"
icon="/src/assets/update-pin-icon.svg"
confirmText="确认"
@confirm="showEmergencyModal = false"
/>
<InitWarn
v-if="showWasteAlertModal"
:visible="showWasteAlertModal"
title="提示"
message="请及时清理废料箱"
icon="/src/assets/Warn.svg"
confirmText="确定"
@close="showWasteAlertModal = false"
@confirm="handleConfirm"
/>
<IdCardInfo
v-model="idCardListDialogVisible"
v-if="idCardListDialogVisible"
></IdCardInfo>
</template>
<script setup lang="ts">
import BallGrid from './BallGrid.vue'
import BallGrid2 from './BallGrid2.vue'
import IdCardInfo from './IdCardInfo.vue'
import { ref, watch, reactive, onMounted } from 'vue'
import { ref, watch, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useEmergencyStore, useConsumablesStore, useDeviceStore } from '../../../../store'
import {
useEmergencyStore,
useConsumablesStore,
useDeviceStore,
} from '../../../../store'
import { LiquidState } from '../../../../types/Index/index'
import wasteFullIcon from '@/assets/Index/waste-full.svg'
import wasteIcon from '@/assets/Index/waste.svg'
import type {
EmergencyPosStateMessage,
LargeBottleGroup,
import {
emergencyStateDesc,
type EmergencyPosStateMessage,
type LargeBottleGroup,
} from '@/websocket/socket'
import { createWebSocket } from '@/websocket/socket'
import { isTubeExist } from '@/services/Index/index'
import { InitWarn } from './Warn'
import { getServerInfo } from '@/utils/getServerInfo'
import { EMERGENCY_STATE } from "@/constant"
const { wsUrl } = getServerInfo('/api/v1/app/ws/state')
const socket = createWebSocket(wsUrl)
const emergencyStore = useEmergencyStore()
const consumableStore = useConsumablesStore()
const deviceStore = useDeviceStore();
const deviceStore = useDeviceStore()
const visible = ref(false)
const openTableModal = () => {
visible.value = true
}
//
const props = defineProps({
moveLiquids: {
@ -225,6 +229,29 @@ const emit = defineEmits([
'updateTipNum',
])
const idCardListDialogVisible = ref(false)
const openIdCardTableModal = () => {
idCardListDialogVisible.value = true
}
const canSetEmergency = computed(() => {
if (!emergencyStore.emergencyInfo) {
return false
}
return (
deviceStore.deviceState.workState === 'IDLE' ||
(deviceStore.deviceState.workState === 'WORKING' &&
(emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE' ||
emergencyStore.emergencyInfo.state === 'ERROR')) ||
(deviceStore.deviceState.workState === 'PAUSE' &&
(emergencyStore.emergencyInfo.state === 'EMPTY' ||
emergencyStore.emergencyInfo.state === 'TO_BE_PROCESSED' ||
emergencyStore.emergencyInfo.state === 'PROCESS_COMPLETE' ||
emergencyStore.emergencyInfo.state === 'ERROR'))
)
})
let longPressTimer: ReturnType<typeof setTimeout> | null = null
const handleTouchStart = () => {
longPressTimer = setTimeout(() => {
@ -240,11 +267,9 @@ const handleTouchEnd = () => {
}
}
//
// const workState = ref(deviceStore.deviceState.workState)
let showModal = ref(false)
let showWasteAlertModal = ref(false)
const handleConfirm = () => {
showModal.value = false
showWasteAlertModal.value = false
}
//
const wasteStatus = ref(props.wasteStatus)
@ -252,148 +277,31 @@ watch(
() => props.wasteStatus,
(newVal) => {
wasteStatus.value = newVal
if (newVal) showModal.value = true
if (newVal) showWasteAlertModal.value = true
},
{ immediate: true, deep: true },
)
//使websocket
const startWebSocket = () => {
socket.connect()
}
const emergencyStateList = [
'EMPTY',
'TO_BE_PROCESSED',
'PROCESS_COMPLETE',
'ERROR',
] //EMPTY,PROCESS_COMPLETE,ERROR
onMounted(() => {
//
getEmergencyStatus()
startWebSocket()
socket.subscribe<EmergencyPosStateMessage>(
'EmergencyPosState',
handleEmergencyPosState,
)
})
let emergencyStatus = ref(false)
if (deviceStore.deviceState.workState === 'WORKING') {
emergencyStatus.value = false;
}
//
const handleEmergencyPosState = (data: EmergencyPosStateMessage['data']) => {
// data.tube.state = 'PROCESSING'
if (deviceStore.deviceState.workState === 'WORKING') {
emergencyStatus.value = false;
return;
}
let { state } = data.tube
// EMPTYTO_BE_PROCESSEDPROCESS_COMPLETEERROR
emergencyStore.setInfo(data.tube)
if(emergencyStateList.includes(state)){
emergencyStatus.value = true;
}else{
emergencyStatus.value = false;
}
emergencyStore.setInfo(data.tube)
//"PROCESS_COMPLETE",
if (state === 'PROCESS_COMPLETE') {
socket.unsubscribe<EmergencyPosStateMessage>(
'EmergencyPosState',
unSubEmergencyPosState,
)
}
}
//
const unSubEmergencyPosState = () => {}
//
const getEmergencyStatus = () => {
isTubeExist().then((res) => {
console.log('这是急诊位状态=1=', res)
if (res && res.data) {
const data = res.data
const { tube } = data
if (tube && tube.state && emergencyStateList.includes(tube.state)) {
emergencyStatus.value = true
}
}
})
}
//
const activeTab = ref(0)
//
const isActive = ref(false)
watch(
() => props.emergencyInfo,
(newVal) => {
//
if (newVal && (newVal.state == EMERGENCY_STATE.EMPTY || newVal.state == EMERGENCY_STATE.ERROR)) {
isActive.value = false //
} else {//
isActive.value = true
}
},
{ immediate: true }, //
)
const emergencyInfo = reactive(emergencyStore.emergencyInfo || {})
const router = useRouter()
//
let showEmergencyModal = ref(false)
const addEmergency = () => {
//
if(!consumableStore.consumableData.reactionPlateGroup){
showEmergencyModal.value = true;
return
}
//
const clonereactionList = [...consumableStore.consumableData.reactionPlateGroup]
const hasRactionPlatGroup = clonereactionList.every(item => !item.projId)
if(hasRactionPlatGroup){
showEmergencyModal.value = true;
return
}
//
if (emergencyStore.emergencyInfo?.pos) {
//便
router.push({
path: '/index/emergency',
query: { data: encodeURIComponent(JSON.stringify(props.emergencyInfo)) },
})
} else {
if (canSetEmergency) {
//
if (
consumableStore.consumableData.reactionPlateGroup.every(
(p) => !p.isInstall,
)
) {
showEmergencyModal.value = true
return
}
router.push('/index/emergency')
}
}
//
const showEmergencyInfo = (item?: EmergencyPosStateMessage['data']['tube']) => {
console.log('回显急诊信息', item)
router.push({
path: '/index/emergency',
})
}
//
const activeEmergencyBalls = ref<{ [key: number]: boolean }>({})
//
const updateEmergencyBalls = () => {
activeEmergencyBalls.value[1] = Object.keys(emergencyInfo).length > 0
}
// emergencyInfo
watch(
() => emergencyInfo,
() => {
updateEmergencyBalls()
},
{ immediate: true, deep: true },
)
// remainingCount
const handleUpdateTipNum = (
remainingCount: number,
@ -463,7 +371,6 @@ const changePlate = (index: number) => {
}
.move-liquid-controller {
display: flex;
justify-content: space-between;
flex-wrap: nowrap;
@ -502,8 +409,8 @@ const changePlate = (index: number) => {
color: #e9f1ff;
/* 选中时文字颜色 */
.controller-number {
background-color: #3D7AF1;
border-color: #3D7AF1;
background-color: #3d7af1;
border-color: #3d7af1;
}
}
}
@ -577,21 +484,15 @@ const changePlate = (index: number) => {
background-color: #fff;
align-items: center;
.emergency-ball {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #d9d9d9;
align-items: center;
.emergency-desc {
width: 100%;
border-radius: 6px;
background-color: #ddd;
line-height: 40px;
font-weight: 700;
font-size: 30px;
color: #a1a1a1;
&.active {
background-color: red;
color: white;
}
font-weight: 600;
font-size: 24px;
color: #999;
text-align: center;
}
}

5
src/store/modules/consumables.ts

@ -151,9 +151,6 @@ export const useConsumablesStore = defineStore(
return idColorMap
})
//所有耗材都存在
let hasAllConsumables = ref(false)
return {
setIdCardInserted,
isIdCardInserted,
@ -168,8 +165,6 @@ export const useConsumablesStore = defineStore(
projectsAvailable,
projIdColorMap,
hasAllConsumables,
}
},
// {

24
src/store/modules/device.ts

@ -1,33 +1,43 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { DeviceWorkStateMessage, FooterMessageState, SensorStateMessage } from '../../websocket/socket'
export const useDeviceStore = defineStore('device', () => {
import * as R from 'ramda'
import type {
DeviceWorkStateMessage,
FooterMessageState,
SensorStateMessage,
} from '../../websocket/socket'
export const useDeviceStore = defineStore('device', () => {
const deviceState = ref<DeviceWorkStateMessage['data']>({
workState: 'IDLE',
pending: false,
} as DeviceWorkStateMessage['data'])
function setDeviceState(data: DeviceWorkStateMessage['data']) {
deviceState.value = data
if (!R.equals(data, deviceState.value)) {
deviceState.value = data
}
}
const sensorState = ref<SensorStateMessage['data']>({
pboxTemperature: 20,
incubateBoxTemperature: 20,
wasteBinFullFlag: false
wasteBinFullFlag: false,
})
const setSensorState = (data: SensorStateMessage['data']) => {
sensorState.value = data
if (!R.equals(data, sensorState.value)) {
sensorState.value = data
}
}
const messageState = ref<FooterMessageState['data']>({
topMessage: {
time: 0,
messageLevel: 'Info',
message: '空闲'
message: '空闲',
},
messageBoxList: []
messageBoxList: [],
})
const setMessageState = (data: FooterMessageState['data']) => {
messageState.value = data

36
src/store/modules/emergency.ts

@ -1,28 +1,22 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import * as R from 'ramda'
import type { EmergencyPosStateMessage } from '../../websocket/socket'
export const useEmergencyStore = defineStore(
'emergency',
() => {
let emergencyInfo = ref<
EmergencyPosStateMessage['data']['tube'] | undefined
>(undefined)
const setInfo = (data: EmergencyPosStateMessage['data']['tube']) => {
export const useEmergencyStore = defineStore('emergency', () => {
const emergencyInfo = ref<
EmergencyPosStateMessage['data']['tube'] | undefined
>(undefined)
const setInfo = (data: EmergencyPosStateMessage['data']['tube']) => {
if (!R.equals(data, emergencyInfo.value)) {
emergencyInfo.value = data
console.log('更新急诊:', data)
}
//卸载耗材
const unloadInfo = () => {
emergencyInfo.value = undefined
}
}
return {
emergencyInfo,
setInfo,
unloadInfo,
}
},
{
persist: true,
},
)
return {
emergencyInfo,
setInfo,
}
})

1
src/store/modules/settingTestTube.ts

@ -38,6 +38,7 @@ export const useSettingTestTubeStore = defineStore(
bloodTypes.value || [],
)
})
return {
bloodTypes,
setBloodTypes,

7
src/store/modules/testTube.ts

@ -1,6 +1,7 @@
import { TestTubeRack, TubeHolderSettingMessage } from '@/websocket/socket'
import { defineStore } from 'pinia'
import { ref } from 'vue'
import * as R from 'ramda'
export const useTestTubeStore = defineStore(
'testTube',
@ -14,7 +15,9 @@ export const useTestTubeStore = defineStore(
const tubeRacks = ref<TubeHolderSettingMessage['data']>([])
const setTubeRacks = (data: TubeHolderSettingMessage['data']) => {
tubeRacks.value = data
if (!R.equals(data, tubeRacks.value)) {
tubeRacks.value = data
}
}
return {
@ -22,7 +25,7 @@ export const useTestTubeStore = defineStore(
setTubeRack,
tubeRacks,
setTubeRacks
setTubeRacks,
}
},
{

17
src/websocket/socket.ts

@ -91,7 +91,17 @@ export type EmergencyTubeState =
| 'PROCESSING'
| 'PROCESS_COMPLETE'
| 'ERROR'
| '' //todo del
export const emergencyStateDesc = {
EMPTY: '空',
TO_BE_PROCESSED: '待处理',
PENDING: '已挂起',
RESOURCE_IS_READY: '已就绪',
PROCESSING: '处理中',
PROCESS_COMPLETE: '处理完成',
ERROR: '出错'
}
// 急诊位状态消息
interface EmergencyPosStateMessage extends BaseMessage {
type: 'EmergencyPosState'
@ -115,7 +125,6 @@ interface EmergencyPosStateMessage extends BaseMessage {
timestamp: number
}
export type RunningTubeState =
| 'EMPTY'
| 'TO_BE_PROCESSED'
@ -169,7 +178,7 @@ export interface TubeSetting {
export interface TestTubeRack {
uuid: string
state: 'INACTIVE' | 'ACTIVE' | 'LOCKED'
tubeSettings: TubeSetting[],
tubeSettings: TubeSetting[]
}
export interface TubeHolderSettingMessage extends BaseMessage {
@ -201,7 +210,7 @@ interface ProjectInfo {
}
export type SubTankState =
'EMPTY'
| 'EMPTY'
| 'RESERVED'
| 'WAITING_FOR_DROP'
| 'INCUBATING'

Loading…
Cancel
Save