|
|
@ -5,6 +5,7 @@ import { getSolsList } from 'apis/solution' |
|
|
|
import emptyIcon from 'assets/images/empty.svg' |
|
|
|
import { FtMessage } from 'libs/message' |
|
|
|
import { allPropertiesDefined } from 'libs/utils' |
|
|
|
import { cloneDeep } from 'lodash' |
|
|
|
import { onMounted, ref } from 'vue' |
|
|
|
import { useRoute } from 'vue-router' |
|
|
|
|
|
|
@ -34,6 +35,10 @@ onMounted(async () => { |
|
|
|
step.params.minutes = Math.floor(step.params.second / 60) |
|
|
|
step.params.seconds = step.params.second % 60 |
|
|
|
} |
|
|
|
if (step.params.coolingSecond) { |
|
|
|
step.params.coolingMinutes = Math.floor(step.params.coolingSecond / 60) |
|
|
|
step.params.coolingSeconds = step.params.coolingSecond % 60 |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
loading.value = false |
|
|
@ -64,15 +69,19 @@ const okHandle = async () => { |
|
|
|
} |
|
|
|
// 找到第一个参数不完整的步骤 |
|
|
|
const invalidStepIndex = form.value.stepList?.findIndex( |
|
|
|
(step: any) => { |
|
|
|
(step: any, index) => { |
|
|
|
if (['startHeating', 'shaking'].includes(step.method)) { |
|
|
|
if (step.params.minutes || step.params.seconds) { |
|
|
|
step.params.second = (step.params.minutes || 0) * 60 + (step.params.seconds || 0) || undefined |
|
|
|
} |
|
|
|
if (step.params.coolingMinutes || step.params.coolingSeconds) { |
|
|
|
step.params.coolingSecond = (step.params.coolingMinutes || 0) * 60 + (step.params.coolingSeconds || 0) || undefined |
|
|
|
} |
|
|
|
} |
|
|
|
step.params.description = `${index + 1}.` |
|
|
|
switch (step.method) { |
|
|
|
case 'addLiquid': |
|
|
|
step.params.description = step.params.addLiquidList.map((liquid: { containerId: number, volume: number }) => `添加${solutionList.value.find(s => s.id === containerList.value.find(c => c.id === liquid.containerId)?.solutionId)?.name}-${liquid.volume}ml `).join(';') |
|
|
|
step.params.description = '添加溶液' |
|
|
|
break |
|
|
|
case 'startHeating': |
|
|
|
step.params.description = `加热: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|
|
@ -115,7 +124,7 @@ const cancel = () => { |
|
|
|
} |
|
|
|
|
|
|
|
const stepMap: Record<string, CraftTypes.StepItem> = { |
|
|
|
addLiquid: { name: '加液', method: 'addLiquid', params: { addLiquidList: [{ containerId: undefined, volume: undefined }], description: undefined } }, |
|
|
|
// addLiquid: { name: '加液', method: 'addLiquid', params: { addLiquidList: [{ containerId: undefined, volume: undefined }], description: undefined } }, |
|
|
|
startHeating: { name: '加热', method: 'startHeating', params: { temperature: undefined, second: undefined, description: undefined, minutes: undefined, seconds: undefined } }, |
|
|
|
shaking: { name: '摇匀', method: 'shaking', params: { second: undefined } }, |
|
|
|
takePhoto: { name: '拍照', method: 'takePhoto', params: { } }, |
|
|
@ -124,13 +133,93 @@ const stepMap: Record<string, CraftTypes.StepItem> = { |
|
|
|
const addStep = (data: CraftTypes.StepItem) => { |
|
|
|
form.value.stepList?.push(data) |
|
|
|
} |
|
|
|
|
|
|
|
const addLiquidForm = ref<any>({ |
|
|
|
tubeNums: [], |
|
|
|
solutionList: [ |
|
|
|
{ |
|
|
|
solutionId: undefined, |
|
|
|
volume: undefined, |
|
|
|
offset: undefined, |
|
|
|
}, |
|
|
|
], |
|
|
|
}) |
|
|
|
const addLiquidRules = { |
|
|
|
tubeNums: [ |
|
|
|
{ required: true, message: '请选择试管', trigger: 'change' }, |
|
|
|
], |
|
|
|
} |
|
|
|
|
|
|
|
const addLiquidFormRef = ref() |
|
|
|
|
|
|
|
const activeTube = ref(Array.from({ length: 16 }).fill(false)) |
|
|
|
|
|
|
|
const selectVisible = ref(false) |
|
|
|
const checkChange = () => { |
|
|
|
activeTube.value = Array.from({ length: 16 }).fill(selectVisible.value) |
|
|
|
addLiquidForm.value.tubeNums = activeTube.value.map((item, index) => index + 1).filter(item => activeTube.value[item - 1]) |
|
|
|
addLiquidFormRef.value.validateField('tubeNums') |
|
|
|
} |
|
|
|
|
|
|
|
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') |
|
|
|
activeTube.value[Number(num) - 1] = !activeTube.value[Number(num) - 1] |
|
|
|
addLiquidForm.value.tubeNums = activeTube.value.map((item, index) => index + 1).filter(item => activeTube.value[item - 1]) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const addHandle = async () => { |
|
|
|
try { |
|
|
|
const valid = await addLiquidFormRef.value.validate() |
|
|
|
if (!valid) { |
|
|
|
return |
|
|
|
} |
|
|
|
console.log(addLiquidForm.value) |
|
|
|
// addList.value!.push(addLiquidForm) |
|
|
|
const index = form.value.stepList?.findIndex(item => item.method === 'addLiquid') |
|
|
|
if (index !== -1) { |
|
|
|
form.value.stepList?.[index || 0].params?.push(cloneDeep(addLiquidForm.value)) |
|
|
|
} |
|
|
|
else { |
|
|
|
form.value.stepList?.push({ |
|
|
|
name: '加液', |
|
|
|
method: 'addLiquid', |
|
|
|
params: [cloneDeep(addLiquidForm.value)], |
|
|
|
} as CraftTypes.StepItem) |
|
|
|
} |
|
|
|
addLiquidForm.value = { |
|
|
|
tubeNums: [], |
|
|
|
solutionList: [ |
|
|
|
{ |
|
|
|
solutionId: undefined, |
|
|
|
volume: undefined, |
|
|
|
offset: undefined, |
|
|
|
}, |
|
|
|
], |
|
|
|
} |
|
|
|
activeTube.value = Array.from({ length: 16 }).fill(false) |
|
|
|
selectVisible.value = false |
|
|
|
addLiquidFormRef.value.resetFields() |
|
|
|
} |
|
|
|
catch (error) { |
|
|
|
console.log(error) |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<template> |
|
|
|
<FtDialog visible :loading :title="form.id ? '编辑工艺' : '新增工艺'" width="80%" :ok-handle="okHandle" @cancel="cancel"> |
|
|
|
<FtDialog visible :loading :title="form.id ? '编辑工艺' : '新增工艺'" width="90%" :ok-handle="okHandle" @cancel="cancel"> |
|
|
|
<el-form ref="formRef" label-width="auto" :model="form" :rules="rules" class="form-box"> |
|
|
|
<el-row :gutter="30"> |
|
|
|
<el-col :span="10"> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="12"> |
|
|
|
<el-form-item label="工艺名称" prop="name"> |
|
|
|
<el-input v-model="form.name" placeholder="请输入工艺名称" /> |
|
|
|
</el-form-item> |
|
|
@ -143,46 +232,147 @@ const addStep = (data: CraftTypes.StepItem) => { |
|
|
|
</div> |
|
|
|
</el-tag> |
|
|
|
</div> |
|
|
|
<el-form ref="addLiquidFormRef" :model="addLiquidForm" :rules="addLiquidRules" label-width="auto" class="liquid-box"> |
|
|
|
<p>加液</p> |
|
|
|
<el-form-item label="" prop="tubeNums"> |
|
|
|
<el-checkbox v-model="selectVisible" style="margin-right: 10px" @change="checkChange"> |
|
|
|
全选 |
|
|
|
</el-checkbox> |
|
|
|
<div class="tube-item" @click.prevent="mousedownHandle" @touch.prevent="mousedownHandle"> |
|
|
|
<span v-for="item in 16" :key="item" class="tube-inner" :class="{ 'tube-inner-active': activeTube[item - 1] }" :index="item" /> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
<el-row> |
|
|
|
<el-col :span="7" style="text-align: center"> |
|
|
|
溶液 |
|
|
|
</el-col> |
|
|
|
<el-col :span="7" style="text-align: center"> |
|
|
|
容量 |
|
|
|
</el-col> |
|
|
|
<el-col :span="7" style="text-align: center"> |
|
|
|
偏移量 |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
<div class="solution-list"> |
|
|
|
<el-row v-for="(s, index) in addLiquidForm.solutionList" :key="index" :gutter="5" class="solution-item"> |
|
|
|
<el-col :span="7"> |
|
|
|
<el-form-item |
|
|
|
label="" |
|
|
|
:prop="`solutionList.${index}.solutionId`" |
|
|
|
:rules="{ |
|
|
|
required: true, |
|
|
|
message: '请选择溶液', |
|
|
|
trigger: 'change', |
|
|
|
}" |
|
|
|
> |
|
|
|
<el-select v-model="s.solutionId" size="small" style="width: 100%" placeholder="溶液"> |
|
|
|
<el-option v-for="item in solutionList" :key="item.id" :label="item.name" :value="item.id" /> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="7"> |
|
|
|
<el-form-item |
|
|
|
label="" :prop="`solutionList.${index}.volume`" :rules="{ |
|
|
|
required: true, |
|
|
|
message: '请输入容量', |
|
|
|
trigger: 'blur', |
|
|
|
}" |
|
|
|
> |
|
|
|
<el-input v-model.number="s.volume" size="small" type="number" style="width: 100%" placeholder="容量"> |
|
|
|
<template #append> |
|
|
|
ml |
|
|
|
</template> |
|
|
|
</el-input> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="7"> |
|
|
|
<el-form-item |
|
|
|
label="" :prop="`solutionList.${index}.offset`" :rules="{ |
|
|
|
required: true, |
|
|
|
message: '请输入偏移量', |
|
|
|
trigger: 'blur', |
|
|
|
}" |
|
|
|
> |
|
|
|
<el-input v-model.number="s.offset" size="small" type="number" style="width: 100%" placeholder="偏移量"> |
|
|
|
<template #append> |
|
|
|
ml |
|
|
|
</template> |
|
|
|
</el-input> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="3" class="icon-box"> |
|
|
|
<el-icon v-if="addLiquidForm.solutionList.length > 1" color="red" @click="addLiquidForm.solutionList.splice(index, 1)"> |
|
|
|
<RemoveFilled /> |
|
|
|
</el-icon> |
|
|
|
<el-icon |
|
|
|
v-if="index === addLiquidForm.solutionList.length - 1" color="green" @click="addLiquidForm.solutionList.push({ solutionId: undefined, |
|
|
|
volume: undefined, |
|
|
|
offset: undefined })" |
|
|
|
> |
|
|
|
<CirclePlusFilled /> |
|
|
|
</el-icon> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
</div> |
|
|
|
|
|
|
|
<el-form-item> |
|
|
|
<div style="width: 100%;display: flex;justify-content: center"> |
|
|
|
<ft-button type="primary" @click="addHandle"> |
|
|
|
添加 |
|
|
|
</ft-button> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="14"> |
|
|
|
<el-col :span="12"> |
|
|
|
<div v-if="form.stepList?.length" class="step-box"> |
|
|
|
<div v-for="(item, index) in form.stepList" :key="index" class="step-item"> |
|
|
|
<el-form-item :label="`${index + 1}: ${item.name}`"> |
|
|
|
<div v-if="item.method === 'addLiquid'" class="list-box"> |
|
|
|
<div v-for="(liquid, liquidIndex) in item.params.addLiquidList" :key="liquidIndex" class="list-item"> |
|
|
|
<el-select v-model="liquid.containerId" size="small" placeholder="请选择溶液"> |
|
|
|
<el-option v-for="c in containerList" :key="c.id" :label="solutionList.find(s => s.id === c.solutionId)?.name" :value="c.id" /> |
|
|
|
</el-select> |
|
|
|
<el-input v-model.number="liquid.volume" type="number" size="small" placeholder="请输入容量"> |
|
|
|
<template #append> |
|
|
|
ml |
|
|
|
</template> |
|
|
|
</el-input> |
|
|
|
|
|
|
|
<el-icon color="red" @click="item.params.addLiquidList.splice(liquidIndex, 1)"> |
|
|
|
<RemoveFilled /> |
|
|
|
</el-icon> |
|
|
|
<el-icon v-if="liquidIndex === item.params.addLiquidList.length - 1" color="green" @click="item.params.addLiquidList.push({ containerId: undefined, volume: undefined })"> |
|
|
|
<CirclePlusFilled /> |
|
|
|
</el-icon> |
|
|
|
<div v-for="(liquid, liquidIndex) in item.params" :key="liquidIndex"> |
|
|
|
<span>试管: {{ liquid.tubeNums.length === 16 ? '全部' : liquid.tubeNums.join(',') }} </span> |
|
|
|
<div v-for="(s, sIndex) in liquid.solutionList" :key="sIndex"> |
|
|
|
<span>添加</span> |
|
|
|
<span>{{ solutionList.find(solution => solution.id === s.solutionId)?.name }}</span> |
|
|
|
<span>{{ s.volume }}</span> |
|
|
|
<span>ml-</span> |
|
|
|
<span>偏移量</span> |
|
|
|
<span>{{ s.offset }}</span> |
|
|
|
<span>ml</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<template v-else-if="['startHeating'].includes(item.method)"> |
|
|
|
<div v-else-if="['startHeating'].includes(item.method)"> |
|
|
|
<el-input v-model.number="item.params.temperature" type="number" 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> |
|
|
|
<br> |
|
|
|
<div> |
|
|
|
<span>保持时间</span> |
|
|
|
<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> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<span>冷却时间</span> |
|
|
|
<el-select v-model="item.params.coolingMinutes" 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.coolingSeconds" 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> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<template v-else-if="['shaking'].includes(item.method)"> |
|
|
|
<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" /> |
|
|
@ -211,7 +401,7 @@ const addStep = (data: CraftTypes.StepItem) => { |
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
.form-box { |
|
|
|
height: 50vh; |
|
|
|
height: 70vh; |
|
|
|
.el-row { |
|
|
|
height: 100%; |
|
|
|
.el-col:first-child { |
|
|
@ -230,8 +420,9 @@ const addStep = (data: CraftTypes.StepItem) => { |
|
|
|
width: 100%; |
|
|
|
display: grid; |
|
|
|
grid-template-columns: repeat(2, 1fr); /* 创建3列等宽轨道 */ |
|
|
|
grid-template-rows: repeat(4, auto); /* 创建2行自动高度 */ |
|
|
|
gap: 20px; |
|
|
|
grid-template-rows: repeat(2, auto); /* 创建2行自动高度 */ |
|
|
|
gap: 10px; |
|
|
|
margin-bottom: 10px; |
|
|
|
} |
|
|
|
:deep(.el-tag__content) { |
|
|
|
width: 100%; |
|
|
@ -287,4 +478,66 @@ const addStep = (data: CraftTypes.StepItem) => { |
|
|
|
font-size: 12px; |
|
|
|
line-height: 25px; |
|
|
|
} |
|
|
|
|
|
|
|
.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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.liquid-box { |
|
|
|
background: #ecf5ff; |
|
|
|
padding: 0 10px; |
|
|
|
p { |
|
|
|
text-align: center; |
|
|
|
color: #1989fa; |
|
|
|
} |
|
|
|
} |
|
|
|
.solution-list { |
|
|
|
max-height: 100px; |
|
|
|
overflow: hidden; |
|
|
|
overflow-y: auto; |
|
|
|
} |
|
|
|
.solution-item { |
|
|
|
margin-bottom: 10px; |
|
|
|
|
|
|
|
} |
|
|
|
.el-form-item .el-form-item { |
|
|
|
margin-bottom: 10px; |
|
|
|
} |
|
|
|
.icon-box { |
|
|
|
display: flex; |
|
|
|
margin-top: 4px; |
|
|
|
justify-content: space-between; |
|
|
|
.el-icon { |
|
|
|
font-size: 18px; |
|
|
|
line-height: 100%; |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |