|
|
<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" @onSelectCraft="(craftData)=>{onSelectCraft(item, craftData)}" ></HeatPosition> </div> <!--执行中状态的遮罩层--> </div> <!--拍照区--> <div class="picture_area"> <!--加液区和拍照区可切换--> <TakePickture></TakePickture> <!-- <div v-else style="display: flex;justify-content: center;"> <div class="home_tube"> <div class="inner-circle" v-for="(tubeItem, index) in tubeList" :key="index" :style="{ background: tubeItem.color }" ></div> </div> </div> --> <!--操作区--> <div class="graphite_btn_container"> <van-button v-if="doorStatus" size="large" class="btn_size op_open_door" @click="onCloseDoor" >关门</van-button > <van-button v-else size="large" class="btn_size op_open_door" @click="onOPenDoor" >开门</van-button > <van-button size="large" class="btn_size op_start_task" @click="startTask" >开始实验</van-button > <van-button size="large" class="btn_size op_stop_task" @click="onEndTask" >结束实验</van-button > <van-button size="large" class="btn_size op_select_craft" @click="onChooseCaft" >{{ craftName }}</van-button > <van-button size="large" class="btn_size op_exec_craft" @click="onCraftStart()" >{{ exeCraftName }}</van-button > <van-button size="large" class="btn_size op_add_liquid" @click="onAddLiquid" >添加溶液</van-button > <van-button v-if="!isSharking" size="large" class="btn_size op_shake_up" @click="onStartShakingTube">摇匀</van-button> <van-button v-else size="large" class="btn_size op_shake_up" @click="onStopShakingTube">结束摇匀</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" @click="onMoveToSpecial" >移至特殊</van-button > <van-button v-if="!isHeating" size="large" class="btn_size op_start_heat" @click="onStartHeat" >开始加热</van-button > <van-button v-else size="large" class="btn_size op_start_heat" @click="onStopHeat" >停止加热</van-button >
<van-button size="large" class="btn_size op_up_tray" @click="onUpTray" >抬起托盘</van-button > </div> </div> <van-overlay :show="liquidVisible" v-if="liquidVisible" style="z-index: 9999"> <div class="liquid"> <div class="addLiquid"> <AddLiquid :currentSelectedTube="currentSelectedTube" @cancel="liquidVisible = false" @onAddSolution="onAddSolution" ></AddLiquid> </div> </div> </van-overlay> <!--选择工艺--> <OverlayModal :visible="craftVisible"> <CraftList @changeVisible="changeVisible" @selectedCraft="onHandleSelectedCraft"></CraftList> </OverlayModal>
<!--实验名称--> <OverlayModal :visible="taskNameVisible"> <div class="task_name"> <div class="task_title"> 开始新实验 </div> <div class="task_name_content"> <div class='mt-3'>实验名称:</div> <div class="task_auto"> <input v-model="taskName" placeholder="实验名称" class="task_input" /> <div v-if="!taskName" style="color:red;font-size: 1rem;">请输入实验名称</div> </div> </div>
<br/> <footer class="task_button"> <button class="btn-dark px-2 py-1 min-w-20" @click="onSave">保存</button> <button class="cancel_btn" @click="onCancel">取消</button> </footer> </div> </OverlayModal> </div> </template> <script lang="ts" setup> import { ref, reactive, onMounted, onUnmounted, watch } from "vue"; //@ts-ignore
import {ElMessage, ElMessageBox} 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 { injectFluid } from "@/services/task/task"; import { useStatusStore } from "@/stores/status"; import {getIngTask, saveTaskName, stopTask} from '@/services/task/task'; import { CmdDescMap, taskCmd, type OperationCmd, } from "@/services/globalCmd/globalCmd"; import { craftStart, craftStop } from "@/services/ore/oreManage"; import { getTxnRecord } from "@/services/txn"; import { useSettingStore } from "@/stores/setting"; import { useCraftStore } from "@/stores/craft";
const craftStore = useCraftStore() const craftInfo = ref(craftStore.craftInfo) const settingStore = useSettingStore(); const craftName = ref('选择工艺') const exeCraftName = ref('执行工艺') watch(()=>craftStore.craftInfo, (newVal)=>{ craftInfo.value = newVal if(newVal?.status == 1){ craftName.value = '暂停工艺' exeCraftName.value = '停止工艺' } }) //设备的全局状态
const statusStore = useStatusStore(); const heatAearStatusList = ref(statusStore.status?.heatArea || []) const heatList: any = ref([]); const craftVisible = ref(false); let tubeList = reactive<any>([]); const selectedColor = "#4F85FB"; const emptyColor = "#FFFFFF"; const taskName = ref('') let globeStatus:any = 0; onMounted(() => { //6个加热区数据
heatList.value = settingStore.heatAreaConfig.map((item:any) => { //添加一个字段,默认为未选中
item.isSelect = false; heatAearStatusList.value.forEach((areaItem:any) => { if(areaItem.hardwareId == item.hardwareId){ item = { ...item, heatAearStatus:areaItem } } }) return item; }); //设备16个试管的基础数据
tubeBaseConfig(); //连接socket
const wsClient = createWebSocket(sharedWsUrl); const subscription = wsClient.dataOb.subscribe((data) => { if(!globeStatus){//为了只输入一行,不想后台一直打印此处日志 TODO
console.log('globeStatus====', data) } globeStatus = 1; if (data.type === "cmd") { const cmdInfo = getTxnRecord(data.data.commandId, "task"); if (cmdInfo) { const command:any = cmdInfo.command; //@ts-ignore
const cmdName = CmdDescMap[command]; const result = data.data.success ? "执行完毕" : `执行失败 ${data.data.message}`; ElMessage({ message: `${cmdName} ${result}`, type: data.data.success ? "success" : "error", }); } } }); wsClient.connect();
onUnmounted(() => { subscription.unsubscribe(); }); });
//选中的托盘
const selectedTrayList = ref<any>([]); const selectedTrayObj: any = {}; const onSelectedTray = (heatAreaItem: any, type:string) => { heatList.value[heatAreaItem.index] = heatAreaItem; //取消选中,已经存在selectedTrayList中
let ids = selectedTrayList.value.map((tube:any) =>tube.id) if(type == 'isClick'){//点击加热区
if(ids.includes(heatAreaItem.id)){ heatAreaItem.isSelect = false; selectedTrayList.value = selectedTrayList.value.filter((selectedItem:any) => selectedItem.id != heatAreaItem.id) }else{ heatAreaItem.isSelect = true; selectedTrayList.value.push(heatAreaItem) } }else{ heatAreaItem.isSelect = true; } heatList.value.forEach((item:any) => { if(item.id == heatAreaItem.id){ item = heatAreaItem onHeadleCraft(item) } }) console.log('hasCraftInfo---', hasCraftInfo.value) };
//选择的加热区是否选择了工艺
let hasCraftInfo = ref(false) const onHeadleCraft = (areaItem:any)=>{ if(areaItem.craftInfo){ hasCraftInfo.value = true; } }
//设置加热区温度
const onSetHeatAreaTemp = (dataInfo:any) => { selectedTrayObj[dataInfo.id] = dataInfo; selectedTrayList.value = Object.values(selectedTrayObj); }
//加热区选择的工艺
const onSelectCraft = (item:any, craftInfo:any) => { item.isSelect = true; item.craftInfo = craftInfo onSelectedTray(item,'isMove') } //开始执行工艺
const onCraftStart = () => { if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } //可能会选择多个加热区执行工艺, 批量发送指令
let hasCraft = true; let len = selectedTrayList.value.length; for(let i = 0; i < len; i++){ let item = selectedTrayList.value[i] if(!item.craftInfo){ hasCraft = false; ElMessage.error('选择的加热区未选择工艺') break; } const params = { craftId:null, heatId: item.id } if(craftInfo.value?.status == 1){ craftStop(params).then(res => { ElMessage.success('已停止执行工艺的指令') }) return }else{ params.craftId = item.craftInfo.id craftStart(params).then(res => { ElMessage.success('已执行工艺的指令') }) } }
if(hasCraft){ //给选择加热区heatList加一个正在执行工艺的标识
const selectedIds = selectedTrayList.value.map((item:any) => item.id) heatList.value.forEach((item:any) => { if(selectedIds.includes(item.id)){ item.executing_craft = true; } }); } }
//停止工艺
const onCraftStop = () => {
}
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 = () => { //检查加液区是否有试管。
const liquidArea = statusStore.status?.liquidArea if(liquidArea){ liquidVisible.value = true; }else{ ElMessage.error('加液区未检测到托盘,无法进行加液操作') } };
const onAddSolution = (data: any) => { let ids = data.map((item: any) => item.id); //批量发送加液指令
const params = { injectFluids:data } injectFluid(params) globeStatus = 0 // 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 = () => { //1、是否选择了加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } craftVisible.value = true; };
//选择的工艺
const onHandleSelectedCraft = (craftInfo:any) => { selectedTrayList.value.forEach((item:any) => { item.isSelect = true; item.craftInfo = craftInfo onSelectedTray(item,'isMove') }) const selectedIds = selectedTrayList.value.map((item:any) => item.id) heatList.value.forEach((item:any) => { if(selectedIds.includes(item.id)){ item.craftInfo = craftInfo; } }); changeVisible(); }
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 hardwareId = selectedDataItem.hardwareId; // let trayStatus = heatAearStatusList.value[selectedDataItem.index].trayStatus;
let trayStatus heatAearStatusList.value.forEach((item:any) => { if(hardwareId == item.hardwareId){ trayStatus = item.trayStatus } }) // trayStatus: 0为无托盘,1为有托盘,2为托盘抬起
if (trayStatus == 1) { ElMessage.error("选择的加热区已有试管架,重新选择加热区"); return; }
//调用移至加热接口
const params = { heatId: 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)); selectedDataItem.tubeList = JSON.parse(JSON.stringify(tubeList)); //标注该加热区是选中状态
item.isSelect = true; } }); tubeList.forEach((item:any) => { item.color = ''; item.default = '' }); heatList.value = [...list]; };
//移至加液区(操作区)
const currentSelectedTube = ref({}) const onMoveToOperationArea = () => { //1、判断加液区是否有试管架(暂时获取不到这个状态)
//1、是否选择了试管架/加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择加热区"); return false; } //2、只能选择一个试管架/加热区
if (selectedTrayList.value.length != 1) { ElMessage.error("只能选择一个加热区"); return false; } let selectedDataItem = selectedTrayList.value[0]; currentSelectedTube.value = selectedDataItem //3、选择的加热区有没有试管架
if(!selectedDataItem.tubeList || !selectedDataItem.tubeList.length){ ElMessage.error("选择的加热区没有试管架"); return false; } //4、发送移至加液区指令
const params = { heatId: selectedDataItem.id } onSendCmd('moveToActionArea', params) //更新UI
heatList.value.forEach((item:any) => { if(item.id == selectedDataItem.id){ tubeList = [...item.tubeList] } }) onSelectedTray(selectedDataItem, 'isMove') return true; }
//移至特殊区域
const onMoveToSpecial = () => { //检查是否设置了异常区域
const systemSetting = settingStore.systemSetting let specialArea:any = {} if(systemSetting && systemSetting.length){ systemSetting.forEach(item => { if(item.code == "sys_setting_abnormal_area"){ specialArea = item; } })
if(!specialArea.id){ ElMessage.error('未设置异常区域,请在系统配置中设置') return; }
//是否选择了加热区的试管架
if (!selectedTrayList.value.length) { ElMessage.error("请选择试管架"); return; } //2、只能选择一个试管架/加热区
if (selectedTrayList.value.length != 1) { ElMessage.error("只能选择一个试管架"); return; } let selectedDataItem = selectedTrayList.value[0]; selectedDataItem.isSelect = false onSelectedTray(selectedDataItem, 'isMove')
const params = { heatId:selectedDataItem.id } onSendCmd('moveToActionArea', params) } }
//开始实验
const taskNameVisible = ref(false) const taskId = ref() const onSave = ()=> { if(!taskName.value)return; const params = { name: taskName.value } saveTaskName(params).then(res => { if(res.success){ taskId.value = res.data.id; ElMessage.success('保存成功') onCancel() }else{ ElMessage.error(res.msg) } }) }
const startTask = async () => { const res = await getIngTask();
if(res.data) { ElMessageBox.confirm(`上一实验"${res.data.name}"未结束,是否结束并开始新的实验`, '提示', { confirmButtonText: "确定", cancelButtonText: "取消", center: true, }).then(() => { stopTask({taskId: res.data.id}).then(res=> { if(res.success){ ElMessage.success('实验已停止') taskNameVisible.value = true; }else{ ElMessage.error('实验停止失败') } }).catch(e=>{ ElMessage.error(e) }) }).catch(() => {}) }else { taskNameVisible.value = true }
}
//结束实验
const onEndTask = () => { const params = { taskId: taskId.value } stopTask(params).then(res=> { heatList.value.forEach((item:any) => { item.executing_craft = false; }); if(res.success){ ElMessage.success('实验已停止') }else{ ElMessage.error('实验停止失败') } }).catch(e=>{ ElMessage.error(e) }) }
const onCancel = ()=> { taskNameVisible.value = false; }
//开门
const doorStatus = ref(false)//开门状态 。true: 门已打开, false: 门未打开
const onOPenDoor = () => { const params = {}; const command: OperationCmd = "openDoor"; taskCmd({ command, params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); doorStatus.value = true; } else { ElMessage.error(res.msg); } }); };
//关门
const onCloseDoor = () => { const params = {}; const command: OperationCmd = "closeDoor"; taskCmd({ command, params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); doorStatus.value = false; } else { ElMessage.error(res.msg); } }); }
//开始加热
const isHeating = ref(false) 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) => { const params = { heatId : heatArea.id, temperature: heatArea.temperature } taskCmd({ command: 'startHeat', params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); isHeating.value = true; } else { ElMessage.error(res.msg); } }); }) // onSendCmd("startHeat", cmdList)
}
//停止加热
const onStopHeat = () => { //选择的加热区
if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } selectedTrayList.value.forEach((heatArea:any) => { const params = { heatId : heatArea.id, } taskCmd({ command: 'stopHeat', params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); isHeating.value = false; } else { ElMessage.error(res.msg); } }); }) }
//开始摇匀
const isSharking = ref(false) const onStartShakingTube = () => { const params = {} taskCmd({ command:'startShakeUp', params}).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); isSharking.value = true; } else { ElMessage.error(res.msg); } });
}
//结束摇匀
const onStopShakingTube = () => { const params = {} taskCmd({ command:'stopShakeUp', params}).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); isSharking.value = false; } else { ElMessage.error(res.msg); } }); }
//抬起托盘
const onUpTray = () => { if (!selectedTrayList.value.length) { ElMessage.error("请选择目标加热区"); return; } //选择了多个加热区,可托起多个加热区试管加
selectedTrayList.value.forEach((heatAreaItem:any) => { const params = { heatId: heatAreaItem.id }
taskCmd({ command:'upTray', params }).then((res) => { if (res.success) { ElMessage.success("指令已发送,请稍等"); } else { ElMessage.error(res.msg); } }); }) }
//修改加热区状态 selectedValue: 0 | 1 | 2; // 0为无托盘,1为有托盘,2为托盘抬起
const updateheatAearStatus = (selectedValue:any, heatId:string) => { let heaterList = statusStore.status?.heatArea if(heaterList){ heaterList.forEach((item:any) => { if(item.heaterId == heatId){ item.trayStatus = selectedValue } }) const data:any = { ...statusStore.status, heater:[...heaterList] } statusStore.setStatus(data) } }
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; // @media (min-width: $md) {
flex-direction: column; align-items: stretch; // }
.picture_area { display: flex; flex-direction: column-reverse; margin: 0 12px; } @media (min-width: $lg) { flex-direction: row; align-items: start; .picture_area { display: flex; flex-direction: column; } } } .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 { width: 13rem; height: 13rem; background: #384d5d; opacity: 1; margin-top: 0.5rem; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; 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); } }
.task_name{ height:17.25rem; width: 27.5rem; background: #ffffff;
.task_title{ font-size: 1.25rem; color: #40474E; margin-left: 1.25rem; margin-top: 1.875rem }
.task_name_content{ margin-top:1.875rem; margin-left: 1.5rem; font-size: 1.25rem; color: #40474E; display: flex; .task_input{ border: 1px solid #dcdcdc; border-radius: 8px; height: 3rem; } }
.task_button{ display: flex; justify-content: center; } } .cancel_btn{ border: 1px solid rgb(226, 226, 226); width: 5rem; margin-left: 2rem; } </style>
|