|
|
<script setup lang="ts"> import { craftRemove, craftRestart } from 'apis/crafts' import { ElMessageBox } from 'element-plus' import { FtMessage } from 'libs/message' import { allPropertiesDefined } from 'libs/utils' import { cloneDeep } from 'lodash' import { useHomeStore } from 'stores/homeStore' import { computed, onMounted, ref } from 'vue'
const props = defineProps({ sourceData: { type: Object, default: () => ({ monitorId: 61, // 异常工艺id
heatId: 'heat_module_02', // 加热区id
craftsId: 3, // 工艺id
craftsName: '仅加热', // 工艺名称
currentStepIndex: 2, // 当前步骤
currentStepResult: '已经加热分钟已经加热分钟已经加热分钟已经加热分钟已经加热分钟已经加热分钟:2', // 当前步骤结果
steps: [ { name: '预热', method: 'preHeat', params: { temperature: 12, description: '1.预热到12度', }, }, { name: '加稀硝酸', method: 'addThin', params: { volume: 3, description: '2.添加稀硝酸 3ml', }, }, { name: '加热', method: 'heat', params: { temperature: 4, time: 60, description: '3.加热: 4度, 保持1分0秒', minutes: 1, }, }, { name: '加浓硝酸', method: 'addThick', params: { height: 6, volume: 5, description: '4.添加浓硝酸 5ml', }, }, { name: '清洗', method: 'clean', params: { cycle: 8, height: 7, volume: 2, description: '5.针头高度7mm, 加2ml水清洗8次', }, }, { name: '烘干', method: 'dry', params: { temperature: 9, time: 60, description: '6.烘干: 9度, 保持1分0秒', minutes: 1, }, }, { name: '退火', method: 'anneal', params: { temperature: 11, time: 420, description: '7.退火: 11度, 保持7分0秒', minutes: 7, }, }, ], columns: [// 即工艺最原始选择的试管列
1, 5, ], }), }, }) const emits = defineEmits(['ok', 'close'])
const homeStore = useHomeStore()
onMounted(async () => { form.value = { craftId: props.sourceData.craftsId, heatId: props.sourceData.heatId, craftMonitorId: props.sourceData.monitorId, modifyParam: { }, columns: cloneDeep(props.sourceData.columns), steps: cloneDeep(props.sourceData.steps), } selectedColumns.value = selectedColumns.value.map((item, index) => { return props.sourceData.columns.includes(index + 1) }) })
const form = ref<any>({})
const validateHandle = (rule: any, value: any, callback: any) => { if (!value?.length) { callback(new Error('请选择试管')) } else { callback() } }
const rules = { columns: [ { required: true, message: '请选择试管', trigger: 'change', validator: validateHandle }, ], }
const hearInfo = computed(() => { return homeStore.heatAreaList.find(item => item.value === props.sourceData.heatId) })
const resumeCraftHandle = async () => { try { const valid = await formRef.value.validate() if (!valid) { return } // 找到第一个参数不完整的步骤
const errorMsg: string[] = [] const invalidStepIndex = form.value.steps.findIndex((step: any, index: number) => { if (['heat', 'dry', 'anneal'].includes(step.method)) { if (step.params.minutes || step.params.seconds) { step.params.time = (step.params.minutes || 0) * 60 + (step.params.seconds || 0) || undefined } else { step.params.time = undefined } if (step.params.temperature > 400 && step.method === 'anneal') { errorMsg.push(`步骤${index + 1}: 退火温度不能超过400度`) } if (step.params.temperature > 200 && step.method === 'heat') { errorMsg.push(`步骤${index + 1}: 加热温度不能超过200度`) } if (step.params.temperature > 200 && step.method === 'dry') { errorMsg.push(`步骤${index + 1}: 烘干温度不能超过200度`) } } step.params.description = `${index + 1}.` switch (step.method) { case 'clean': step.params.description += `针头高度${step.params.height}mm, 加${step.params.volume}ml水清洗${step.params.cycle}次` break case 'addThin': step.params.description += `添加稀硝酸 ${step.params.volume}mL` break case 'addThick': step.params.description += `添加浓硝酸 ${step.params.volume}mL` break case 'reduceLiquid': step.params.description += `针头高度${step.params.height}mm抽取液体` break case 'preHeat': step.params.description += `预热到${step.params.temperature}度` break case 'heat': step.params.description += `加热: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` break case 'dry': step.params.description += `烘干: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` break case 'anneal': step.params.description += `退火: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` break } return !allPropertiesDefined(step.params, ['minutes', 'seconds', 'description']) })
if (invalidStepIndex !== -1) { FtMessage.error(`步骤${invalidStepIndex + 1}: 请填写完整参数`) return } if (errorMsg.length) { FtMessage.error(errorMsg.join('; ')) return } form.value.modifyParam = form.value.steps?.[props.sourceData.currentStepIndex]?.params await craftRestart(form.value) FtMessage.success('恢复成功') emits('ok') } catch (e) { console.log(e) } }
const stopCraftHandle = async () => { await ElMessageBox.confirm('确定删除当前工艺?', '消息', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }) await craftRemove(props.sourceData.monitorId) FtMessage.success('删除成功') emits('ok') }
const cancel = () => { emits('close') }
const stepMap = { addThin: '加稀硝酸', addThick: '加浓硝酸', clean: '清洗', preHeat: '预热', heat: '加热', dry: '烘干', anneal: ' 退火', } const formRef = ref() const selectVisible = ref(false) const checkChange = () => { selectedColumns.value = Array.from({ length: 5 }).fill(selectVisible.value) form.value.columns = selectedColumns.value.map((item, index) => index + 1).filter(item => selectedColumns.value[item - 1]) formRef.value.validateField('columns') }
const selectedColumns = ref(Array.from({ length: 5 }).fill(false)) const mousedownHandle = async (index: number) => { selectedColumns.value[index - 1] = !selectedColumns.value[index - 1] form.value.columns = selectedColumns.value.map((item, index) => { return item ? index + 1 : false }).filter(item => item !== false) formRef.value.validateField('columns') } </script>
<template> <FtDialog visible title="工艺异常" width="80%"> <el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-box" label-position="left"> <el-row> <el-col :span="10"> <el-form-item label="加热区"> <el-tag>{{ hearInfo?.label }}</el-tag> </el-form-item> <el-form-item label="工艺名称"> <el-tooltip :content="sourceData.craftsName" placement="top" trigger="click"> <el-tag>{{ sourceData.craftsName }}</el-tag> </el-tooltip> </el-form-item> <el-form-item label="当前步骤"> <el-tooltip :content="sourceData?.steps?.[sourceData?.currentStepIndex - 1]?.params?.description" placement="top" trigger="click"> <el-tag>{{ sourceData?.steps?.[sourceData?.currentStepIndex - 1]?.params?.description }}</el-tag> </el-tooltip> </el-form-item> <el-form-item label="步骤结果"> <el-tooltip :content="sourceData?.currentStepResult" placement="top" trigger="click"> <el-tag>{{ sourceData?.currentStepResult }}</el-tag> </el-tooltip> </el-form-item> <el-form-item label="选择试管" prop="columns"> <el-checkbox v-model="selectVisible" style="margin-right: 10px" @change="checkChange"> 全选 </el-checkbox> <div class="tube-item"> <div v-for="item in 5" :key="item" class="tube-line" :class="{ 'tube-line-active': selectedColumns[item - 1] }"
@click.prevent="() => mousedownHandle(item)" @touch.prevent="() => mousedownHandle(item)" > <span v-for="i in 8" :key="i" class="tube-line-inner" /> </div> </div> </el-form-item> </el-col> <el-col :span="14"> <div v-if="form.steps.length" class="step-box"> <div v-for="(item, index) in form.steps" :key="index" class="step-item" :class="{ 'step-item-success': sourceData?.currentStepIndex > index, 'step-item-ing': sourceData?.currentStepIndex === index }" > <el-form-item :label="sourceData?.currentStepIndex === index ? `${index + 1}: ${stepMap[item.method]}` : ''"> <template v-if="sourceData?.currentStepIndex !== index"> <span>{{ item.params.description }}</span> </template> <template v-else> <template v-if="item.method === 'clean'"> <el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> <template #append> mm </template> </el-input> <el-input v-model="item.params.volume" style="width: 100px" type="number" size="small" placeholder="请输入加水量" > <template #append> mL </template> </el-input> <el-input v-model="item.params.cycle" type="number" size="small" placeholder="请输入次数"> <template #append> 次 </template> </el-input> </template> <template v-else-if="['addThin', 'addThick'].includes(item.method)"> <el-input v-model="item.params.volume" type="number" size="small" placeholder="请输入容量"> <template #append> mL </template> </el-input> <el-input v-if="item.method === 'addThick'" v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> <template #append> mm </template> </el-input> </template> <template v-else-if="item.method === 'reduceLiquid'"> <el-input v-model="item.params.height" type="number" size="small" placeholder="请输入高度"> <template #append> mm </template> </el-input> </template> <template v-else-if="item.method === 'preHeat'"> <el-input v-model="item.params.temperature" type="number" size="small" placeholder="请输入温度" > <template #append> ℃ </template> </el-input> </template> <template v-else-if="['heat', 'dry', 'anneal'].includes(item.method)"> <el-input v-model="item.params.temperature" type="number" :max="item.method === 'anneal' ? 400 : 200" size="small" placeholder="加热温度"> <template #append> ℃ </template> </el-input> <el-select v-model="item.params.minutes" style="width: 70px" clearable size="small" placeholder="请选择" > <el-option v-for="i in 60" :key="i" :label="i" :value="i" /> </el-select> <span class="unit-text">分</span> <el-select v-model="item.params.seconds" style="width: 70px" clearable size="small" placeholder="请选择" > <el-option v-for="i in 60" :key="i" :label="i" :value="i" /> </el-select> <span class="unit-text">秒</span> </template> </template> </el-form-item> </div> </div> </el-col> </el-row> </el-form> <template #footer> <ft-button type="primary" :click-handle="resumeCraftHandle"> 恢复工艺 </ft-button> <ft-button type="danger" :click-handle="stopCraftHandle"> 删除工艺 </ft-button> <ft-button @click="cancel"> 关闭 </ft-button> </template> </FtDialog> </template>
<style scoped lang="scss"> .step-item-success { .el-form-item { background: rgba(168,225,168,0.4) !important; } } .step-item-ing { .el-form-item { background: rgba(25,137,250,0.4) !important; } } .step-item { .el-form-item { background: rgba(82, 148, 215, 0.06); padding: 5px; margin-bottom: 10px; :deep(.el-form-item__label) { height: 25px; line-height: 25px; } :deep(.el-form-item__content) { width: 100%; display: flex; align-items: center; position: relative;
.el-input, .el-select { width: 120px; margin: 0 5px; } .info-box { width: 80%; height: 100%; } } } } .unit-text { font-size: 12px; line-height: 25px; }
.tube-item { padding: 5px; background: #384D5D; border-radius: 10px; display: grid; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(1, 1fr); grid-gap: 5px; position: relative; .tube-line { display: flex; flex-direction: column; .tube-line-inner { display: inline-block; width: 25px; height: 25px; border-radius: 50%; background: #fff; margin: 2px; transition: background 0.5s; } } .tube-line-disable { .tube-line-inner { background: #C6C6C6; } } .tube-line-active { .tube-line-inner { background: #26D574; } } }
.el-form-item { :deep(.el-form-item__content) { width: 100%; display: flex; align-items: center; position: relative;
.el-tag { max-width: 90%; .el-tag__content { width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } } } } </style>
|