16 changed files with 442 additions and 89 deletions
-
4.env.test
-
4src/apis/crafts.ts
-
2src/apis/home.ts
-
2src/apis/solution.ts
-
4src/app.vue
-
313src/components/craft/AddCraft/index.vue
-
20src/components/craft/AddCraftDialog.vue
-
14src/components/craft/CraftStatus.vue
-
10src/components/home/SelectCraft/index.vue
-
10src/components/home/Tube/index.vue
-
6src/libs/utils.ts
-
2src/router/index.ts
-
6src/stores/systemStore.ts
-
82src/views/craft/index.vue
-
46src/views/home/index.vue
-
6src/views/ore/index.vue
@ -0,0 +1,313 @@ |
|||
<script setup lang="ts"> |
|||
import { getContainerList } from 'apis/container' |
|||
import { createCraft, updateCraft } from 'apis/crafts' |
|||
import { getSolsList } from 'apis/solution' |
|||
import emptyIcon from 'assets/images/empty.svg' |
|||
import { FtMessage } from 'libs/message' |
|||
import { allPropertiesDefined } from 'libs/utils' |
|||
import { onMounted, ref } from 'vue' |
|||
import { useRoute } from 'vue-router' |
|||
|
|||
const props = defineProps({ |
|||
sourceData: { |
|||
type: Object, |
|||
default: () => ({}), |
|||
}, |
|||
}) |
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const route = useRoute() |
|||
const oresId: number = route.query.oreId as unknown as number |
|||
|
|||
const containerList = ref<Container.ContainerItem[]>([]) |
|||
const solutionList = ref<Solution.SolutionItem[]>([]) |
|||
onMounted(async () => { |
|||
containerList.value = await getContainerList() |
|||
solutionList.value = (await getSolsList()).list |
|||
if (props.sourceData) { |
|||
form.value = { ...props.sourceData, stepList: JSON.parse(props.sourceData.steps || '[]') } |
|||
form.value.stepList.forEach((step: any) => { |
|||
if (step.params.time) { |
|||
step.params.minutes = Math.floor(step.params.time / 60) |
|||
step.params.seconds = step.params.time % 60 |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
const form = ref({ |
|||
stepList: [], |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const rules = { |
|||
name: [ |
|||
{ required: true, trigger: 'blur', message: '请输入预设名称' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
// 找到第一个参数不完整的步骤 |
|||
const invalidStepIndex = form.value.stepList.findIndex( |
|||
(step: any) => { |
|||
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 |
|||
} |
|||
} |
|||
switch (step.method) { |
|||
case 'clean': |
|||
step.params.description = `针头高度${step.params.height}mm清洗${step.params.cycle}次` |
|||
break |
|||
case 'addLiquid': |
|||
step.params.description = `添加${solutionList.value.find(s => s.id === containerList.value.find(c => c.id === step.params.containerId)?.solutionId)?.name}-${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']) |
|||
}, |
|||
|
|||
) |
|||
console.log(form.value) |
|||
|
|||
if (invalidStepIndex !== -1) { |
|||
FtMessage.error(`步骤${invalidStepIndex + 1}: 请填写完整参数`) |
|||
return |
|||
} |
|||
|
|||
form.value.steps = JSON.stringify(form.value.stepList) |
|||
form.value.oresId = oresId |
|||
if (form.value.id) { |
|||
await updateCraft(form.value) |
|||
} |
|||
else { |
|||
await createCraft(form.value) |
|||
} |
|||
FtMessage.success('保存成功') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const stepMap = { |
|||
preHeat: { name: '预热', method: 'preHeat', params: { temperature: undefined, description: undefined } }, |
|||
addLiquid: { name: '加液', method: 'addLiquid', params: { volume: undefined, containerId: undefined, description: undefined } }, |
|||
heat: { name: '加热', method: 'heat', params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined } }, |
|||
reduceLiquid: { name: '抽液', method: 'reduceLiquid', params: { height: undefined, description: undefined } }, |
|||
clean: { name: '清洗', method: 'clean', params: { cycle: undefined, height: undefined, description: undefined } }, |
|||
dry: { name: '烘干', method: 'dry', params: { temperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined } }, |
|||
anneal: { name: '退火', method: 'anneal', params: { temperature: undefined, feedTemperature: undefined, time: undefined, description: undefined, minutes: undefined, seconds: undefined } }, |
|||
} |
|||
|
|||
const addStep = (data: any) => { |
|||
form.value.stepList.push(data) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible :title="form.id ? '编辑预设' : '新增预设'" width="80%" :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-form-item label="预设名称" prop="name"> |
|||
<el-input v-model.number="form.name" type="number" placeholder="请输入预设名称" /> |
|||
</el-form-item> |
|||
<el-form-item label="步骤列表"> |
|||
<div class="button-content"> |
|||
<el-tag v-for="item in stepMap" :key="item" size="large" @click="() => addStep(item)"> |
|||
<div style="display: flex;align-items: center;justify-content: space-around;width: 100%;"> |
|||
<el-icon><Plus /></el-icon> |
|||
<span> {{ item.name }}</span> |
|||
</div> |
|||
</el-tag> |
|||
</div> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="14"> |
|||
<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}`"> |
|||
<template v-if="item.method === 'clean'"> |
|||
<el-input v-model.number="item.params.cycle" type="number" size="small" placeholder="请输入次数"> |
|||
<template #append> |
|||
次 |
|||
</template> |
|||
</el-input> |
|||
<el-input v-model.number="item.params.height" type="number" size="small" placeholder="请输入高度"> |
|||
<template #append> |
|||
mm |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'addLiquid'"> |
|||
<el-select v-model="item.params.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="item.params.volume" type="number" size="small" placeholder="请输入容量"> |
|||
<template #append> |
|||
ml |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="item.method === 'reduceLiquid'"> |
|||
<el-input v-model.number="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.number="item.params.temperature" type="number" size="small" placeholder="请输入温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</template> |
|||
<template v-else-if="['heat', 'dry'].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> |
|||
<div v-else-if="['anneal'].includes(item.method)" class="info-box"> |
|||
<div> |
|||
<el-input v-model.number="item.params.temperature" type="number" size="small" placeholder="退火温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
<el-input v-model.number="item.params.feedTemperature" type="number" size="small" placeholder="下料温度"> |
|||
<template #append> |
|||
℃ |
|||
</template> |
|||
</el-input> |
|||
</div> |
|||
<div> |
|||
<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> |
|||
<el-icon style="margin-left: auto;" @click="() => form.stepList.splice(index, 1)"> |
|||
<Close /> |
|||
</el-icon> |
|||
</el-form-item> |
|||
</div> |
|||
</div> |
|||
<div v-else class="empty-box"> |
|||
<img :src="emptyIcon" alt=""> |
|||
<span>暂无步骤</span> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.form-box { |
|||
height: 50vh; |
|||
.el-row { |
|||
height: 100%; |
|||
.el-col:first-child { |
|||
border-right: 1px solid #eee; |
|||
} |
|||
.el-col { |
|||
height: 100%; |
|||
.step-box { |
|||
height: 100%; |
|||
overflow: auto; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.button-content { |
|||
width: 100%; |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); /* 创建3列等宽轨道 */ |
|||
grid-template-rows: repeat(4, auto); /* 创建2行自动高度 */ |
|||
gap: 20px; |
|||
} |
|||
:deep(.el-tag__content) { |
|||
width: 100%; |
|||
} |
|||
.empty-box { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: #ccc; |
|||
img { |
|||
margin-bottom: 10px; |
|||
} |
|||
} |
|||
.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; |
|||
|
|||
.el-input, .el-select { |
|||
width: 120px; |
|||
margin: 0 5px; |
|||
} |
|||
.info-box { |
|||
width: 80%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.unit-text { |
|||
font-size: 12px; |
|||
line-height: 25px; |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue