You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
14 KiB
583 lines
14 KiB
<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(() => {
|
|
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="deviceStore.deviceInfo.deviceType === 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="">
|
|
<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;
|
|
width: 22vw;
|
|
padding-left: 1.3vw;
|
|
}
|
|
.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;
|
|
padding: 1vw;
|
|
font-size: 1.3rem;
|
|
width: 22.5vw;
|
|
}
|
|
}
|
|
.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>
|