|
|
<template> <div class="graphite_home component-page overflow-auto" id="heatArea"> <div class="heat_area"> <div v-for="(item, index) in heatList" :key="item.id"> <HeatPosition :heatInfo="{ ...item, index }" :tubeIndex="index + 1" @onSelectedTray="onSelectedTray" @onSetHeatAreaTemp = "onSetHeatAreaTemp" ></HeatPosition> </div> <!--执行中状态的遮罩层--> </div> <!--拍照区--> <div class="picture_area"> <!--加液区和拍照区可切换--> <div style="display: flex; justify-content: center; align-items: center"> <div style="font-size: 1rem; margin-left: 0.3rem">加液区</div> <van-switch v-model="switchModule" size="1rem" active-color="#ee0a24" inactive-color="green" /> <div style="font-size: 1rem; margin-left: 0.3rem">拍照区</div> </div>
<TakePickture v-if="switchModule"></TakePickture> <div v-else class="home_tube"> <div class="inner-circle" v-for="(tubeItem, index) in tubeList" :key="index" :style="{ background: tubeItem.color }" @click="onChooseTube(tubeItem, index)" ></div> </div> <!--操作区--> <div class="graphite_btn_container"> <van-button size="large" class="btn_size op_open_door" @click="onOPen" >开门</van-button > <van-button size="large" class="btn_size op_start_task" >开始实验</van-button > <van-button size="large" class="btn_size op_stop_task" >结束实验</van-button > <van-button size="large" class="btn_size op_select_craft" @click="onChooseCaft" >选择工艺</van-button > <van-button size="large" class="btn_size op_exec_craft" >执行工艺</van-button > <van-button size="large" class="btn_size op_add_liquid" @click="onAddLiquid" >添加溶液</van-button > <van-button size="large" class="btn_size op_shake_up">摇匀</van-button> <van-button size="large" class="btn_size op_move_heat" @click="onMoveToHeat" >移至加热</van-button > <van-button size="large" class="btn_size op_move_act" @click="onMoveToOperationArea" >移至加液</van-button > <van-button size="large" class="btn_size op_move_exception" >移至特殊</van-button > <van-button size="large" class="btn_size op_start_heat" @click="onStartHeat" >开始加热</van-button > <van-button size="large" class="btn_size op_up_tray" >抬起托盘</van-button > </div> </div> <van-overlay :show="liquidVisible" style="z-index: 9999"> <div class="liquid"> <div class="addLiquid"> <AddLiquid @cancel="liquidVisible = false" @onAddSolution="onAddSolution" ></AddLiquid> </div> </div> </van-overlay> <!--选择工艺--> <OverlayModal :visible="craftVisible"> <CraftList @changeVisible="changeVisible"></CraftList> </OverlayModal> </div> </template> <script lang="ts" setup> import { ref, reactive, onMounted, onUnmounted, watch } from "vue"; //@ts-ignore
import { ElMessage } from "element-plus"; import { createWebSocket, sharedWsUrl } from "@/services/socket"; import { HeatPosition, TakePickture, AddLiquid } from "./components"; import OverlayModal from "@/components/OverlayModal.vue"; import CraftList from "@/views/graphite/components/CraftList.vue"; import { graphiteMock } from "./components/mock"; import { useStatusStore } from "@/stores/status"; import { CmdDescMap, taskCmd, type OperationCmd, } from "@/services/globalCmd/globalCmd"; import { getTxnRecord } from "@/services/txn"; import { useSettingStore } from "@/stores/setting";
const settingStore = useSettingStore();
//设备的全局状态
const statusStore = useStatusStore(); const heatList: any = ref([]); const craftVisible = ref(false); const switchModule = ref(false); let tubeList = reactive<any>([]); const selectedColor = "#4F85FB"; const emptyColor = "#FFFFFF"; const defaultColor = "#189952"; onMounted(() => { //6个加热区数据
heatList.value = settingStore.heatAreaConfig; //设备16个试管的基础数据
tubeBaseConfig(); //连接socket
const wsClient = createWebSocket(sharedWsUrl); const subscription = wsClient.dataOb.subscribe((data) => { if (data.type === "cmd") { const cmdInfo = getTxnRecord(data.data.commandId, "task"); if (cmdInfo) { const cmdName = CmdDescMap[cmdInfo.command]; const result = data.data.success ? "执行完毕" : `执行失败 ${data.data.message}`; ElMessage({ message: `${cmdName} ${result}`, type: data.data.success ? "success" : "error", }); } } else if (data.type === "status") { statusStore.setStatus(data.data); } else if (data.type === "warn") { ElMessage({ message: data.data.message, type: "warning", }); } }); wsClient.connect();
onUnmounted(() => { subscription.unsubscribe(); }); });
//选择的托盘
const selectedTrayList = ref<any>([]); const selectedTrayObj: any = {}; const onSelectedTray = (data: any) => { console.log("data==操作加热区:=", data); if (!data.isSelect) { delete selectedTrayObj[data.id]; } else { selectedTrayObj[data.id] = data; } selectedTrayList.value = Object.values(selectedTrayObj); };
//设置加热区温度
const onSetHeatAreaTemp = (dataInfo:any) => { selectedTrayObj[dataInfo.id] = dataInfo; selectedTrayList.value = Object.values(selectedTrayObj); }
const tubeBaseConfig = () => { //默认为16个
for (let i = 0; i < 16; i++) { tubeList.push({ id: i + 1, color: "rgb(212, 212, 212)", }); } };
//添加溶液
const liquidVisible = ref(false); const onAddLiquid = () => { //检查加液区是否有试管。但是现在没有这个状态,暂时不考虑。
liquidVisible.value = true; };
const onAddSolution = (data: any) => { let ids = data.map((item: any) => item.id); tubeList.forEach((item: any) => { if (ids.includes(item.id)) { item.default = defaultColor; item.color = defaultColor; item.isSelected = true; } }); };
const changeVisible = () => { craftVisible.value = false; };
//选择工艺
const onChooseCaft = () => { craftVisible.value = true; };
const onChooseTube = (tubeItem: any, index: any) => { if (!tubeItem.id) return; //@ts-ignore
let list = [...tubeList]; for (let i = 0; i < list.length; i++) { let item = list[i]; if (index == i) { item.color = selectedColor; item.isSelected = true; } else { item.color = item.default ? item.default : emptyColor; item.isSelected = false; } } tubeList = [...list]; };
//移至加热
const onMoveToHeat = () => { //1、是否选择了加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } //2、只能选择一个加热区
if (selectedTrayList.value.length != 1) { ElMessage.error("只能选择一个加热区"); return; } let selectedDataItem = selectedTrayList.value[0]; //2、判断选择的加热区是否已经有了试管架
let heatAearStatus: any = statusStore.status?.trayStatus || graphiteMock.heatAreaStatus; if (!heatAearStatus[selectedDataItem.index]) { ElMessage.error("选择的加热区已有试管架,重新选择加热区"); return; }
//调用移至加热接口
const params = { areaId: selectedDataItem.id, }; const command: OperationCmd = "moveToHeatArea"; taskCmd({ command, params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); } else { ElMessage.error(res.msg); } });
//指令完成成更新UI
let list = [...heatList.value]; list.forEach((item: any) => { if (item.id == selectedDataItem.id) { item.tubeList = JSON.parse(JSON.stringify(tubeList)); selectedTrayObj[item.id] = item; selectedTrayList.value = Object.values(selectedTrayObj); } }); tubeList.forEach((item:any) => { item.color = ''; item.default = '' }); heatList.value = [...list]; };
//移至加液区(操作区)
const onMoveToOperationArea = () => { //1、判断加液区是否有试管架(暂时获取不到这个状态)
//1、是否选择了试管架/加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择试管架"); return; } //2、只能选择一个试管架/加热区
if (selectedTrayList.value.length != 1) { ElMessage.error("只能选择一个试管架"); return; } let selectedDataItem = selectedTrayList.value[0]; selectedDataItem.isSelect = false //3、选择的加热区有没有试管架
if(!selectedDataItem.tubeList || !selectedDataItem.tubeList.length){ ElMessage.error("选择的加热区没有试管架"); return; }
//4、发送移至加液区指令
const params = { areaId: selectedDataItem.id } onSendCmd('moveToActionArea', params) //更新UI
heatList.value.forEach((item:any) => { if(item.id == selectedDataItem.id){ tubeList = [...item.tubeList] item.tubeList = null; } }) onSelectedTray(selectedDataItem) }
//开门
const onOPen = () => { const params = {}; const command: OperationCmd = "openDoor"; taskCmd({ command, params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); } else { ElMessage.error(res.msg); } }); };
//开始加热
const onStartHeat = () => { //选择的加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } //判断选中的加热区是否有试管架
let existTubeRack = true; //加热区是否设置了加热温度
let hasSetTemp = true; selectedTrayList.value.forEach((item:any) => { let tubeList = item.tubeList; if(!tubeList || !tubeList.length){ existTubeRack = false; } if(!item.temperature){ hasSetTemp = false; } })
if(!existTubeRack){ ElMessage.error("选择的加热区未放置试管架,请重新选择") return; } if(!hasSetTemp){ ElMessage.error("选择的加热区未设置温度,请设置温度") return; }
//后台批量发送指令
const cmdList:any = [] selectedTrayList.value.forEach((heatArea:any) => { cmdList.push({ command:'startHeat', params:{ areaId : heatArea.id } }) }) console.log('cmdList---', cmdList) onSendCmd("startHeat", cmdList) }
const onSendCmd = (command:OperationCmd,params:any)=> { //发送加热指令
taskCmd({ command, params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); } else { ElMessage.error(res.msg); } }); } </script> <style lang="scss" scoped> @use "@/assets/style/mixin.scss" as *;
.graphite_home { background: #f6f6f6; display: flex; gap: 1rem; // @media (min-width: $md) {
flex-direction: column; align-items: stretch; // }
.picture_area { display: flex; flex-direction: column-reverse; } @media (min-width: $lg) { flex-direction: row; align-items: start; .picture_area { display: flex; flex-direction: column; height: 47.5rem; } } } .heat_area { margin: 5px 0;
background: #ffffff; border-radius: 20px;
column-gap: 8px; row-gap: 10px; padding: 1.5rem 0.5rem; min-width: 600px; flex: 1 1 auto; display: grid; grid-template-columns: repeat(3, 1fr); > * { justify-self: center; align-self: center; }
@media (min-width: $md) { column-gap: 12px; row-gap: 20px; height: 47.5rem; }
@media (min-width: $xl) { padding: 4.5rem 1rem; }
.craft_executing_modal { position: absolute; width: 10.5rem; height: 18rem; background: rgb(230, 230, 230); opacity: 0.5; z-index: 9999; } }
.picture_area { display: flex; flex-direction: column; margin: 5px; // margin-left: 1.25rem;
background: #ffffff; border-radius: 20px; padding: 0 1.5rem; @media (min-width: $lg) { flex: 1 1 180px; } @media (min-width: $xl) { flex: 0 0 auto; width: 27rem; }
.graphite_btn_container { margin: 2rem 0; gap: 0.625rem; display: grid; @media (max-width: calc($md - 0.1px)) { grid-template-columns: repeat(2, 1fr); .op_open_door { grid-column: 1/-1; } .op_up_tray { grid-column: 1/-1; } } @media (min-width: $md) and (max-width: calc($lg - 0.1px)) { grid-template-columns: repeat(6, 1fr); > * { grid-column: span 3; } .op_open_door { grid-column: 1/-1; } .op_move_heat { grid-column: span 2; } .op_move_act { grid-column: span 2; } .op_move_exception { grid-column: span 2; } } @media (min-width: $lg) and (max-width: calc($xl - 0.1px)) { grid-template-columns: repeat(2, 1fr); .op_open_door { grid-column: 1/-1; } .op_up_tray { grid-column: 1/-1; } } @media (min-width: $xl) { grid-template-columns: repeat(6, 1fr); > * { grid-column: span 3; } .op_open_door { grid-column: 1/-1; } .op_move_heat { grid-column: span 2; } .op_move_act { grid-column: span 2; } .op_move_exception { grid-column: span 2; } } } }
.btn_size { height: 2.75rem; color: #1989fa; border: 1px solid #1989fa; font-size: 1.25rem; }
.liquid { display: flex; justify-content: center; width: 100%; height: 100%; align-items: center; .addLiquid { width: 70.375rem; height: 52rem; background: #ffffff; } }
.home_tube { height: 12rem; background: #384d5d; opacity: 1; margin-top: 0.5rem; display: flex; flex-wrap: wrap; gap: 0.5rem; padding-left: 0.4rem; padding-top: 0.2rem; border-radius: 1.5rem; .inner-circle { border-radius: 50%; width: 2.5rem; height: 2.5rem; background-color: rgb(212, 212, 212); } } </style>
|