|
|
<script setup lang="ts"> import WifiConnSvg from 'assets/images/wifi-conn.svg' import WifiUnconnSvg from 'assets/images/wifi-unconn.svg' import ErrorEventsModal from 'components/system/ErrorEventsModal.vue' import NetReconnection from 'components/system/NetReconnection.vue' import { formatDateTime, openFullscreen } from 'libs/utils' import { authRoutes } from 'router/routes' import { useDeviceStore } from 'stores/deviceStore' import { onMounted, onUnmounted, ref, watch, watchEffect } from 'vue' import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router'
import { getDeviceStatus } from '@/libs/deviceComm' import { FtMessageBox } from '@/libs/messageBox' import { useHomeStore } from '@/stores/homeStore' import { useLiquidStore } from '@/stores/liquidStore' import { useSealStore } from '@/stores/sealStore' import { useSystemStore } from '@/stores/systemStore'
const { locale } = useI18n() const router = useRouter() const liquidStore = useLiquidStore() const sealStore = useSealStore() const homeStore = useHomeStore() const deviceStore = useDeviceStore() const systemStore = useSystemStore() const languages = systemStore.languages const currentTime = ref(formatDateTime('YYYY-MM-DD HH:mm:ss')) const timeInterval = ref() const languageType = ref('zh-cn') const deviceInfo = ref<Device.DeviceInfo>(deviceStore.deviceInfo) const workStateName = ref<string>('空闲') const disinfectState = ref(homeStore.disinfectionState.state) const liquidAddState = ref(liquidStore.liquidAddWorkState) const liquidDrainState = ref(liquidStore.liquidDrainWorkState) const deviceState = ref(deviceStore.deviceState) const sealInfo = ref(sealStore.sealInfo) const websocketConnected = ref(systemStore.websocketConnected) let touchStartTime = 0 let touchCount = 0
onMounted(() => { // 连续3次点击任意地方,全屏显示
onFullScreen() })
const onFullScreen = () => { document.body.addEventListener('touchstart', (event) => { const now = Date.now() const timeDiff = now - touchStartTime if (timeDiff < 300 && timeDiff > 0) { touchCount++ if (touchCount === 3) { event.preventDefault() openFullscreen() } } else { touchCount = 1 } touchStartTime = now }) }
const showDeviceStateName = () => { if (deviceState.value.state?.toLocaleLowerCase() !== 'idle') { if (disinfectState.value !== 'idle' && disinfectState.value !== 'finished') { workStateName.value = homeStore.disinfectionState.statedisplayName } else if (liquidAddState.value.workState !== 'idle') { workStateName.value = liquidAddState.value.workStateDisplay } else if (liquidDrainState.value.workState !== 'idle') { workStateName.value = liquidDrainState.value.workStateDisplay } else if (sealInfo.value.workState !== 'idle') { workStateName.value = sealInfo.value.workStateDisplay } } else { workStateName.value = '空闲' } }
onMounted(() => { runSystemTime(systemStore.systemTime) })
watchEffect(() => { deviceInfo.value = deviceStore.deviceInfo websocketConnected.value = systemStore.websocketConnected // 消毒状态
disinfectState.value = homeStore.disinfectionState.state // 加液/排液状态
liquidAddState.value = liquidStore.liquidStateData liquidDrainState.value = liquidStore.liquidDrainWorkState deviceState.value = deviceStore.deviceState // 密封测试状态
sealInfo.value = sealStore.sealInfo showDeviceStateName() })
watch( () => systemStore.systemTime, (newVal) => { if (timeInterval.value) { clearInterval(timeInterval.value) } runSystemTime(newVal) }, { deep: true }, )
const runSystemTime = (time: number) => { if (time) { let sysTime = time timeInterval.value = setInterval(() => { sysTime = sysTime + 1000 currentTime.value = formatDateTime('YYYY-MM-DD HH:mm:ss', sysTime) }, 1000) } }
onUnmounted(() => { clearInterval(timeInterval.value) }) // const showErrorModal = () => {
// ErrorBox.alert('这是一条警告信息')
// }
const onLogout = () => { // 判断是否有正在执行的操作
const hasEx = getDeviceStatus() if (hasEx) { // 有正在执行的操作,给出提示
FtMessageBox.error('退出登录前请停止当前的操作') return } FtMessageBox.warning('请确认是否退出登录?').then(() => { localStorage.removeItem('user') router.push('/login') }) }
// 切换语言的方法
const toggleLanguage = () => { locale.value = locale.value === 'zh' ? 'en' : 'zh' localStorage.setItem('locale', locale.value) // 保存到本地存储
}
const statusMap = { info: { type: 'info', name: '设备弹窗信息', }, success: { type: 'success', name: '设备执行信息', }, check: { type: 'danger', name: '设备错误信息', }, warn: { type: 'warning', name: '设备告警信息', }, } </script>
<template> <el-container class="main"> <el-header class="header"> <div class="logo"> <img src="../assets/images/logo.svg" alt=""> </div> <div class="header-right"> <div class="header-menu"> <div class="aside"> <el-tag v-for="item in authRoutes.filter(item => item.meta!.isDefault)" :key="item.path" class="menu-tag" :class="{ 'aside-item-active': router.currentRoute.value.path.includes(item.path) }" @click="router.push(item.path)" > <template #default> <div class="menu-tags"> <img class="swing-icon" :src=" (router.currentRoute.value.path.includes(item.path) ? item.meta!.activeIcon : item.meta!.icon) as string " alt="" > <span class="text" :class="{ 'active-text': router.currentRoute.value.path.includes(item.path) }">{{ item.meta!.title }}</span> </div> </template> </el-tag> </div> </div> <div class="user"> <span v-if="__DEVICE_TYPE__ === deviceStore.deviceTypeMap.LargeSpaceDM_B"> <img v-if="websocketConnected" width="20" :src="WifiConnSvg" alt=""> <img v-else :src="WifiUnconnSvg" width="20" alt=""> </span> <el-select v-model="languageType" class="select-language" :disabled="false" @change="toggleLanguage"> <el-option v-for="language in languages" :key="language.value" style="height: 2rem" :value="language.value" :label="language.name" > {{ language.name }} </el-option> </el-select> <bt-button type="primary" button-text="注销" @click="onLogout" /> </div> </div> </el-header> <el-container class="container"> <el-main> <router-view v-slot="{ Component }" class="content"> <transition name="el-fade-in-linear"> <keep-alive include="seal"> <component :is="Component" /> </keep-alive> </transition> </router-view> </el-main> </el-container> <el-footer class="footer"> <el-row> <el-col :span="6"> <div class="ip-info"> IP : {{ deviceInfo.ip }} </div> </el-col> <el-col :span="12"> <div class="footer-left"> <img src="../assets/images/run.svg" alt="" style="padding-right: 5px"> <span v-if="!systemStore.systemLogList.length" class="text">设备运行状态</span> <el-popover v-else width="auto" trigger="click" placement="top"> <template #reference> <el-tag style="width: 100%" :type="statusMap[systemStore.systemLogList[0]?.status as keyof typeof statusMap].type" > {{ statusMap[systemStore.systemLogList[0]?.status as keyof typeof statusMap].name }}: {{ systemStore.systemLogList[0]?.name }} {{ systemStore.systemLogList[0]?.time }} </el-tag> </template> <template #default> <div class="log-box"> <el-tag v-for="(item, key) in systemStore.systemLogList" :key :type="statusMap[item?.status as keyof typeof statusMap].type" > <div style="display: flex; justify-content: space-between; width: 100%"> <span> <span>{{ statusMap[item.status as keyof typeof statusMap].name }}: </span> <span>{{ item.name }}</span> </span> <span>{{ item.time }}</span> </div> </el-tag> </div> </template> </el-popover> </div> </el-col> <el-col :span="6"> <div class="time"> {{ currentTime }} </div> </el-col> </el-row> </el-footer> <NetReconnection /> <ErrorEventsModal /> </el-container> </template>
<style scoped lang="scss"> .main { box-sizing: border-box; height: 100%; background: #fafafa; .header { height: 50px; width: 100%; display: flex; align-items: center; padding: 10px 15px; } .footer { height: 50px; width: 100%; display: flex; align-items: center; padding: 10px 15px; position: sticky; } .header { color: #393f46; box-shadow: 0px 1px 5px 0px rgba(9, 39, 62, 0.15);
.logo { height: 22px; width: 100px; display: flex; align-items: center; .title { margin: 0 10px; color: #8799ab; font-weight: 600; } img { height: 100%; } .expand-icon { height: 15px; transition: all 0.3s; } .fold-icon { height: 15px; transform: rotate(90deg); transition: all 0.3s; } } .header-right { display: flex; align-items: center; height: 100%; .header-menu { width: 68vw; } .wifi-icon { width: 40px; height: 100%; background: #fff; border-radius: 5px; display: flex; align-items: center; justify-content: center; img { height: 50%; } } } .user { width: 20vw; text-align: right; right: 5px; display: flex; align-items: center; gap: 25px; padding-left: 10px; .select-language { width: 100px; border-radius: 5px; margin-right: 5px; } .user-logout { margin-left: auto; } } } .container { height: calc(100% - 100px); background: #fff; } } .log-box { width: 500px; height: 400px; overflow: auto; :deep(.el-tag) { margin: 5px 0; width: 100%; .el-tag__content { width: 100%; } } } .aside { overflow: hidden; padding-left: 10px; display: flex; align-items: center; .menu-tag { height: 30px; border: 0; width: 10rem; display: flex; gap: 5px; font-size: 1.6vw; background: rgba(0, 0, 0, 0); transition: background-color 0.5s; } .menu-tags { display: flex; align-items: center; .text { padding-left: 10px; color: #191919; } .active-text { color: #ffffff; } } .aside-item { height: 50px; border-radius: 10px; margin: 10px 0; padding: 0 10px; display: flex; align-items: center; overflow: hidden; justify-content: center; min-width: 6rem; img { margin-right: 10px; } } .aside-item-active { background: #1989fa; display: flex; justify-content: center; } }
.aside-off { width: 70px; //transition: all 0.1s ease;
.aside-item { .text { opacity: 0; } } .aside-item-active { background: rgba(0, 0, 0, 0); color: #fff; } } .user-dropdown-item { display: flex; align-items: center; height: 100%; color: #393f46; font-weight: bold; img { height: 30px; margin-right: 10px; } } .el-main { padding: 0 1px; height: 100%; position: relative; } .content { width: 100%; height: $main-container-height; padding: 10px; } .footer-expand { padding: 10px 15px 10px 85px !important; } .main .footer { padding: 10px; .el-row { width: 100%; height: 100%; .el-col { height: 100%; } } .footer-left, .footer-right { width: 100%; height: 100%; background: #fff; border-radius: 5px; display: flex; align-items: center; padding: 0 20px; } .footer-left { border-right: 5px solid #f6f6f6; align-items: center; justify-content: center; img { height: 60%; } .text { color: #1c1c1c; margin-left: 10px; font-size: 14px; } } .footer-right { border-left: 10px solid #f6f6f6; .status { width: 15px; height: 15px; border-radius: 50%; background: #4ee993; } .text { color: #1c1c1c; margin-left: 10px; font-size: 14px; } }
.ip-info { font-size: 1.3rem; padding-left: 2vw; } .alarm-info { font-size: 1.5rem; width: 53vw; //padding-left: 1.3vw;
background: #f5f5f5; height: 5vh; display: flex; align-items: center; justify-content: center; gap: 5px; .alarm-workState { margin-left: 5px; } } .time { height: 100%; display: flex; align-items: center; justify-content: end; border-radius: 5px; font-size: 1.3rem; margin-right: 2vw; } } .aside-item { .swing-icon { // animation: swing 1s ease-in-out;
width: 1.25rem; } } .logout { display: flex; img { width: 15px; margin-right: 10px; } }
@keyframes swing { 0% { transform: rotate(0deg); } 25% { transform: rotate(-30deg); } 50% { transform: rotate(30deg); } 75% { transform: rotate(-15deg); } 100% { transform: rotate(0deg); } } </style>
|