|
|
<script lang="ts" setup> import { sendCmd, syncSendCmd } from 'apis/system' import homeFinish from 'assets/images/home/home-finish.svg' import RunFormulaConfig from 'components/formula/RunFormulaConfig.vue' import LineChart from 'components/home/LineChart.vue' import { ElLoading } from 'element-plus' import { stopTimer } from 'libs/countdownTimer' import { FtMessage } from 'libs/message' import { FtMessageBox } from 'libs/messageBox' import { compareJSON, deviceStateMap } from 'libs/utils' import { computed, onMounted, onUnmounted, provide, ref, watchEffect } from 'vue' import { useRouter } from 'vue-router'
import { useFormulaStore } from '@/stores/formulaStore' import { useHomeStore } from '@/stores/homeStore'
const configRef = ref() provide<(methods: Home.GrandsonMethods) => void>('registerGrandsonMethods', (methods) => { configRef.value = methods }) const router = useRouter() const formulaStore = useFormulaStore() const homeStore = useHomeStore() const formulaInfo = ref() const disinfectionState = ref(homeStore.disinfectionState) const curStateRemainTime = ref(homeStore.curStateRemainTime) const disinfectFormulaVisible = ref(false) const isDeviceIdle = ref(homeStore.isDeviceIdle)
const rate = ref() const log = ref()
watchEffect(async () => { formulaInfo.value = formulaStore.currentSelectedFormulaInfo disinfectionState.value = homeStore.disinfectionState curStateRemainTime.value = homeStore.curStateRemainTime isDeviceIdle.value = homeStore.isDeviceIdle
if (['idle', 'finished'].includes(homeStore.disinfectionState.state)) { formulaInfo.value = formulaStore.selectedFormulaInfo rate.value = formulaStore.selectedFormulaInfo?.injection_pump_speed log.value = formulaStore.selectedFormulaInfo?.loglevel } else { const realForm = (await formulaStore.getRealtimeConfig()).rely rate.value = homeStore.realRate || realForm?.injection_pump_speed log.value = homeStore.realLog || realForm?.loglevel } })
const onDisinfectConfig = () => { disinfectFormulaVisible.value = true }
// 结束消毒
const onFinishDisinfect = async () => { await FtMessageBox.warning('请确认是否结束消毒') homeStore.setRate(undefined) homeStore.setLog(undefined) stopTimer() const loading = ElLoading.service({ lock: true, text: '正在结束消毒', background: 'rgba(255, 255, 255, 0.8)', }) try { const stopParams = { className: 'DisinfectionCtrlServiceExt', fnName: 'stop', params: { loglevel: formulaStore.loglevel }, } await sendCmd(stopParams) const poll = setInterval(() => { if (operationState.value) { loading.close() clearInterval(poll) } }, 100) } catch (e) { console.log(e) loading.close() } } const chartRef = ref() const onSave = async () => { const formData = await chartRef.value?.saveFormData() if (!formData) { return } // 消毒中更新实时配置
const res = await formulaStore.getRealtimeConfig() const diff = compareJSON(res.rely, formData) const diffKeys = Object.keys(diff) if (diffKeys.length) { await Promise.all( diffKeys.map(async (key) => { await formulaStore.setRealtimeConfig(key, diff[key].newVal) }), ) FtMessage.success('设定成功') homeStore.setRate(formData.injection_pump_speed) homeStore.setLog(formData.loglevel) } }
const goHome = () => { router.push('/home') } const onClose = () => { disinfectFormulaVisible.value = false }
const chartList = ref<any[]>([]) const operationState = computed(() => { return disinfectionState.value.state === 'idle' || disinfectionState.value.state === 'finished' })
let poll: any = null
const chartRefs = ref([])
const getData = async (type?: string) => { const data = await syncSendCmd({ className: 'H2O2SensorMgr', fnName: 'getH2O2SensorList', })
const list = data.rely console.log(list) !type && (chartList.value = list.filter(item => item.isOnline).map(item => ({ ...item, data: [] }))) for (let i = 0; i < list.length; i++) { const item: any = list[i] const res = await syncSendCmd({ className: 'H2O2SensorMgr', fnName: 'getDisinfectionH2O2DataRecordList', params: { type: item.type, id: item.id, interval: 30, }, }) item.data = res.rely } console.log(list) chartList.value = list.filter(item => item.isOnline) console.log(chartList.value) }
const chartLoading = ref(false) onMounted(async () => { chartLoading.value = true await getData() chartLoading.value = false poll = setInterval(() => { if (operationState.value) { clearInterval(poll) return } getData('interval') }, 1000 * 30) })
onUnmounted(() => { clearInterval(poll) })
const formatSeconds = (seconds: number) => { if (seconds === -1) { seconds = 0 } const hours = Math.floor(seconds / 3600) const minutes = Math.floor((seconds % 3600) / 60) const remainingSeconds = seconds % 60
// 补零函数
const padZero = (num: number) => num.toString().padStart(2, '0')
return `${padZero(hours)}:${padZero(minutes)}:${padZero(remainingSeconds)}` } </script>
<template> <main class="main-content"> <el-descriptions :column="4"> <!-- <el-descriptions-item label="设定注射速率"> --> <!-- <el-tag> --> <!-- <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ rate }}</span>g/min --> <!-- </el-tag> --> <!-- </el-descriptions-item> --> <el-descriptions-item label="实时注射速率"> <el-tag> <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ homeStore.disinfectionState.injectedVelocity }}</span>g/min </el-tag> </el-descriptions-item> <el-descriptions-item label="目标消毒等级"> <el-tag> <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ log }}</span>Log </el-tag> </el-descriptions-item>
<el-descriptions-item label="实时消毒等级"> <el-tag> <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ parseInt(homeStore.disinfectionState.nlog?.toString()) }}</span>Log </el-tag> </el-descriptions-item> </el-descriptions> <div v-loading="chartLoading" element-loading-background="rgba(255, 255, 255, 0.1)" class="line-chart-content" :style="{ 'grid-template-columns': `repeat(${chartList.length},1fr)` }" > <LineChart v-for="(item, index) in chartList" ref="chartRefs" :key="index" class="chart-box" :style="{ width: `calc(100% / ${chartList.length} - 10px)` }" :env-data="item" /> </div> <div class="line-chart-bottom"> <div class="home-chart-time"> <div v-if="!homeStore.isDeviceIdle" class="home-remain-time"> <div class="home-chart-label"> <span v-if="disinfectionState.state === 'disinfection'"> 预计剩余时间: </span> <span v-else> 消毒状态: </span> </div> <div v-if="disinfectionState.state === 'disinfection'" class="home-chart-value"> {{ formatSeconds(homeStore.disinfectionState.curStateRemainTimeS) }} </div> <div v-else class="home-chart-value"> {{ deviceStateMap[disinfectionState.state] }} </div> </div> </div> <div class="home-chart-btn"> <el-button v-if="!isDeviceIdle" type="danger" @click="onFinishDisinfect"> <template #icon> <img :src="homeFinish" alt="" style="height: 20px"> </template> 结束消毒 </el-button> <el-button v-if="!isDeviceIdle" type="primary" @click="onDisinfectConfig"> 运行参数 </el-button> <el-button @click="goHome"> 返回 </el-button> </div> </div>
<ft-dialog v-model="disinfectFormulaVisible" title="运行参数" width="80vw"> <div class="formula-config"> <RunFormulaConfig ref="chartRef" /> </div> <template #footer> <div class="config-btn"> <el-button @click="onClose"> 关闭 </el-button> <el-button type="primary" @click="onSave"> 确认 </el-button> </div> </template> </ft-dialog> </main> </template>
<style lang="scss" scoped> .main-content { overflow: hidden; background: rgba(147, 203, 255, 0.1); height: 100%; border-radius: 10px; box-shadow: 0 1px 5px 0 rgba(9, 39, 62, 0.15); padding: 10px; .formula-config { display: grid; padding: 10px; width: 100%; } .line-chart-formula { width: 40vw; //background: #E0F0FF;
height: 3.5rem; display: flex; justify-content: center; align-items: center; //border: 1px solid #E0F0FF;
border-radius: 10px; margin-left: 2rem; } .line-chart-set { display: flex; justify-content: end; align-items: center; width: 65vw; padding-right: 20px; } .line-chart-title { height: 15%; display: flex; align-items: center; } .line-chart-content { display: flex; flex-direction: row; // 子元素水平排列(默认值)
align-items: flex-start; // 子元素顶部对齐
overflow-x: auto; // 横向滚动支持
width: 100%; height: calc(100% - 80px); gap: 10px; .chart-box { flex: 0 0 auto; width: 100%; height: 100%; min-width: 200px; } } .line-chart-bottom { height: 40px; display: flex; padding-right: 20px; align-items: center; .home-chart-btn { display: flex; justify-content: end; width: 65%; } .home-chart-time { width: 35%; .home-remain-time { background: #fff; margin: 10px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 20px; gap: 10px; .home-remaini-value { color: #2892f3; } } } } } :deep(.el-descriptions) { width: 100%; height: 40px; .el-descriptions__body { background: rgba(0, 0, 0, 0); .el-descriptions__table { tr { display: flex; justify-content: center; gap: 20px; } } } } :deep(.el-descriptions__cell) { display: flex; align-items: center; //background: #fff;
//margin-bottom: 5px;
//padding: 5px !important;
//.el-descriptions__label {
// display: inline-block;
// width: 120px;
// margin: 0;
// font-size: 15px;
// font-weight: 600;
// color: #606266;
//}
//.el-descriptions__content {
// flex: 1;
// text-align: right;
// display: flex;
// justify-content: flex-end;
//}
} </style>
|