Browse Source

feat: 首页实验

feature/three
guoapeng 3 months ago
parent
commit
fedc2d2256
  1. 85
      src/components/home/SelectCraft/index.vue
  2. 50
      src/components/home/StartExperiment/index.vue
  3. 141
      src/components/home/Tube/index.vue
  4. 16
      src/stores/homeStore.ts
  5. 11
      src/types/home.d.ts
  6. 199
      src/views/home/index.vue

85
src/components/home/SelectCraft/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>

50
src/components/home/StartExperiment/index.vue

@ -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>

141
src/components/home/Tube/index.vue

@ -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>

16
src/stores/homeStore.ts

@ -7,31 +7,37 @@ export const useHomeStore = defineStore('home', {
label: 'A-1', label: 'A-1',
value: 'heat_module_01', value: 'heat_module_01',
selected: false, selected: false,
temperature: undefined,
}, },
{ {
label: 'A-2', label: 'A-2',
value: 'heat_module_01',
value: 'heat_module_02',
selected: false, selected: false,
temperature: undefined,
}, },
{ {
label: 'A-3', label: 'A-3',
value: 'heat_module_01',
value: 'heat_module_03',
selected: false, selected: false,
temperature: undefined,
}, },
{ {
label: 'A-4', label: 'A-4',
value: 'heat_module_01',
value: 'heat_module_04',
selected: false, selected: false,
temperature: undefined,
}, },
{ {
label: 'A-5', label: 'A-5',
value: 'heat_module_01',
value: 'heat_module_05',
selected: false, selected: false,
temperature: undefined,
}, },
{ {
label: 'A-6', label: 'A-6',
value: 'heat_module_01',
value: 'heat_module_06',
selected: false, selected: false,
temperature: undefined,
}, },
], ],
}), }),

11
src/types/home.d.ts

@ -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
}
}

199
src/views/home/index.vue

@ -1,12 +1,207 @@
<script setup lang="ts"> <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> </script>
<template> <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> </div>
</template> </template>
<style scoped lang="scss"> <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> </style>
Loading…
Cancel
Save