消毒机设备
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.
 
 
 
 
 

391 lines
11 KiB

<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>