5 changed files with 356 additions and 31 deletions
-
8src/components/common/FTDialog/index.vue
-
290src/components/craft/AddCraft/index.vue
-
11src/libs/utils.ts
-
19src/types/craft.d.ts
-
59src/views/craft/index.vue
@ -0,0 +1,290 @@ |
|||
<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[]>([]) |
|||
|
|||
const loading = ref(true) |
|||
onMounted(async () => { |
|||
containerList.value = (await getContainerList())?.filter(item => item.type === 0) |
|||
solutionList.value = (await getSolsList()).list |
|||
if (props.sourceData) { |
|||
form.value = { ...props.sourceData, stepList: JSON.parse(props.sourceData.steps || '[]') } |
|||
form.value.stepList?.forEach((step: CraftTypes.StepItem) => { |
|||
if (step.params.second) { |
|||
step.params.minutes = Math.floor(step.params.second / 60) |
|||
step.params.seconds = step.params.second % 60 |
|||
} |
|||
}) |
|||
} |
|||
loading.value = false |
|||
}) |
|||
|
|||
const form = ref<{ |
|||
name?: string |
|||
stepList?: CraftTypes.StepItem[] |
|||
steps?: string |
|||
oresId?: number |
|||
id?: number |
|||
}>({ |
|||
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 (['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 |
|||
} |
|||
} |
|||
switch (step.method) { |
|||
case 'addLiquid': |
|||
step.params.description = step.params.addLiquidList.map(liquid => `添加${solutionList.value.find(s => s.id === containerList.value.find(c => c.id === liquid.containerId)?.solutionId)?.name}-${liquid.volume}ml; `) |
|||
break |
|||
case 'startHeating': |
|||
step.params.description = `加热: ${step.params.temperature}度, 保持${step.params.minutes || 0}分${step.params.seconds || 0}秒` |
|||
break |
|||
case 'shaking': |
|||
step.params.description = `摇匀: ${step.params.second}秒` |
|||
break |
|||
case 'takePhoto': |
|||
step.params.description = `拍照` |
|||
break |
|||
} |
|||
return !allPropertiesDefined(step.params, ['minutes', 'seconds', 'description']) |
|||
}, |
|||
|
|||
) |
|||
console.log(form.value) |
|||
|
|||
if (invalidStepIndex !== -1) { |
|||
FtMessage.error(`步骤${(invalidStepIndex || 0) + 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: Record<string, CraftTypes.StepItem> = { |
|||
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: { } }, |
|||
} |
|||
|
|||
const addStep = (data: CraftTypes.StepItem) => { |
|||
form.value.stepList?.push(data) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible :loading :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}`"> |
|||
<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> |
|||
</div> |
|||
<template 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> |
|||
<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" /> |
|||
</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> |
|||
<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; |
|||
} |
|||
.list-box { |
|||
width: 90%; |
|||
height: 100%; |
|||
.list-item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding-bottom: 10px; |
|||
.el-icon { |
|||
cursor: pointer; |
|||
font-size: 20px; |
|||
margin-left: 10px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.unit-text { |
|||
font-size: 12px; |
|||
line-height: 25px; |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue