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