|
|
<script setup lang="ts"> import { pauseCraft, resumeCraft, stopCraft } from 'apis/crafts' import { trayTube } from 'apis/home' import errorIcon from 'assets/images/error.svg' import ingIcon from 'assets/images/ing.svg' import successIcon from 'assets/images/success.svg' import waitIcon from 'assets/images/wait.svg' import CountDown from 'components/common/Countdown.vue' import { useHomeStore } from 'stores/homeStore' import { useSystemStore } from 'stores/systemStore' import { computed, ref } from 'vue'
const props = withDefaults(defineProps<{ data: System.HeatArea }>(), { data: () => ({ moduleCode: 'heat_module_01', trayUp: 1, trayStatus: 0, fanOpen: false, heating: false, heatingType: 'constant', capExist: false, temperature: 0, targetTemperature: 0, }), })
const emits = defineEmits(['selectChange', 'setTemperature'])
const homeStore = useHomeStore() const systemStore = useSystemStore() const mousedownHandle = async (e: Event) => { let event if ('touches' in e) { event = (e.touches as TouchList)[0] } else { event = e } if (event.target!.classList!.contains('tube-inner')) { const num = event.target!.getAttribute('index') await trayTube({ trayUuid: tray.value!.uuid, tubes: [ { tubeNum: Number(num), exists: !tray.value?.tubes.find(t => t.tubeNum === Number(num))?.exists, }, ], }) } }
const activeTubeBox = ref(false)
const tubeSelect = () => { emits('selectChange') }
const hearInfo = computed(() => { return homeStore.heatAreaList.find(item => item.value === props.data.moduleCode) })
const craft = computed(() => { return systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode)?.crafts })
const craftSteps = computed(() => { const steps = systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode)?.crafts?.craft?.steps return steps ? JSON.parse(steps) : undefined }) console.log(craftSteps.value)
const tray = computed(() => { return systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode) })
const pauseCraftHandle = async () => { await pauseCraft({ heatId: props.data.moduleCode, }) }
const resumeCraftHandle = async () => { await resumeCraft({ heatId: props.data.moduleCode, }) }
const stopCraftHandle = async () => { await stopCraft({ heatId: props.data.moduleCode, }) }
defineExpose({ activeTubeBox, }) </script>
<template> <div class="tube" :class="{ 'tube-active': hearInfo?.selected, 'tube-shadow': data.trayUp === 1 }"> <div class="header"> <span>{{ hearInfo?.label }}</span> <el-tag v-show="!data.trayStatus" type="info"> 空置 </el-tag> <el-tag v-show="data.trayStatus" type="success"> 已放置 </el-tag> </div> <div class="tube-item" :class="{ 'tube-item-heat': ['warm_up', 'thermostatic'].includes(data.heatingType), 'tube-item-constant': data.heatingType === 'constant', 'tube-item-fan': data.fanOpen }" @click.prevent="mousedownHandle" @touch.prevent="mousedownHandle"> <div v-if="data.trayStatus === 0" class="tube-disable" /> <div v-if="craft?.state" class="status" :class="{ 'status-success': craft?.state === 'FINISHED', 'status-wait': craft?.state === 'READY', 'status-PAUSED': craft?.state === 'PAUSED', 'status-error': craft?.state === 'ERROR', 'status-ing': craft?.state === 'RUNNING', }" > <img v-if="craft?.state === 'FINISHED'" :src="successIcon" alt=""> <img v-if="craft?.state === 'RUNNING'" :src="ingIcon" alt=""> <img v-if="craft?.state === 'READY'" :src="waitIcon" alt=""> <img v-if="craft?.state === 'PAUSED'" :src="waitIcon" alt=""> <img v-if="craft?.state === 'ERROR'" :src="errorIcon" alt="">
<span class="status-name">{{ craft?.craft?.name || ' ' }}</span> <span v-if="craft?.state === 'RUNNING'" class="status-text">工艺执行中</span> <span v-if="craft?.state === 'READY'" class="status-text">工艺等待执行</span> <span v-if="craft?.state === 'PAUSED'" class="status-text">工艺已暂停</span> <span v-if="craft?.state === 'ERROR'" class="status-text">工艺执行错误</span> <span v-if="craft?.state === 'FINISHED'" class="status-text">工艺执行完毕</span>
<el-tooltip v-if="craft?.state === 'RUNNING' && craftSteps && craftSteps[craft.currentIndex || 0]?.params?.description" :content="`${craftSteps[craft.currentIndex || 0].params.description}`" placement="top" trigger="click"> <div class="status-description"> {{ craftSteps[craft.currentIndex || 0].params.description }} </div> </el-tooltip> <div class="status-operation"> <ft-button v-if="craft?.state === 'RUNNING'" type="primary" size="small" :click-handle="pauseCraftHandle"> 暂停 </ft-button> <ft-button v-if="craft?.state === 'PAUSED'" type="primary" size="small" :click-handle="resumeCraftHandle"> 继续 </ft-button> <ft-button v-if="['RUNNING', 'PAUSED'].includes(craft?.state)" size="small" :click-handle="stopCraftHandle"> 停止 </ft-button> <span v-if="craft?.state === 'FINISHED'">请取走托盘</span> </div> </div> <span v-for="item in 16" :key="item" class="tube-inner" :class="{ 'tube-inner-active': tray?.tubes.find(tu => tu.tubeNum === item)?.exists }" :index="item" /> </div> <div class="temperature-box"> <span> <span>当前温度: </span> <span>{{ data.temperature || '--' }}</span> <span>℃</span> </span> <span v-show="data.heatingType === 'warm_up'" style="color: #FF4500;font-weight: bold ">预热中</span> <span v-show="data.heatingType === 'thermostatic'" style="color: #FF4500;;font-weight: bold ">加热中</span> <span v-show="data.heatingType === 'constant'" style="color: #eccbb6;font-weight: bold ">恒温中</span> <span v-show="data.fanOpen" style="color: #6CD3FF;font-weight: bold ">降温中</span> </div> <CountDown v-if="data.startHeatTime && data.targetTime" :current="new Date().getTime()" :start-time="data.startHeatTime" :duration="data.targetTime" /> <div class="footer"> <div class="tem-box"> <span :class="{ 'active-footer': hearInfo?.selected }"> {{ data.targetTemperature || '--' }}℃</span> </div> <ft-button size="small" :type="hearInfo?.selected ? 'primary' : 'default'" @click="tubeSelect"> 选择 </ft-button> </div> </div> </template>
<style scoped lang="scss"> .tube-active { border-color: #275EFB !important; } .tube-shadow { box-shadow: 0 0 10px rgba(0,0,0,0.9); } .tube { box-sizing: border-box; width: 100%; height: 95%; background: #E9F3FF; border-radius: 10px; padding: 10px; font-size: 14px; display: flex; flex-direction: column; justify-content: space-between; border: 2px solid #E9F3FF; transition: all 0.3s; position: relative; overflow: hidden;
.header { display: flex; justify-content: space-between; align-items: center; color: #4D6882; } .tube-item-heat { background: #FF4500 !important; } .tube-item-fan { background: #6CD3FF !important; } .tube-item-constant { background: #F0E5DE !important; } .tube-item { padding: 5px; background: #384D5D; border-radius: 10px; display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr); grid-gap: 5px; position: relative; .tube-disable { position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: rgba(255,255,255,0.9); border-radius: 9px; } .tube-inner { display: inline-block; width: 25px; height: 25px; border-radius: 50%; background: #fff; margin: 2px; transition: background 0.5s; } .tube-inner-active { background: #26D574; border: 1px solid #fff; } } .temperature-box { display: flex; justify-content: space-between; background: #fff; padding: 5px; border-radius: 5px; font-size: 12px; } .footer { display: flex; justify-content: space-between; align-items: center; font-weight: bold; color: #4D6882; .active-footer { color: #1562B7; } .ft-button { margin-right: 0; } }
}
.status { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.9); z-index: 1; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; border-radius: 10px; img { width: 22px; margin: 5px 0; } .status-name { font-size: 14px; } .status-text { font-size: 16px; font-weight: 700; } .status-operation { width: 100%; margin-top: auto; margin-bottom: 10px; display: flex; justify-content: space-around; .ft-button { margin-right: 5px; } } } .status-success { border: 1px solid #14A656; } .status-wait { background: rgba(242,235,231, 0.9); border: 1px solid #EE8223; color: #EE8223; } .status-PAUSED { background: rgba(242,235,231, 0.9); border: 1px solid #EE8223; color: #EE8223; } .status-error { background: rgba(232,212,222, 0.9); border: 1px solid #DF1515; color: #DF1515; } .status-ing { background: rgba(205,223,255, 0.9); border: 1px solid #0256FF; color: #0256FF; }
.tem-box { display: flex; align-items: center; .el-icon { margin-left: 5px; } }
.status-description { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 95%; padding: 0 3px; display: inline-block; background: #fff; border-radius: 2px; margin: 2px 0; } </style>
|