|
|
<template> <div id="index-container"> <el-header class="nav-bar"> <div class="logo"> <img src="@/assets/Index/logo.svg" alt="" width="150px" /> </div> <div class="nav-tabs" @click="handleTabClick"> <!-- 使用 router-link 进行路由跳转 --> <router-link to="/index/regular" class="nav-tab" :class="{ active: selectedTab === '常规' }" data-tab="常规" >常规</router-link > <router-link to="/index/history" class="nav-tab" :class="{ active: selectedTab === '历史' }" data-tab="历史" >历史</router-link > <router-link to="/index/setting" class="nav-tab" :class="{ active: selectedTab === '设置' }" data-tab="设置" >设置</router-link > </div> <div v-if="deviceStore.deviceState.workState === 'IDLE'" class="test-control" > <!-- 显示开始测试按钮 --> <el-button type="primary" class="start-test" @click="startTest" >开始测试</el-button > </div> <div v-else class="test-control"> <!-- 显示暂停和停止按钮 --> <el-button type="warning" class="pause-test" @click="pauseTest" v-if="deviceStore.deviceState.workState !== 'PAUSE'" >暂停</el-button > <el-button type="success" class="continue-test" @click="continueTest" v-else >继续</el-button > <el-button type="danger" class="stop-test" @click="stopTest" >停止</el-button > </div> </el-header>
<!-- 间隔线 --> <div class="interval"> <div class="blue-line" :style="{ width: `${lineWidth}px`, left: `${lineLeft}px` }" ></div> </div>
<!-- 主内容区域 --> <main class="main-content"> <router-view v-slot="{ Component }"> <keep-alive :exclude="['TubeUserId']"> <component :is="Component" /> </keep-alive> </router-view> </main>
<!-- 底部操作信息 --> <el-footer class="footer-info"> <el-dropdown placement="top-start"> <div class="user-card"> <img class="user-logo" src="@/assets/Index/user.svg" /> <div class="user-name">操作人:{{ username || '未登录' }}</div> </div> <template #dropdown> <el-dropdown-menu> <el-dropdown-item> <img class="user-logo" src="@/assets/Index/user.svg" height="30"></img> <button class="logout" style="width:100px" @click="onLogout">注销</button> </el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown>
<div class="equipment-status"> <div class="status-text">系统:{{ EventText }}</div> </div> <div class="time-card"> <img class="time-logo" src="@/assets/Index/clock.svg" /> <div class="time-text"> <Time></Time> </div> </div> </el-footer> <InitWarn v-if="showModal" :visible="showModal" title="确认操作" message="确认试管架中的试管槽是否取下,避免自检/自动复位时对设备产生损坏!" icon="/src/assets/Warn.svg" cancelText="返回" confirmText="确认取下/开始复位" @close="showModal = false" @confirm="handleConfirm" /> <!-- 通用加载弹窗组件 --> <LoadingModal v-if="showDeviceResettingModal" :visible="showDeviceResettingModal" title="正在自检/自动复位中" message="请不要有任何手动操作!" cancelText="返回" confirmText="确认取下/开始复位" disableButtons @close="showDeviceResettingModal = false" /> <LoadingModal v-if="showDeviceWaitingModal" :visible="showDeviceWaitingModal" title="设备正在响应中" message="请不要有任何手动操作!" :showBtns="false" /> <!-- 自动自检已完成 --> <InitWarn v-if="showAlreadyModal" :visible="showAlreadyModal" title="确认操作" message="自检/自动复位已完成!" icon="/src/assets/OK.svg" cancelText="返回" confirmText="确认" @close="showAlreadyModal = false" @confirm="handleAlreadyConfirm" /> <!-- 自动自检失败 --> <InitWarn v-if="showFailModal" :visible="showFailModal" title="检测失败" :message="failMessage" icon="/src/assets/Warn.svg" cancelText="返回" confirmText="重试" @close="showFailModal = false" @confirm="startInit" /> <InitWarn v-if="idCardInserted" :visible="idCardInserted" title="检测到id卡插入" message="是否保存id卡信息" cancelText="返回" icon="/src/assets/update-pin-icon.svg" confirmText="确认保存" @close="idCardInserted = false" @confirm="saveIdInfo" /> <InitWarn v-if="showErrorModal" :visible="showErrorModal" title="错误提示" :message="ErrorMessage" icon="/src/assets/Warn.svg" cancelText="返回" confirmText="确认" @close="confirmError" @confirm="confirmError" /> <InitWarn v-if="showWarnModal" :visible="showWarnModal" title="注意" :message="WarnMessage" cancelText="返回" icon="/src/assets/update-pin-icon.svg" confirmText="确认" @close="confirmWarn" @confirm="confirmWarn" /> </div> </template> <script setup lang="ts"> import { useRouter } from 'vue-router' import { ref, onMounted, onBeforeUnmount } from 'vue' import { ElMessage } from 'element-plus' import { Time, InitWarn, LoadingModal } from './Components/Consumables' import { startWork, pauseWork, continueWork, stopWork, getDeviceWorkState, getInitState, initDevice, saveMountedCardInfo, openBuzzer, closeBuzzer, } from '../../services/index' import { User } from '../../types/Index' import { useConsumablesStore, useDeviceStore } from '../../store' import { createWebSocket } from '../../websocket/socket' import type { AppEventMessage } from '../../websocket/socket' import { getServerInfo } from '../../utils/getServerInfo' import { eventBus } from '../../eventBus' import { logout } from '@/services/Login/login' const selectedTab = ref(sessionStorage.getItem('selectedTab') || '常规') const lineWidth = ref(0) const lineLeft = ref(0) const showModal = ref(false) const showDeviceResettingModal = ref(false) const showDeviceWaitingModal = ref(false) const showAlreadyModal = ref(false) const showFailModal = ref(false) // const checkData = ref<CheckItem[]>([]);
// const failItems = ref<CheckItem[]>([]);
const consumableStore = useConsumablesStore() const deviceStore = useDeviceStore() // 新增的变量
const user = ref<User>( JSON.parse(sessionStorage.getItem('token') || '{}') as unknown as User, )
const username = ref<string>(user.value.account) const failMessage = ref('') // 存储动态生成的错误信息
const idCardInserted = ref(false) // id卡插入状态
//事件状态
const EventText = ref<string>('闲置...') const showWarnModal = ref(false) const ErrorMessage = ref<string>('') const showErrorModal = ref(false) const WarnMessage = ref<string>('')
// WebSocket 实例
const { wsUrl } = getServerInfo('/api/v1/app/ws/event')
const ws = createWebSocket(wsUrl)
// 处理应用事件消息
const handleAppEvent = (data: AppEventMessage['data']) => { console.log('🚀 ~ handleAppEvent ~ data:', data) if (data.typeName === 'AppPromptEvents' && data.prompt) { data.prompt.forEach(async (item) => { if (item.type === 'Error') { showErrorModal.value = true ErrorMessage.value = item.info await openBuzzer() } else if (item.type === 'Warn') { showWarnModal.value = true WarnMessage.value = item.info } }) } else if (data.typeName === 'AppIDCardMountEvent') { consumableStore.setIdCardInserted(true) idCardInserted.value = true EventText.value = 'id卡已插入' } else if (data.typeName === 'AppIDCardUnmountEvent') { consumableStore.setIdCardInserted(false) idCardInserted.value = false EventText.value = 'id卡已拔出' } else if (data.typeName === 'AppTubeholderSettingUpdateEvent') { EventText.value = '试管架配置更新' eventBus.emit('AppTubeSettingUpdateEvent') } else if (data.typeName === 'DoA8kStepActionEvent') { EventText.value = data.actionStepName! } else { EventText.value = '闲置...' } }
//注销用户
const router = useRouter() const onLogout = () => { logout().then(() => { router.push({ path: '/login', }) sessionStorage.setItem('token', '') }) }
//确认错误事件
const confirmError = async () => { showErrorModal.value = false //关闭蜂鸣器
await closeBuzzer() EventText.value = '闲置...' } //确认警告事件
const confirmWarn = async () => { showWarnModal.value = false } //保存id卡信息
const saveIdInfo = async () => { const res = await saveMountedCardInfo() if (res.success) { console.log('保存id卡信息成功') idCardInserted.value = false } } const showInitDeviceAlert = () => { showModal.value = true } onMounted(() => { eventBus.on('initDevice', showInitDeviceAlert) ws.connect() ws.subscribe<AppEventMessage>('AppEvent', handleAppEvent) })
onBeforeUnmount(() => { eventBus.off('initDevice', showInitDeviceAlert) ws.unsubscribe<AppEventMessage>('AppEvent', handleAppEvent) ws.disconnect() }) // 动态生成错误信息
// const generateErrorMessages = (data: CheckItem[]): string[] => {
// return data
// .filter(item => !item.pass)
// .map(item => `错误:${item.typechinfo} 检测未通过,请检查设备状态。`);
// };
const untilDeviceReady = async () => { showDeviceWaitingModal.value = true const res = await getDeviceWorkState() if (res.ecode === 'SUC') { if (res.data.pending) { setTimeout(async () => await untilDeviceReady(), 250) } else { showDeviceWaitingModal.value = false } } else { showDeviceWaitingModal.value = false } }
// 开始测试
const startTest = async () => { const res = await getInitState() if (res.ecode === 'SUC' && !res.data.deviceInited) { eventBus.emit('initDevice') return } try { showDeviceWaitingModal.value = true const res = await startWork() if (res.success) { await untilDeviceReady() } else { showDeviceWaitingModal.value = false ElMessage({ message: res.data.info, type: 'error', duration: 2000, }) } } catch (error) { console.error('开始测试失败:', error) } }
// 暂停测试
const pauseTest = async () => { showDeviceWaitingModal.value = true const res = await pauseWork() if (res.success) { await untilDeviceReady() } else { showDeviceWaitingModal.value = false ElMessage({ message: res.data.info, type: 'error', duration: 2000, }) } }
// 停止测试时清除标记
const stopTest = async () => { showDeviceWaitingModal.value = true const res = await stopWork() if (res.success) { await untilDeviceReady() } else { showDeviceWaitingModal.value = false ElMessage({ message: res.data.info, type: 'error', duration: 2000, }) } } //继续测试
const continueTest = async () => { showDeviceWaitingModal.value = true const res = await continueWork() if (res.success) { await untilDeviceReady() } else { showDeviceWaitingModal.value = false ElMessage({ message: res.data.info, type: 'error', duration: 2000, }) } } const handleConfirm = async () => { showModal.value = false // 关闭初始弹窗
await startInit() }
const startInit = async () => { await initDevice() await pollingInitState() }
const pollingInitState = async () => { showDeviceResettingModal.value = true // 显示 LoadingModal
const res = await getInitState() if (res.ecode === 'SUC') { if (res.data.isBusy) { setTimeout(async () => await pollingInitState(), 500) } else { showDeviceResettingModal.value = false if (res.data.passed) { console.log('初始化成功') sessionStorage.setItem('deviceResetFinished', 'true') showAlreadyModal.value = true } else { const infos = res.data.promopt.detailInfos failMessage.value = infos && infos.length > 0 ? infos.map((d: any) => d.name).join('\n') : res.data.promopt.info showFailModal.value = true // 显示失败弹窗
} } } }
// const checkIfResetCompleted = async () => {
// if (showFailModal.value) {
// showFailModal.value = false;
// }
// showLoadingModal.value = true; // 显示 LoadingModal
// // 模拟 2 秒延时
// await new Promise(resolve => setTimeout(resolve, 2000));
// //通过获取初始化状态来判断是否初始化成功
// const initState = await getInitState();
// if (initState.ecode === "SUC") {
// //检测初始化是否成功
// if (initState.data.passed) {
// console.log("初始化成功")
// sessionStorage.setItem('deviceResetFinished', "true");
// showLoadingModal.value = false;
// showAlreadyModal.value = true;
// } else {
// console.log("初始化失败")
// await getCheckData(); // 获取检查数据找到错误项
// const failedItems = checkData.value.filter(item => !item.pass);
// if (failedItems.length > 0) {
// const errorMessages = generateErrorMessages(failedItems);
// console.log('生成的错误信息:', errorMessages);
// failItems.value = failedItems; // 更新失败的检查项
// failMessage.value = errorMessages.join('\n'); // 更新错误信息
// showLoadingModal.value = false; // 隐藏 LoadingModal
// showFailModal.value = true; // 显示失败弹窗
// } else {
// console.log("初始化失败,但是没有失败项")
// showLoadingModal.value = false; // 隐藏 LoadingModal
// showAlreadyModal.value = true; // 显示已完成弹窗
// sessionStorage.setItem('deviceResetFinished', "true");
// }
// }
// }
// };
// const isTestTubeSlotReady = ref(false); // 试管槽状态标记
// 在获取检测数据后,判断试管槽状态
// const checkTestTubeSlotStatus = (data: CheckItem[]) => {
// const slotCheck = data.find(item => item.type === 'CHECK_TEST_TUBE_SLOT_READY');
// isTestTubeSlotReady.value = slotCheck?.pass ?? false;
// console.log('试管槽状态:', isTestTubeSlotReady.value ? '准备就绪' : '未准备好');
// };
const handleAlreadyConfirm = () => { console.log('用户确认操作') showAlreadyModal.value = false showFailModal.value = false }
onMounted(() => { checkInit() })
// 开始检测
const checkInit = () => { const hasExecutedReset = sessionStorage.getItem('deviceResetFinished') if (!hasExecutedReset || hasExecutedReset === 'false') { showModal.value = true } }
// 更新蓝线的宽度和左偏移量
const updateLinePosition = (element: any) => { lineWidth.value = element.offsetWidth // 当前选中标签的宽度
lineLeft.value = element.offsetLeft // 当前选中标签的左偏移量
}
// 处理标签点击事件
const handleTabClick = (event: any) => { const tabElement = event.target.closest('.nav-tab') if (tabElement) { const tab = tabElement.dataset.tab selectedTab.value = tab sessionStorage.setItem('selectedTab', tab) // 将选中标签存储到 localStorage
updateLinePosition(tabElement) // 更新蓝线的位置和宽度
} }
// 页面加载时默认选中"常规"标签,并设置蓝线的位置
onMounted(() => { const defaultTab = document.querySelector( `.nav-tab[data-tab="${selectedTab.value}"]`, ) if (defaultTab) { updateLinePosition(defaultTab) } }) </script>
<style scoped lang="less"> #index-container { margin: 0; padding: 0; width: 100vw; height: 100vh; background-color: #fff; position: relative; box-sizing: border-box; display: flex; flex-direction: column;
.fade-slide-enter-active, .fade-slide-leave-active { transition: opacity 0.5s, transform 0.5s; /* 控制透明度和滑动的过渡时间 */ }
.fade-slide-enter-from { opacity: 0; transform: translateX(20px); /* 从右侧滑入 */ }
.fade-slide-leave-to { opacity: 0; transform: translateX(-20px); /* 向左滑出 */ }
.nav-bar { width: 100%; height: 60px; margin-bottom: 2px; display: flex; justify-content: space-between; align-items: center;
.logo { width: 100px; height: 75px; padding-left: 10px; }
.nav-tabs { width: 60%; font-size: 24px; display: flex; justify-content: space-around; font-size: 40px;
.nav-tab { cursor: pointer; padding-bottom: 10px; color: black; transition: color 0.3s; position: relative; }
.nav-tab.active { color: #478ffe; } }
.test-control { display: flex; justify-content: space-evenly; padding-right: 10px;
.start-test { width: 200px; height: 40px; background-color: #73bc54; font-size: 20px; }
.pause-test, .stop-test, .continue-test { width: 100px; height: 40px; font-size: 20px; } } }
.interval { width: 100%; height: 5px; background-color: #f5f5f5; position: relative;
.blue-line { position: absolute; bottom: 0; height: 5px; background-color: #478ffe; transition: all 0.3s ease; /* 添加平滑过渡效果 */ } }
.footer-info { width: 100%; height: 40px; display: flex; justify-content: space-between; align-items: center; position: relative; bottom: 0; margin-top: auto;
.user-logo, .time-logo { width: 40px; height: 40px; }
.user-card, .time-card { display: flex; align-items: center; font-size: 26px; }
.equipment-status { background-color: #f5f5f5; width: 400px; height: 40px; line-height: 40px; text-align: center; border-radius: 10px;
.status-text { font-size: 26px; } } .logout { width: 70px; text-align: center; font-size: 20px; margin: auto; } }
.main-content { flex: 1; min-height: 0; // 添加:防止溢出
} } </style>
|