|
|
<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="!isTesting" 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="!isPaused">暂停</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> <component :is="Component" /> </keep-alive> </router-view> </main>
<!-- 底部操作信息 --> <el-footer class="footer-info"> <div class="user-card"> <img class="user-logo" src="@/assets/Index/user.svg"></img> <div class="user-name">操作人:{{ username || "未登录" }}</div> </div> <div class="equipment-status"> <div class="status-text">系统:{{ EventText }}</div> </div> <div class="time-card"> <img class="time-logo" src="@/assets/Index/clock.svg"></img> <div class="time-text"> <Time></Time> </div> </div> </el-footer> </div> <InitWarn v-if="showModal" :visible="showModal" title="确认操作" message="确认试管架中的试管槽是否取下,避免自检/自动复位时对设备产生损坏!" icon="/src/assets/Warn.svg" cancelText="返回" confirmText="确认取下/开始复位" @close="showModal = false" @confirm="handleConfirm" /> <!-- 通用加载弹窗组件 --> <LoadingModal v-if="showLoadingModal" :visible="showLoadingModal" title="正在自检/自动复位中" message="请不要有任何手动操作!" cancelText="返回" confirmText="确认取下/开始复位" disableButtons @close="showLoadingModal = 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="checkIfResetCompleted" /> <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" />
</template>
<script setup lang="ts"> import { ref, onMounted, onBeforeUnmount } from 'vue'; import { Time, InitWarn, LoadingModal } from './components/Consumables'; import { getCheckList, startWork, pauseWork, continueWork, stopWork, getInitState, initDevice, saveMountedCardInfo, openBuzzer, closeBuzzer } from '../../services/index'; import { CheckItem, User } from '../../types/Index'; import { useConsumablesStore } from '../../store'; import { createWebSocket } from '../../websocket/socket'; import type { AppEventMessage } from '../../websocket/socket'; import { getServerInfo } from '../../utils/getServerInfo'; const selectedTab = ref(sessionStorage.getItem('selectedTab') || '常规'); const lineWidth = ref(0); const lineLeft = ref(0); const showModal = ref(false); const showLoadingModal = ref(false); const showAlreadyModal = ref(false); const showFailModal = ref(false); const checkData = ref<CheckItem[]>([]); const failItems = ref<CheckItem[]>([]); const consumableStore = useConsumablesStore(); // 新增的变量
const user = ref<User>(JSON.parse(sessionStorage.getItem('token') || '{}') as unknown as User) const isTesting = ref(false); // 用于控制按钮的显示
const isPaused = ref(false); // 用于控制是否暂停测试
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.isIdCardInserted = true; idCardInserted.value = true; EventText.value = "id卡已插入" } else if (data.typeName === 'AppIDCardUnmountEvent') { consumableStore.isIdCardInserted = false; idCardInserted.value = false; EventText.value = "id卡已拔出" } else if (data.typeName === 'AppTubeholderSettingUpdateEvent') { EventText.value = "试管架配置更新" } else if (data.typeName === "DoA8kStepActionEvent") { EventText.value = data.actionStepName! } else { EventText.value = "闲置..." } };
//确认错误事件
const confirmError = async () => { showErrorModal.value = false //关闭蜂鸣器
await closeBuzzer() EventText.value = "闲置..." } //确认警告事件
const confirmWarn = async () => {
showWarnModal.value = false return } //保存id卡信息
const saveIdInfo = async () => { const res = await saveMountedCardInfo(); if (res.success) { console.log("保存id卡信息成功") idCardInserted.value = false; } } onMounted(() => {
ws.connect(); ws.subscribe<AppEventMessage>('AppEvent', handleAppEvent); });
onBeforeUnmount(() => {
ws.unsubscribe<AppEventMessage>('AppEvent', handleAppEvent); ws.disconnect(); }); // 动态生成错误信息
const generateErrorMessages = (data: CheckItem[]): string[] => { return data .filter(item => !item.pass) .map(item => `错误:${item.typechinfo} 检测未通过,请检查设备状态。`); };
// 开始测试
const startTest = async () => { try { const res = await startWork(); if (res.success) { isTesting.value = true; } } catch (error) { console.error('开始测试失败:', error); isTesting.value = false; } const isCheck = sessionStorage.getItem('testStarted'); if (!isCheck) { isTesting.value = false; await checkIfResetCompleted() }
};
// 暂停测试
const pauseTest = async () => { const res = await pauseWork(); if (res.success) { isPaused.value = true; } // 在这里添加暂停测试的逻辑
};
// 停止测试时清除标记
const stopTest = async () => { console.log('测试已停止'); const res = await stopWork(); if (res.success) { isTesting.value = false; } }; //继续测试
const continueTest = async () => { const res = await continueWork(); if (res.success) { isPaused.value = false; console.log('继续测试'); } }; const handleConfirm = async () => { showModal.value = false; // 关闭初始弹窗
await initDevice(); // 初始化设备
await checkIfResetCompleted(); // 检测是否复位完成
};
// 检查是否复位完成
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('testStarted', "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('testStarted', "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(() => { openTest(); });
// 开始检测
const openTest = () => { const hasExecuted = sessionStorage.getItem('testStarted'); // 如果没有标记,执行 openTest 并设置标记
if (!hasExecuted) { showModal.value = true; } }; // 获取检测数据
const getCheckData = async () => { try { const res = await getCheckList(); if (res.data && res.data.ecode == "SUC") { checkData.value = res.data; console.log('设备检查的结果:', checkData.value); // 检查试管槽状态
checkTestTubeSlotStatus(checkData.value); } else { console.log("获取检测数据失败") showFailModal.value = true; failMessage.value = "获取检测数据失败,请检查设备状态。" } } catch (error) { console.error('获取检测数据失败:', error); showFailModal.value = true; failMessage.value = "获取检测数据失败,请检查设备状态。" } };
// 更新蓝线的宽度和左偏移量
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; } } }
.main-content { flex: 1; min-height: 0; // 添加:防止溢出
} } </style>
|