6 changed files with 495 additions and 7 deletions
-
85src/components/home/SelectCraft/index.vue
-
50src/components/home/StartExperiment/index.vue
-
141src/components/home/Tube/index.vue
-
16src/stores/homeStore.ts
-
11src/types/home.d.ts
-
199src/views/home/index.vue
@ -0,0 +1,85 @@ |
|||
<script setup lang="ts"> |
|||
import { getOreList } from 'apis/ore' |
|||
import { FtMessage } from 'libs/message' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { onMounted, ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const homeStore = useHomeStore() |
|||
|
|||
onMounted(() => { |
|||
getOres() |
|||
}) |
|||
|
|||
const form = ref({ |
|||
craftId: undefined, |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const rules = { |
|||
craftId: [ |
|||
{ required: true, message: '请选择工艺', trigger: 'change' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
// TODO 调用工艺保存接口 |
|||
// await add(form.value) |
|||
FtMessage.success('工艺已设定') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
|
|||
const oreList = ref<Ore.OreItem[]>([]) |
|||
|
|||
const getOres = async () => { |
|||
const res = await getOreList() |
|||
oreList.value = res.list |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="选择工艺" width="40%" @ok="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="120" :model="form" :rules="rules"> |
|||
<el-form-item label="加热区"> |
|||
<el-tag v-for="item in homeStore.heatAreaList.filter(item => item.selected)" :key="item.value" class="mask-tag"> |
|||
{{ item.label }} |
|||
</el-tag> |
|||
</el-form-item> |
|||
<el-form-item label="工艺" prop="craftId"> |
|||
<el-select v-model="form.craftId" placeholder="请选择工艺"> |
|||
<el-option-group |
|||
v-for="group in oreList" |
|||
:key="group.id" |
|||
:label="group.oresName" |
|||
> |
|||
<el-option |
|||
v-for="item in group.craftsList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-option-group> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.el-tag { |
|||
margin-right: 5px; |
|||
} |
|||
</style> |
@ -0,0 +1,50 @@ |
|||
<script setup lang="ts"> |
|||
import { FtMessage } from 'libs/message' |
|||
import { ref } from 'vue' |
|||
|
|||
const emits = defineEmits(['ok', 'cancel']) |
|||
|
|||
const form = ref({ |
|||
name: undefined, |
|||
}) |
|||
const formRef = ref() |
|||
|
|||
const rules = { |
|||
name: [ |
|||
{ required: true, message: '请输入实验名称', trigger: 'blur' }, |
|||
], |
|||
} |
|||
|
|||
const okHandle = async () => { |
|||
try { |
|||
const valid = await formRef.value.validate() |
|||
if (!valid) { |
|||
return |
|||
} |
|||
// TODO 调用开始实验接口 |
|||
// await add(form.value) |
|||
FtMessage.success('实验已开始') |
|||
emits('ok') |
|||
} |
|||
catch (error) { |
|||
console.log(error) |
|||
} |
|||
} |
|||
const cancel = () => { |
|||
emits('cancel') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<FtDialog visible title="开始新实验" width="40%" @ok="okHandle" @cancel="cancel"> |
|||
<el-form ref="formRef" label-width="120" :model="form" :rules="rules"> |
|||
<el-form-item label="实验名称" prop="name"> |
|||
<el-input v-model="form.name" placeholder="" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
</FtDialog> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
|
|||
</style> |
@ -0,0 +1,141 @@ |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue' |
|||
|
|||
withDefaults(defineProps<{ data: Home.HeatArea }>(), { |
|||
data: () => ({ label: 'A-1', value: 'heat_module_01', selected: false, temperature: undefined }), |
|||
}) |
|||
|
|||
const emits = defineEmits(['selectChange']) |
|||
|
|||
const mousedownHandle = (e: Event) => { |
|||
let event |
|||
if ('touches' in e) { |
|||
event = (e.touches as TouchList)[0] |
|||
} |
|||
else { |
|||
event = e |
|||
} |
|||
if (event.target!.classList!.contains('tube-inner')) { |
|||
activeTubeNum.value = event.target!.getAttribute('index') |
|||
} |
|||
} |
|||
|
|||
const activeTubeNum = ref(0) |
|||
const activeTubeBox = ref(false) |
|||
|
|||
const tubeSelect = () => { |
|||
emits('selectChange') |
|||
} |
|||
defineExpose({ |
|||
activeTubeBox, |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="tube" :class="{ 'tube-active': data.selected }"> |
|||
<div class="header"> |
|||
<span>{{ data.label }}</span> |
|||
<span>已放置</span> |
|||
</div> |
|||
<div class="tube-item" @mousedown.prevent="mousedownHandle" @touchstart.prevent="mousedownHandle"> |
|||
<div v-if="false" class="tube-disable" /> |
|||
<span v-for="item in 16" :key="item" class="tube-inner" :class="{ 'tube-inner-active': item <= activeTubeNum }" :index="item" /> |
|||
</div> |
|||
<div v-if="data.temperature" class="temperature-box"> |
|||
<span> |
|||
<span>目标温度: </span> |
|||
<span>{{ data.temperature }}</span> |
|||
<span>℃</span> |
|||
</span> |
|||
<span>加热中</span> |
|||
</div> |
|||
<div v-else class="temperature-box" style="justify-content: center"> |
|||
点击设置目标温度 |
|||
</div> |
|||
<div class="footer"> |
|||
<span :class="{ 'active-footer': data.selected }"> 200℃</span> |
|||
<ft-button size="small" :type="data.selected ? 'primary' : 'default'" @click="tubeSelect"> |
|||
选择 |
|||
</ft-button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.tube-active { |
|||
border-color: #275EFB !important; |
|||
} |
|||
.tube { |
|||
box-sizing: border-box; |
|||
width: 100%; |
|||
height: 95%; |
|||
background: #E9F3FF; |
|||
border-radius: 10px; |
|||
padding: 10px; |
|||
font-size: 14px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
border: 2px solid #E9F3FF; |
|||
transition: border 0.3s; |
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
color: #4D6882; |
|||
} |
|||
.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; |
|||
} |
|||
} |
|||
.temperature-box { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
background: #fff; |
|||
padding: 5px; |
|||
border-radius: 5px; |
|||
font-size: 12px; |
|||
} |
|||
.footer { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
font-weight: bold; |
|||
color: #4D6882; |
|||
.active-footer { |
|||
color: #1562B7; |
|||
} |
|||
.ft-button { |
|||
margin-right: 0; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,11 @@ |
|||
declare namespace Home { |
|||
interface HomeStore { |
|||
heatAreaList: HeatArea[] |
|||
} |
|||
interface HeatArea { |
|||
label: 'A-1' | 'A-2' | 'A-3' | 'A-4' | 'A-5' | 'A-6' |
|||
value: 'heat_module_01' | 'heat_module_02' | 'heat_module_03' | 'heat_module_04' | 'heat_module_05' | 'heat_module_06' |
|||
selected: boolean |
|||
temperature: number | undefined |
|||
} |
|||
} |
@ -1,12 +1,207 @@ |
|||
<script setup lang="ts"> |
|||
import SelectCraft from 'components/home/SelectCraft/index.vue' |
|||
import StartExperiment from 'components/home/StartExperiment/index.vue' |
|||
import Tube from 'components/home/Tube/index.vue' |
|||
import { FtMessage } from 'libs/message' |
|||
import { useHomeStore } from 'stores/homeStore' |
|||
import { ref } from 'vue' |
|||
|
|||
const homeStore = useHomeStore() |
|||
|
|||
const startVisible = ref(false) |
|||
|
|||
const startExperimentHandle = () => { |
|||
// TODO 需要判断有没有进行中的实验 |
|||
startVisible.value = true |
|||
} |
|||
|
|||
const selectCraftVisible = ref(false) |
|||
const selectCraft = () => { |
|||
const count = homeStore.heatAreaList.filter(item => item.selected).length |
|||
if (!count) { |
|||
FtMessage.warning('请选择加热区') |
|||
return |
|||
} |
|||
selectCraftVisible.value = true |
|||
} |
|||
|
|||
const executeCraftHandle = () => { |
|||
const count = homeStore.heatAreaList.filter(item => item.selected).length |
|||
if (!count) { |
|||
FtMessage.warning('请选择加热区') |
|||
return |
|||
} |
|||
// TODO 需要判断有没有选择工艺 |
|||
// TODO 调用执行工艺接口 |
|||
FtMessage.success('执行工艺成功') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
首页 |
|||
<div class="home-page"> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="15"> |
|||
<div class="page-left"> |
|||
<Tube v-for="(item, index) in homeStore.heatAreaList" :key="item.value" :data="item" @select-change="homeStore.selectChange(index)" /> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="9"> |
|||
<div class="page-right"> |
|||
<div class="top"> |
|||
<div class="photo"> |
|||
<el-icon><Camera /></el-icon> |
|||
<span>拍照</span> |
|||
</div> |
|||
</div> |
|||
<div class="button-box"> |
|||
<el-row> |
|||
<el-col :span="24"> |
|||
<ft-button> |
|||
开门 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="12"> |
|||
<ft-button @click="startExperimentHandle"> |
|||
开始实验 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<ft-button disabled> |
|||
停止实验 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="12"> |
|||
<ft-button @click="selectCraft"> |
|||
选择工艺 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<ft-button :click-handle="executeCraftHandle"> |
|||
执行工艺 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="12"> |
|||
<ft-button> |
|||
添加溶液 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<ft-button> |
|||
摇匀 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="8"> |
|||
<ft-button> |
|||
移至加热 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<ft-button> |
|||
移至加液 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<ft-button> |
|||
移至特殊 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="12"> |
|||
<ft-button> |
|||
开始加热 |
|||
</ft-button> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<ft-button> |
|||
抬起托盘 |
|||
</ft-button> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<StartExperiment v-if="startVisible" @ok="startVisible = false" @cancel="startVisible = false" /> |
|||
<SelectCraft v-if="selectCraftVisible" @ok="selectCraftVisible = false" @cancel="selectCraftVisible = false" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.home-page { |
|||
background: rgba(0, 0, 0, 0) !important; |
|||
padding: 0 !important; |
|||
.el-row { |
|||
height: 100%; |
|||
.el-col { |
|||
height: 100%; |
|||
.page-left, .page-right { |
|||
height: 100%; |
|||
background: #fff; |
|||
border-radius: 8px; |
|||
padding: 10px; |
|||
} |
|||
.page-left { |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); /* 创建3列等宽轨道 */ |
|||
grid-template-rows: repeat(2, auto); /* 创建2行自动高度 */ |
|||
gap: 10px; |
|||
justify-content: center; /* 水平居中 */ |
|||
align-items: center; |
|||
} |
|||
.page-right { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100%; |
|||
.top { |
|||
height: 50%; |
|||
background: #4D6882; |
|||
position: relative; |
|||
border-radius: 8px; |
|||
overflow: hidden; |
|||
.photo { |
|||
width: 100%; |
|||
height: 40px; |
|||
background: rgba(0, 0, 0, 0.3); |
|||
position: absolute; |
|||
bottom: 0; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
color: #fff; |
|||
font-size: 15px; |
|||
} |
|||
} |
|||
.button-box { |
|||
height: 50%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-end; |
|||
.el-row { |
|||
height: fit-content; |
|||
margin-bottom: 15px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.el-col { |
|||
.ft-button { |
|||
width: 100%; |
|||
.my-button { |
|||
padding: 0 !important; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue