Browse Source

优化菜单 使用tag

master
LiLongLong 2 months ago
parent
commit
d9f9627414
  1. 29
      package-lock.json
  2. 1
      package.json
  3. 40
      src/app.vue
  4. 1
      src/assets/images/background-logo.svg
  5. 0
      src/components/common/Card/index.vue
  6. 128
      src/components/common/ErrorModal/index.vue
  7. 2
      src/components/common/FTButton/index.vue
  8. 20
      src/components/common/SelectModal/index.vue
  9. 2
      src/components/common/SoftKeyboard/index.vue
  10. 92
      src/components/formula/FormulaConfig.vue
  11. 16
      src/components/formula/FormulaTable.vue
  12. 64
      src/components/home/Environment.vue
  13. 66
      src/components/home/config.vue
  14. 2
      src/components/liquid/LiquidLevel.vue
  15. 50
      src/components/seal/DashboardChart.vue
  16. 32
      src/components/setting/AddUser.vue
  17. 11
      src/components/setting/User.vue
  18. 48
      src/layouts/default.vue
  19. 48
      src/libs/countdownTimer.ts
  20. 39
      src/libs/modalUtil.ts
  21. 2
      src/libs/socket.ts
  22. 20
      src/libs/utils.ts
  23. 2
      src/main.ts
  24. 2
      src/router/index.ts
  25. 45
      src/stores/formulaStore.ts
  26. 61
      src/stores/homeStore.ts
  27. 14
      src/stores/liquidStore.ts
  28. 14
      src/stores/sealStore.ts
  29. 30
      src/stores/settingStore.ts
  30. 26
      src/types/home.d.ts
  31. 5
      src/types/liquid.d.ts
  32. 8
      src/types/seal.d.ts
  33. 2
      src/types/setting.d.ts
  34. 418
      src/views/debug/index.vue
  35. 2
      src/views/formula/index.vue
  36. 68
      src/views/home/index.vue
  37. 50
      src/views/liquid/index.vue
  38. 125
      src/views/seal/index.vue

29
package-lock.json

@ -27,6 +27,7 @@
"express": "^5.1.0",
"konva": "^9.3.18",
"lodash": "^4.17.21",
"nanoid": "^5.1.5",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"pinyin": "^2.11.2",
@ -7790,9 +7791,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
@ -7801,10 +7802,10 @@
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
"node": "^18 || >=20"
}
},
"node_modules/napi-postinstall": {
@ -9286,6 +9287,24 @@
"node": ">=0.8.0"
}
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",

1
package.json

@ -44,6 +44,7 @@
"express": "^5.1.0",
"konva": "^9.3.18",
"lodash": "^4.17.21",
"nanoid": "^5.1.5",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"pinyin": "^2.11.2",

40
src/app.vue

@ -1,13 +1,14 @@
<script setup lang='ts'>
import { sendCmd } from '@/apis/system'
import { useHomeStore } from 'stores/homeStore'
import { onMounted } from 'vue'
import { onMounted, ref } from 'vue'
const homeStore = useHomeStore()
onMounted(async () => {
setInterval(() => {
initData()
}, 1000)
startProgress()
})
const initData = async () => {
@ -21,6 +22,17 @@ const initData = async () => {
homeStore.updateHomeData(resData.rely.val)
}
//
const disinfectionParams = {
className: 'DisinfectionCtrlService',
fnName: 'getState',
params: {},
}
const disinfectionData = await sendCmd(disinfectionParams)
if (disinfectionData.rely) {
homeStore.updateHomeDisinfectionState(disinfectionData.rely)
}
const liquidParams = {
fnName: 'getState',
className: 'AddLiquidService',
@ -42,18 +54,32 @@ const initData = async () => {
homeStore.setDeviceState(deviceData.rely)
}
}
const progress = ref(0)
let timer: any = null
const version = __APP_VERSION__
const startProgress = () => {
timer = setInterval(() => {
const randomStep = Math.floor(Math.random() * 9 + 1)
progress.value = Math.min(progress.value + randomStep, 100)
if (progress.value >= 100) {
clearInterval(timer)
}
}, 100)
}
</script>
<template>
<!-- 进度条容器 -->
<!-- <div v-if='progress < 100' class='main-content'>
<div class='progress-container'>
<div class='progress-bar' :style='{ width: `${progress}%` }' />
<div class='progress-text'>
<div v-if="progress < 100" class="main-content">
<div class="progress-container">
<div class="progress-bar" :style="{ width: `${progress}%` }" />
<div class="progress-text">
v{{ version }}系统初始化中 {{ progress }}%
</div>
</div>
</div> -->
</div>
<router-view v-slot="{ Component }" class="main-content">
<transition name="">
@ -66,7 +92,7 @@ const initData = async () => {
.main-content {
width: 100%;
height: 100%;
background: url('assets/images/background.svg') no-repeat center;
background: url('assets/images/background-logo.svg') no-repeat center;
background-size: cover;
overflow: hidden;
}

1
src/assets/images/background-logo.svg
File diff suppressed because it is too large
View File

0
src/components/common/Card/index.vue

128
src/components/common/ErrorModal/index.vue

@ -0,0 +1,128 @@
<script setup lang="ts">
defineProps({
title: {
type: String,
default: '错误提示',
},
message: {
type: String,
default: '发生错误',
},
confirmText: {
type: String,
default: '确定',
},
visible: {
type: Boolean,
default: false,
},
})
const emits = defineEmits(['close'])
const close = () => {
emits('close')
}
//
// const handleMaskClick = (e: MouseEvent) => {
// if (e.target === e.currentTarget) {
// close()
// }
// }
</script>
<template>
<div v-if="visible" class="error-box-mask">
<div class="error-box-container">
<div class="error-box-header">
<div class="error-title">{{ title }}</div>
<button @click="close">X</button>
</div>
<div class="error-box-content">
<slot>{{ message }}</slot>
</div>
<div class="error-box-footer">
<button @click="close" class="error-box-btn">{{ confirmText }}</button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
$btn-color: #e66541;
.error-box-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.error-box-container {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 350px;
max-width: 90%;
}
.error-box-header {
padding: 15px;
border-bottom: 1px solid #ebeef5;
display: flex;
justify-content: space-between;
align-items: center;
}
.error-title{
color: $btn-color;
font-size: 20px;
}
.error-box-header h3 {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #303133;
}
.error-box-header button {
background: none;
border: none;
font-size: 18px;
color: #909399;
cursor: pointer;
outline: none;
}
.error-box-content {
padding: 20px 15px;
font-size: 14px;
color: #606266;
line-height: 1.5;
}
.error-box-footer {
padding: 15px;
border-top: 1px solid #ebeef5;
text-align: right;
}
.error-box-btn {
padding: 8px 16px;
background-color: $btn-color;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
outline: none;
}
.error-box-btn:hover {
background-color: $btn-color;
}
</style>

2
src/components/common/FTButton/index.vue

@ -55,7 +55,7 @@ defineExpose({
<!-- 添加 loading 判断 -->
<Loading class="rotate-loading" /> <!-- 添加旋转类 -->
</el-icon>
<span><slot /></span>
<div><slot /></div>
</div>
</div>
</template>

20
src/components/common/SelectModal/index.vue

@ -22,26 +22,17 @@ const props = defineProps({
const emits = defineEmits(['confirm', 'cancel'])
const { options, selectedValue, searchable } = toRefs(props)
const { options, selectedValue } = toRefs(props)
//
const searchTerm = ref('')
const tempSelectedValue = ref(selectedValue.value)
//
const filteredOptions = computed(() => {
if (!searchTerm.value || !searchable.value) {
return options.value
}
return options.value.filter((option) => {
return option.label.toLowerCase().includes(searchTerm.value.toLowerCase())
})
})
const selectOption = (option: System.Option) => {
tempSelectedValue.value = option.value
}
const filteredOptions = computed(() => {
return options.value.filter(item => item.value)
})
const confirmSelection = () => {
emits('confirm', tempSelectedValue.value)
}
@ -71,7 +62,7 @@ const handleCancel = () => {
{{ option.label }}
</li>
</ul>
<div v-if="!filteredOptions.length && searchTerm" class="no-results">
<div v-if="!filteredOptions.length" class="no-results">
没有找到匹配项
</div>
</div>
@ -142,6 +133,7 @@ const handleCancel = () => {
flex: 1;
overflow-y: auto;
padding: 10px 0;
max-height: 15vw;
}
.search-box {

2
src/components/common/SoftKeyboard/index.vue

@ -153,7 +153,7 @@ watch(() => props.isVisible, (newVal) => {
}
.keyboard-row button:hover {
background-color: #e0e0e0;
/* background-color: #e0e0e0; */
}
.keyboard-row button:active {

92
src/components/formula/FormulaConfig.vue

@ -1,24 +1,37 @@
<script lang="ts" setup>
import { useFormulaStore } from '@/stores/formulaStore'
import SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
import { onMounted, ref, watch } from 'vue'
import { inject, onMounted, ref, watch } from 'vue'
const props = defineProps<{
type: string
}>()
const formulaStore = useFormulaStore()
const logLevelList = ref(formulaStore.logEnums)
const formData = ref<Formula.FormulaItem>(formulaStore.currentSelectedFormulaInfo || formulaStore.initFormulaInfo)
const formData = ref<Formula.FormulaItem>({
...formulaStore.initFormulaInfo,
})
const inputValue = ref<string>('')
const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('number')
const softKeyboardRef = ref()
const focusedInput = ref<string | null>(null)
const formdataInputStyle = ref('formdata-input')
const registerGrandsonMethods = inject<(methods: any) => void>('registerGrandsonMethods')
onMounted(() => {
if (props.type === 'home') {
formdataInputStyle.value = 'formdata-input-home'
if (formulaStore.currentSelectedFormulaInfo) {
formData.value = formulaStore.currentSelectedFormulaInfo
}
}
//
document.addEventListener('click', (e: any) => {
if (softKeyboardRef.value && !e.target?.name) {
keyboardVisible.value = false
}
})
registerGrandsonMethods?.({ getFormData })
})
// store
@ -28,6 +41,12 @@ watch(() => formulaStore.currentSelectedFormulaInfo, (newVal) => {
}
})
watch(() => formulaStore.initFormulaInfo, (newVal) => {
if (newVal) {
formData.value = newVal
}
})
//
watch(inputValue, (newVal: string | number) => {
if (focusedInput.value) {
@ -38,6 +57,10 @@ watch(inputValue, (newVal: string | number) => {
}
})
const getFormData = () => {
return formData.value
}
//
watch(formData, (newValue) => {
if (focusedInput.value) {
@ -46,7 +69,7 @@ watch(formData, (newValue) => {
}, { deep: true })
const handleSubmit = () => {
console.log('提交表单数据:', formData.value)
formulaStore.updateFormulaList(formData.value)
}
const openKeyboard = (e: any) => {
@ -74,50 +97,50 @@ const openKeyboardType = (labelName: string) => {
<template>
<div class="formula-form">
<el-form :model="formData" label-width="200px" label-position="left">
<el-form :model="formData" label-width="200px" label-position="left" :class="{ formulaFormItem: type === 'home' }">
<el-form-item label="配方名称">
<el-input v-model="formData.name" v-prevent-keyboard name="name" class="formdata-input" @focus="openKeyboard" placeholder="配方名称" />
<el-input v-model="formData.name" v-prevent-keyboard name="name" :class="formdataInputStyle" @focus="openKeyboard" placeholder="配方名称" />
</el-form-item>
<el-form-item label="喷射蠕动泵转速">
<el-input-number v-model.number="formData.injection_pump_speed" v-prevent-keyboard name="injection_pump_speed" :min="0" :max="40" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.injection_pump_speed" v-prevent-keyboard name="injection_pump_speed" :min="0" :max="40" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">g/min</span>
</el-form-item>
<el-form-item label="空气阀配置">
<el-select v-model="formData.proportional_valve_default_value" class="formdata-input" placeholder="请选择">
<el-select v-model="formData.proportional_valve_default_value" :class="formdataInputStyle" placeholder="请选择">
<el-option label="高压" value="120" />
</el-select>
</el-form-item>
<el-form-item label="消毒继续过氧化氢浓度">
<el-input-number v-model.number="formData.continued_gs" v-prevent-keyboard name="continued_gs" :min="0" :max="800" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.continued_gs" v-prevent-keyboard name="continued_gs" :min="0" :max="800" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">ppm</span>
</el-form-item>
<el-form-item label="消毒停止过氧化氢浓度">
<el-input-number v-model.number="formData.stoped_gs" v-prevent-keyboard name="stoped_gs" :min="0" :max="800" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.stoped_gs" v-prevent-keyboard name="stoped_gs" :min="0" :max="800" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">ppm</span>
</el-form-item>
<el-form-item label="消毒继续相对湿度">
<el-input-number v-model.number="formData.continued_humi" v-prevent-keyboard name="continued_humi" :min="0" :max="100" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.continued_humi" v-prevent-keyboard name="continued_humi" :min="0" :max="100" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">%RH</span>
</el-form-item>
<el-form-item label="消毒停止相对湿度">
<el-input-number v-model.number="formData.stoped_humi" v-prevent-keyboard name="stoped_humi" :min="0" :max="100" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.stoped_humi" v-prevent-keyboard name="stoped_humi" :min="0" :max="100" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">%RH</span>
</el-form-item>
<el-form-item label="消毒继续过氧化氢相对饱和度">
<el-input-number v-model.number="formData.continued_satur" v-prevent-keyboard name="continued_satur" :min="0" :max="100" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.continued_satur" v-prevent-keyboard name="continued_satur" :min="0" :max="100" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">%RS</span>
</el-form-item>
<el-form-item label="消毒停止过氧化氢相对饱和度">
<el-input-number v-model.number="formData.stoped_satur" v-prevent-keyboard name="stoped_satur" :min="0" :max="100" @focus="openKeyboard" :controls="false" class="formdata-input" placeholder="请输入" />
<el-input-number v-model.number="formData.stoped_satur" v-prevent-keyboard name="stoped_satur" :min="0" :max="100" @focus="openKeyboard" :controls="false" :class="formdataInputStyle" placeholder="请输入" />
<span class="el-form-item__suffix">%RS</span>
</el-form-item>
<el-form-item label="Log等级">
<!-- <el-input v-model.number="formData.loglevel" class="formdata-input" placeholder="请输入" /> -->
<el-select v-model="formData.loglevel" class="formdata-input">
<!-- <el-input v-model.number="formData.loglevel" :class="formdataInputStyle" placeholder="请输入" /> -->
<el-select v-model="formData.loglevel" :class="formdataInputStyle">
<el-option v-for="item in logLevelList" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<div class="formula-form-btn">
<div v-if="type !== 'home'" class="formula-form-btn">
<slot name="formulaBtn">
<div class="default-btn">
<el-button type="primary" @click="handleSubmit">
@ -130,20 +153,27 @@ const openKeyboardType = (labelName: string) => {
</slot>
</div>
</el-form>
<SoftKeyboard
ref="softKeyboardRef"
v-model="inputValue"
:is-visible="keyboardVisible"
:keyboard-type="keyboardType"
@confirm="handleConfirm"
@close="keyboardVisible = false"
/>
<Teleport to="body">
<SoftKeyboard
ref="softKeyboardRef"
v-model="inputValue"
:is-visible="keyboardVisible"
:keyboard-type="keyboardType"
@confirm="handleConfirm"
@close="keyboardVisible = false"
/>
</Teleport>
</div>
</template>
<style lang="scss" scoped>
.formula-form{
padding: 10px;
.formulaFormItem{
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.formula-form-btn{
display: flex;
justify-content: center;
@ -157,6 +187,18 @@ const openKeyboardType = (labelName: string) => {
.formdata-input{
width: 35vw;
}
.formdata-input-home{
width: 10vw
}
.formula-form-item{
display: grid;
grid-template-columns: 1fr 1fr;
}
.formData-input-config{
width: 10vw;
}
::v-deep .el-input__inner{
text-align: left;
}

16
src/components/formula/FormulaTable.vue

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { useFormulaStore } from '@/stores/formulaStore'
import { ElMessageBox } from 'element-plus'
import { onMounted, ref, watch } from 'vue'
import { onMounted, ref, watchEffect } from 'vue'
const formulaStore = useFormulaStore()
const recipes = ref<Formula.FormulaItem[]>([])
@ -9,18 +9,20 @@ onMounted(() => {
recipes.value = formulaStore.formulaList
})
watch(() => formulaStore.formulaList, (newVal) => {
recipes.value = newVal as Formula.FormulaItem[]
}, { deep: true })
const selectedIndex = ref<number | null>(null)
watchEffect(() => {
if (formulaStore.formulaList) {
recipes.value = formulaStore.formulaList as Formula.FormulaItem[]
}
})
const selectedIndex = ref<number | null>(null)
const selectRecipe = (item: Formula.FormulaItem, index: number) => {
selectedIndex.value = selectedIndex.value === index ? null : index
formulaStore.updateSelectedFormulaData(item)
}
const viewRecipe = (recipe: Formula.FormulaItem) => {
console.log('查看配方:', recipe)
const viewRecipe = (item: Formula.FormulaItem) => {
formulaStore.updateSelectedFormulaData(item)
}
const deleteRecipe = (index: number) => {

64
src/components/home/Environment.vue

@ -3,10 +3,9 @@ import homeInside from 'assets/images/home/home-inside.svg'
import homeProbe1 from 'assets/images/home/home-probe1.svg'
import homeProbe2 from 'assets/images/home/home-probe2.svg'
import { roundNumber } from 'libs/utils'
import { onMounted } from 'vue'
const props = defineProps({
defineProps({
envParams: {
type: Object,
default: () => {},
@ -19,46 +18,43 @@ const imgs: Record<string, any> = {
probe2: homeProbe2,
}
onMounted(() => {
console.log('props----', props.envParams)
})
</script>
<template>
<div>
<div class="title">
<img :src="imgs[envParams.type]"> {{ envParams.title }}
<div class="title">
<img :src="imgs[envParams.type]"> {{ envParams.title }}
</div>
<div class="env-row odd">
<div class="env-row-label ">
温度
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.temp, 2) }}°C
</div>
</div>
<div class="env-row">
<div class="env-row-label ">
相对湿度
</div>
<div class="env-row-value">
{{ roundNumber(envParams.rh, 2) }}%RH
</div>
<div class="env-row odd">
<div class="env-row-label ">
温度
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.temp, 2) }}°C
</div>
</div>
<div class="env-row odd">
<div class="env-row-label ">
相对饱和度
</div>
<div class="env-row">
<div class="env-row-label ">
相对湿度
</div>
<div class="env-row-value">
{{ roundNumber(envParams.rh, 2) }}%RH
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.rs, 2) }}%RS
</div>
<div class="env-row odd">
<div class="env-row-label ">
相对饱和度
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.rs, 2) }}%RS
</div>
</div>
<div class="env-row">
<div class="env-row-label ">
汽化过氧化氢
</div>
<div class="env-row">
<div class="env-row-label ">
汽化过氧化氢
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.h2o2, 2) }}ppm
</div>
<div class="env-row-value ">
{{ roundNumber(envParams.h2o2, 2) }}ppm
</div>
</div>
</template>

66
src/components/home/config.vue

@ -1,17 +1,24 @@
<script lang="ts" setup>
import FormulaConfig from '@/components/formula/FormulaConfig.vue'
import { useFormulaStore } from '@/stores/formulaStore'
import { useHomeStore } from '@/stores/homeStore'
import SelectModal from 'components/common/SelectModal/index.vue'
import FormulaConfig from 'components/formula/FormulaConfig.vue'
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const formulaStore = useFormulaStore()
const homeStore = useHomeStore()
// const formData = ref<Formula.FormulaItem>(formulaStore.currentSelectedFormulaInfo || formulaStore.initFormulaInfo)
const recipes = ref<Formula.FormulaItem[]>([])
const options = ref<System.Option[]>([])
const softKeyboardRef = ref()
const isModalOpen = ref(false)
const keyboardVisible = ref(false)
const selectedValue = ref()
// const focusedInput = ref<string | null>(null)
// const logLevelList = ref(formulaStore.logEnums)
const deviceState = ref(homeStore.deviceStete)
onMounted(() => {
onWatchKeyboard()
recipes.value = [...formulaStore.formulaList]
const list = formulaStore.formulaList
list.forEach((item) => {
@ -40,19 +47,37 @@ const onChooseFormula = () => {
const onDefaultFormula = () => {
formulaStore.initFormulaData()
}
const handleSubmit = () => {
console.log('提交表单数据--')
router.push('/home')
}
const goHome = () => {
router.push('/home')
const onWatchKeyboard = () => {
document.addEventListener('click', (e: any) => {
if (softKeyboardRef.value && !e.target?.name) {
keyboardVisible.value = false
}
})
}
// const openKeyboard = (e: any) => {
// setTimeout(() => {
// keyboardVisible.value = true
// const labelName: string = e.target.name
// openKeyboardType(labelName)
// const formValue = formData.value[labelName as keyof typeof formData.value]
// inputValue.value = formValue.toString()
// focusedInput.value = e.target.name
// }, 100)
// }
//
// const openKeyboardType = (labelName: string) => {
// keyboardType.value = labelName === 'name' ? 'text' : 'number'
// }
// const goHome = () => {
// router.push('/home')
// }
</script>
<template>
<main class="main-content">
<div>
<div class="main-content">
<div v-if="deviceState.state === 'idle'">
<FtButton @click="onChooseFormula">
<el-icon><Plus /></el-icon>
选择配方
@ -63,12 +88,7 @@ const goHome = () => {
</FtButton>
</div>
<div class="formula-config">
<FormulaConfig>
<template #formulaBtn>
<el-button type="primary" @click="handleSubmit">确定</el-button>
<el-button @click="goHome">返回</el-button>
</template>
</FormulaConfig>
<FormulaConfig type="home" />
</div>
<SelectModal
v-if="isModalOpen"
@ -78,7 +98,7 @@ const goHome = () => {
@confirm="handleConfirm"
@cancel="handleCancel"
/>
</main>
</div>
</template>
<style lang="scss" scoped>
@ -89,6 +109,16 @@ const goHome = () => {
.formula-config{
display: flex;
justify-content: center;
padding: 10px;
width: 100%;
}
.formula-config-form{
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px;
}
.formdata-input{
width: 10vw;
}
}
</style>

2
src/components/liquid/LiquidLevel.vue

@ -16,7 +16,7 @@ const liquidHeight = ref((currentLevel.value / totalCapacity) * 200)
<img :src="liquidScaleSvg" alt="刻度" class="liquid-level-img">
<div class="liquid-level-contailner">
<img :src="liquidLevelSvg" alt="液位图" class="liquid-level-img">
<div class="liquid-level-middle" :style="{ height: `${liquidHeight}px` }" />
<!-- <div class="liquid-level-middle" :style="{ height: `${liquidHeight}px` }" /> -->
<div class="liquid-level" :style="{ height: `${liquidHeight}px` }" />
<div class="current-level">
<span>当前液位</span>

50
src/components/seal/DashboardChart.vue

@ -3,7 +3,7 @@ import * as echarts from 'echarts'
import { onMounted, ref } from 'vue'
const option = ref()
const pressure = ref(50)
const pressure = ref(4.5)
onMounted(() => {
const chartDom = document.getElementById('main')!
const myChart = echarts.init(chartDom)
@ -14,7 +14,7 @@ onMounted(() => {
{
data: [
{
value: +(Math.random() * 100).toFixed(1),
value: +(Math.random() * 6).toFixed(1),
},
],
},
@ -26,50 +26,26 @@ onMounted(() => {
const init = () => {
option.value = {
tooltip: {
formatter: '{a} <br/>{b} : {c}%',
},
series: [
{
name: 'Pressure',
type: 'gauge',
axisLine: {
lineStyle: {
width: 15,
color: [
[0.3, '#67e0e3'],
[0.7, '#37a2da'],
[1, '#fd666d'],
],
},
},
pointer: { //
itemStyle: {
color: 'auto',
},
max: 6,
progress: {
show: true,
width: 5,
},
axisTick: { //
distance: -15,
length: 8, // 线
lineStyle: {
color: '#fff',
width: 1, // 线
},
},
splitLine: {
distance: -30, //
length: 30,
axisLine: {
lineStyle: {
color: '#fff',
width: 4,
width: 5,
},
},
axisLabel: {
color: 'inherit',
distance: 25,
fontSize: 14,
},
detail: {
valueAnimation: true,
formatter: '{value} KPa',
color: 'inherit',
fontSize: 14,
formatter: '{value}Kp',
},
data: [
{

32
src/components/setting/AddUser.vue

@ -2,33 +2,39 @@
import type { FormInstance } from 'element-plus'
import { useSettingStore } from '@/stores/settingStore'
import SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
import { onMounted, ref, watch } from 'vue'
import { onMounted, ref, watchEffect } from 'vue'
const settingStore = useSettingStore()
const visible = ref(settingStore.addUserVisible)
const modalType = ref(settingStore.userModalState)
const inputValue = ref<string>('')
const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('text')
const softKeyboardRef = ref()
const userFormRef = ref()
const focusedInput = ref<string | null>(null)
const modalTitle = ref('新增用户')
const userForm = ref<Setting.User>({
uid: '',
id: '',
passwd: '',
confirmPassword: '',
is_admin: '',
is_admin: false,
})
watch(() => settingStore.addUserVisible, (newVal) => {
console.log('newVal---', newVal)
visible.value = newVal
})
//
watch(inputValue, (newVal) => {
console.log('focusedInput.value--', focusedInput.value)
watchEffect(() => {
modalType.value = settingStore.userModalState
if (settingStore.userModalState === 'edit') {
modalTitle.value = '修改密码'
}
if (settingStore.currentEditUser) {
userForm.value = settingStore.currentEditUser
}
else {
modalTitle.value = '新增用户'
}
visible.value = settingStore.addUserVisible
if (focusedInput.value) {
userForm.value[focusedInput.value as keyof typeof userForm.value] = newVal
userForm.value[focusedInput.value as keyof typeof userForm.value] = inputValue.value
}
})
@ -64,9 +70,7 @@ const openKeyboard = (e: any) => {
setTimeout(() => {
keyboardVisible.value = true
const labelName = e.target.name
console.log('labelName---', labelName)
const formValue = userForm.value[labelName as keyof typeof userForm.value]
console.log('formValue---', formValue)
inputValue.value = formValue ? formValue.toString() : ''
focusedInput.value = e.target.name
}, 100)
@ -84,7 +88,7 @@ const handleConfirm = (value: string) => {
</script>
<template>
<FtDialog v-model="visible" title="新增用户" :ok-handle="() => { onSave(userFormRef) }" @cancel="onClose">
<FtDialog v-model="visible" :title="modalTitle" :ok-handle="() => { onSave(userFormRef) }" @cancel="onClose">
<div>
<el-form ref="userFormRef" :model="userForm" label-width="auto" style="max-width: 400px">
<el-form-item

11
src/components/setting/User.vue

@ -5,12 +5,15 @@ import { ref } from 'vue'
import AddUser from './AddUser.vue'
const settingStore = useSettingStore()
const tableData = ref(settingStore.historyList)
const visible = ref()
const tableData = ref(settingStore.userList)
const updatePwd = (userItem: Setting.User) => {
console.log('userItem===', userItem)
settingStore.updateCurrentEditUser(userItem)
settingStore.updateUserModalState('edit')
settingStore.updateVisible(true)
}
const onAddUser = () => {
settingStore.updateUserModalState('add')
settingStore.updateVisible(true)
}
const onDelUser = () => {
@ -44,7 +47,7 @@ const onDelUser = () => {
<div>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="用户名" />
<el-table-column prop="uid" label="用户名" />
<el-table-column prop="detail" label="操作">
<template #default="scoped">
<el-link type="primary" @click="updatePwd(scoped.row)">修改密码</el-link>
@ -53,7 +56,7 @@ const onDelUser = () => {
</el-table>
</div>
<div>
<AddUser v-model:visible="visible"/>
<AddUser/>
</div>
</div>
</template>

48
src/layouts/default.vue

@ -1,5 +1,6 @@
<script setup lang="ts">
import HomeAlarmSvg from 'assets/images/home/home-alarm.svg'
import ErrorBox from 'libs/modalUtil'
import { formatDateTime } from 'libs/utils'
import { authRoutes } from 'router/routes'
import { onMounted, onUnmounted, ref } from 'vue'
@ -17,6 +18,9 @@ onMounted(() => {
onUnmounted(() => {
clearInterval(timeInterval)
})
const showErrorModal = () => {
ErrorBox.alert('这是一条警告信息')
}
</script>
<template>
@ -28,22 +32,22 @@ onUnmounted(() => {
<div class="header-right">
<div class="header-menu">
<div class="aside">
<div
<el-tag
v-for="item in authRoutes.filter(item => item.meta!.isDefault)"
:key="item.path"
class="aside-item"
class="menu-tag"
:class="{ 'aside-item-active': router.currentRoute.value.path.includes(item.path) }"
@click="router.push(item.path)"
>
<img class="swing-icon" :src="((router.currentRoute.value.path.includes(item.path) ? item.meta!.activeIcon : item.meta!.icon) as string)" alt="">
<span class="text">{{ item.meta!.title }}</span>
</div>
<template #default>
<div class="menu-tags">
<img class="swing-icon" :src="((router.currentRoute.value.path.includes(item.path) ? item.meta!.activeIcon : item.meta!.icon) as string)" alt="">
<span class="text">{{ item.meta!.title }}</span>
</div>
</template>
</el-tag>
</div>
</div>
<!-- <div class="wifi-icon">
<img v-if="isClose" src="../assets/images/wifi.svg" alt="">
<img v-else src="../assets/images/wifi-active.svg" alt="">
</div> -->
<div class="user">
<FtButton type="primary" class="user-logout">
<div>注销</div>
@ -62,7 +66,7 @@ onUnmounted(() => {
</el-container>
<footer class="footer">
<div class="ip-info">IP : 192.0.0.0</div>
<div class="alarm-info">
<div class="alarm-info" @click="showErrorModal">
<img :src="HomeAlarmSvg" width="20" alt="报警">
<div>机器报警信息</div>
</div>
@ -150,7 +154,23 @@ onUnmounted(() => {
display: flex;
align-items: center;
gap: 5px;
//transition: all 0.1s ease;
.menu-tag{
height: 3rem;
border: 0px;
width: 7rem;
display: flex;
gap: 5px;
font-size: 16px;
background: white;
}
.menu-tags{
display: flex;
align-items: center;
.text {
padding-left: 10px;
color: black;
}
}
.aside-item {
height: 50px;
border-radius: 10px;
@ -161,16 +181,10 @@ onUnmounted(() => {
overflow: hidden;
height: 3.5vw;
justify-content: center;
// border: 1px solid #e7e7e7;
min-width: 6rem;
img {
// height: 80%;
margin-right: 10px;
}
.text {
transition: opacity 0.3s ease;
white-space: nowrap;
}
}
.aside-item-active {
background: #1989FA;

48
src/libs/countdownTimer.ts

@ -0,0 +1,48 @@
class CountdownTimer {
private endTime: number
private timer: number | null = null
private callback: (formatTimeStr: string) => void
constructor(timestamp: number, fn: (formatTimeStr: string) => void) {
this.endTime = timestamp
this.callback = fn
this.updateDisplay()
}
private formatTime(seconds: number): string {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
const timeStr = [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
secs.toString().padStart(2, '0'),
].join(':')
return timeStr
}
private updateDisplay(): void {
this.timer = window.setInterval(() => {
const seconds = Math.floor(this.endTime / 1000)
const curStateRemainTimeS = this.formatTime(seconds)
this.callback && this.callback(curStateRemainTimeS)
this.endTime -= 1000
}, 1000)
}
public startTimer(): void {
this.timer = window.setInterval(() => {
this.updateDisplay()
}, 1000)
}
public stopTimer(): void {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
// 倒计时
export default CountdownTimer

39
src/libs/modalUtil.ts

@ -0,0 +1,39 @@
import type { App } from 'vue'
import ErrorBox from 'components/common/ErrorModal/index.vue'
import { createVNode, render } from 'vue'
interface ErrorBoxOptions {
title?: string
message?: string
confirmText?: string
onClose?: () => void
}
let instance: any = null
const ErrorModalBox = {
alert(options: ErrorBoxOptions | string = {}) {
if (typeof options === 'string') {
options = { message: options }
}
const container = document.createElement('div')
instance = createVNode(ErrorBox, {
...options,
visible: true,
onClose: () => {
options.onClose?.()
close()
},
})
render(instance, container)
document.body.appendChild(container.firstElementChild!)
const close = () => {
render(null, container)
document.body.removeChild(container)
instance = null
}
},
install(app: App) {
app.config.globalProperties.$errorBox = ErrorBox
},
}
export default ErrorModalBox

2
src/libs/socket.ts

@ -1,5 +1,6 @@
// src/utils/websocket.ts
import type { Ref } from 'vue'
import ErrorBox from 'libs/modalUtil'
import { ref, watch } from 'vue'
// WebSocket客户端类
@ -98,6 +99,7 @@ export class WebSocketClient {
}
else {
console.error('----error---', response.message)
ErrorBox.alert('这是一条错误信息底部')
}
})
// 设置超时

20
src/libs/utils.ts

@ -19,3 +19,23 @@ export function roundNumber(num: string | number, digits: number) {
num = Number(num)
return num.toFixed(digits)
}
export const deviceStateMap = {
idle: '空闲',
init: '初始化设备',
preheat: '预热',
disinfection: '消毒中',
degradation: '降解中',
finished: '消毒完成',
dehumidificationBeforeDisinfection: '消毒前除湿',
dehumidificationAfterDisinfection: '消毒后除湿',
emptyLiquidFromTheLine: '排空管路',
}
export const sealStateMap = <Record<string, any>>{
idle: '空闲',
initDevice: '初始化',
iniflating: '打压中',
leakText: '检漏中',
stopping: '停止中',
}

2
src/main.ts

@ -4,6 +4,7 @@ import FtDialog from 'components/common/FTDialog/index.vue'
import FtTable from 'components/common/FTTable/index.vue'
import ElementPlus from 'element-plus'
import locale from 'element-plus/es/locale/lang/zh-cn'
import ErrorBox from 'libs/modalUtil'
import pinia from 'stores/index'
import { createApp } from 'vue'
import App from './app.vue'
@ -22,6 +23,7 @@ app.directive('prevent-keyboard', {
},
})
app.use(pinia)
app.use(ErrorBox)
app.component('FtTable', FtTable)
app.component('FtButton', FtButton)
app.component('FtDialog', FtDialog)

2
src/router/index.ts

@ -9,7 +9,7 @@ const router = createRouter({
})
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
if (getToken()) {
if (!getToken()) {
next()
}
else {

45
src/stores/formulaStore.ts

@ -1,3 +1,4 @@
import { nanoid } from 'nanoid'
import { defineStore } from 'pinia'
import { ref } from 'vue'
@ -7,7 +8,6 @@ const logList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((el) => {
name: `${el} Log`,
}
})
const defaultFormulaInfo = {
continued_gs: 200, // 消毒继续过氧化氢浓度
continued_humi: 60, // 消毒继续相对湿度
@ -20,8 +20,8 @@ const defaultFormulaInfo = {
stoped_gs: 300, // 消毒停止过氧化氢浓度
stoped_humi: 85, // 消毒停止相对湿度
stoped_satur: 85, // 消毒停止过氧化氢相对饱和度
name: '', // 配方名称
formula_id: '01', // 配方ID
name: '默认配置', // 配方名称
formula_id: nanoid(), // 配方ID
}
export const useFormulaStore = defineStore('formula', () => {
@ -34,26 +34,56 @@ export const useFormulaStore = defineStore('formula', () => {
const currentSelectedFormulaInfo = ref<Formula.FormulaItem>()
const initFormulaInfo = ref<Formula.FormulaItem>({
...defaultFormulaInfo,
formula_id: nanoid(), // 配方ID // 配方ID
})
const formulaList = ref<Formula.FormulaItem[]>([defaultFormulaInfo])
// 配方中的压力控制
const updatePressurList = (pressurData: string[]) => {
pressurList = pressurData
}
// 消毒页面及列表中选择的配方
const updateSelectedFormulaData = (formulaItem: Formula.FormulaItem) => {
currentSelectedFormulaInfo.value = formulaItem
}
// 新建配方,更新配方列表
const formulaList = ref<Formula.FormulaItem[]>([])
const updateFormulaList = (formulaItem: Formula.FormulaItem) => {
const { formula_id } = formulaItem
// 存在则更新,不存在则添加
const ids = formulaList.value && formulaList.value.map(item => item.formula_id)
if (ids.includes(formula_id)) {
const cloneList = [...formulaList.value]
const list: Formula.FormulaItem[] = []
cloneList.forEach((item) => {
if (item.formula_id === formula_id) {
item = {
...formulaItem,
}
}
list.push(item)
})
formulaList.value = [...list]
}
else {
formulaList.value.push({
...formulaItem,
})
}
}
const initFormulaData = () => {
currentSelectedFormulaInfo.value = defaultFormulaInfo
const id = nanoid()
initFormulaInfo.value = {
...defaultFormulaInfo,
formula_id: id,
}
}
const addFormula = () => {
const id = nanoid()
formulaList.value.push({
...defaultFormulaInfo,
continued_gs: 300,
name: `配方${formulaList.value.length + 1}`,
formula_id: new Date().getTime().toString(),
formula_id: id, // 配方ID,
})
}
return {
@ -68,5 +98,6 @@ export const useFormulaStore = defineStore('formula', () => {
updateSelectedFormulaData,
addFormula,
initFormulaData,
updateFormulaList,
}
})

61
src/stores/homeStore.ts

@ -1,6 +1,16 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 空闲 idle
// 初始化 init
// 预热 preheat
// 消毒 disinfection
// 降解 degradation
// 结束 finished
// 消毒前除湿 dehumidification_before_disinfection
// 消毒后除湿 dehumidification_after_disinfection
// 清空管路 empty_liquid_from_the_line
const h2O2Data: Home.DisplayrelyMgrParams[] = [{
type: 'inside',
title: '仓内',
@ -33,7 +43,19 @@ const liquidItem: Home.LiquidData = {
export const useHomeStore = defineStore('home', () => {
const h2O2SensorData = ref(h2O2Data)
const liquidData = ref(liquidItem)
const logLevelList = [{
label: '1级',
value: 1,
}, {
label: '2级',
value: 2,
}, {
label: '3级',
value: 3,
}, {
label: '4级',
value: 4,
}]
const updateHomeData = (data: Home.DisplayrelyMgr[]) => {
if (data && data.length) {
data.forEach((item, index) => {
@ -49,6 +71,7 @@ export const useHomeStore = defineStore('home', () => {
liquidData.value = liquidInfo
}
// 开始消毒时的逻辑判断
const startDisinfect = () => {
// 是否进行设备的初始化 return false
// 是否选择了配方 return false
@ -60,20 +83,54 @@ export const useHomeStore = defineStore('home', () => {
*className: "DisinfectionCtrlService",
*fnName: "start",
**/
disinfectionState.value.state = 'disinfection'
return true
}
const deviceStete = ref()
const stopDisinfect = () => {
disinfectionState.value.state = 'finished'
}
// 设备状态
const deviceStete = ref({
state: 'idle',
})
const setDeviceState = (deviceInfo: Device.State) => {
deviceStete.value = deviceInfo
}
// 当前设备正在消毒时的状态
const disinfectionState = ref<Home.DisinfectState>({
curStateRemainTimeS: 0,
h2o2SensorData: [
{
h2o2: 0,
online: true,
rh: 32.79999923706055,
rs: 32.80025100708008,
temp: 25.899999618530273,
},
],
injectedVelocity: 0,
nlog: 0,
state: 'idle',
statedisplayName: '空闲',
tlog: 0,
})
const updateHomeDisinfectionState = (disinfectState: Home.DisinfectState) => {
disinfectionState.value = disinfectState
}
return {
h2O2SensorData,
liquidData,
logLevelList,
deviceStete,
disinfectionState,
updateHomeData,
updateHomeLiquid,
startDisinfect,
stopDisinfect,
setDeviceState,
updateHomeDisinfectionState,
}
})

14
src/stores/liquidStore.ts

@ -0,0 +1,14 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useLiquidStore = defineStore('Liquid', () => {
const liquidWorkState = ref<Liquid.LiquidState>('idle')
const updateLiquidWorkState = (state: Liquid.LiquidState) => {
liquidWorkState.value = state
}
return {
liquidWorkState,
updateLiquidWorkState,
}
})

14
src/stores/sealStore.ts

@ -0,0 +1,14 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useSealStore = defineStore('seal', () => {
const sealState = ref('idle')
const updateSealState = (status: Seal.SealState) => {
sealState.value = status
}
return {
sealState,
updateSealState,
}
})

30
src/stores/settingStore.ts

@ -19,8 +19,6 @@ export const useSettingStore = defineStore('setting', () => {
code: 'deviceInfo',
}]
const addUserVisible = ref(false)
const historyList = [
{
name: '2021-01-03',
@ -32,14 +30,40 @@ export const useSettingStore = defineStore('setting', () => {
name: '2021-01-01',
},
]
/* ********************** 用户管理 **************************** */
const userList = ref<Setting.User[]>([{
uid: 'admin(管理员)',
passwd: '111',
is_admin: true,
id: '02384',
}, {
uid: '测试人员',
passwd: '222',
is_admin: true,
id: '1111',
}])
const addUserVisible = ref(false)
const userModalState = ref('add')
const currentEditUser = ref<Setting.User>()
const updateUserModalState = (state: string) => {
userModalState.value = state
}
const updateCurrentEditUser = (userItem: Setting.User) => {
currentEditUser.value = userItem
}
const updateVisible = (visible: boolean) => {
addUserVisible.value = visible
}
return {
settingMenus,
historyList,
addUserVisible,
userModalState,
currentEditUser,
userList,
updateVisible,
updateUserModalState,
updateCurrentEditUser,
}
})

26
src/types/home.d.ts

@ -19,4 +19,30 @@ declare namespace Home {
workState: string
workStateDisplay: string
}
interface GrandsonMethods {
getFormData: () => void
}
interface DisinfectH2owState {
h2o2: string | number
temp: string | number
rh: string | number
rs: string | number
online?: boolean
}
interface DisinfectState {
curStateRemainTimeS: number
h2o2SensorData: DisinfectH2owState[]
injectedVelocity?: number
nlog?: number
state: string
statedisplayName: string
tlog?: number
}
interface Map {
[key: string]: any
}
}

5
src/types/liquid.d.ts

@ -0,0 +1,5 @@
declare namespace Liquid {
// idle,addingLiquid,emptyLineLiquid 空闲,加液中,清空管路中
// idle,work 空闲,排液中
type LiquidState = 'idle' | 'addingLiquid' | 'emptyLineLiquid' | 'work'
}

8
src/types/seal.d.ts

@ -0,0 +1,8 @@
declare namespace Seal {
// idle // 空闲
// initDevice //初始化设备
// inflating //打压中
// leakTesting  // 检漏中
// stopping   // 停止中
type SealState = 'idle' | 'initDevice' | 'inflating' | 'leakTesting' | 'stopping'
}

2
src/types/setting.d.ts

@ -8,6 +8,6 @@ declare namespace Setting {
id?: string | number,
passwd: string,
confirmPassword?: string,
is_admin: string,
is_admin: string | boolean,
}
}

418
src/views/debug/index.vue

@ -1,220 +1,276 @@
import FtButton from 'components/common/FTButton/index.vue';
<script lang="ts" setup></script>
<script lang="ts" setup>
import { roundNumber } from 'libs/utils'
import { useHomeStore } from 'stores/homeStore'
import { ref, watch } from 'vue'
const homeStore = useHomeStore()
const insideEnvParams = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[0])
const env1Params = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[1])
const env2Params = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[2])
watch(() => homeStore.h2O2SensorData, (newVal) => {
if (newVal && newVal.length) {
insideEnvParams.value = newVal[0]
}
}, { deep: true })
//
const onAddLiquid = () => {
}
//
const onDisLiquid = () => {
}
//
const onOffLiquidMotor = () => {
}
//
const onOpenAirPump = () => {
}
//
const onCloseAirPump = () => {
}
//
const onOpenHeat = () => {}
//
const onCloseHeat = () => {}
//
const queryEvaporationState = () => {}
//
const onSprayMetor = () => {}
//
const onRefluxMetor = () => {}
//
const onClosePump = () => {}
//
const onOpenFan = () => {}
//
const onCloseFan = () => {}
//
const onBottomState = () => {}
</script>
<template>
<div class="dashboard-container">
<main class="main-content">
<section class="debug-left">
<div>
<div class="debug-inside">
<div class="debug-label">
仓内
</div>
<div>
<span>温度</span>
<span class="debug-text">28</span>°C
</div>
<div>
<span>湿度</span>
<span class="debug-text">60</span>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">60</span>PPM
</div>
<!-- 上下布局 -->
<section class="debug-upper">
<div class="debug-left-lh">
<div class="debug-label">
仓内环境
</div>
<div class="debug-env-2">
<div class="debug-label">探头2</div>
<div>
<span>温度</span>
<span class="debug-text">28</span>°C
</div>
<div>
<span>湿度</span>
<span class="debug-text">60</span>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">60</span>PPM
</div>
<div>
<span>温度</span>
<span class="debug-text">{{ roundNumber(insideEnvParams.temp, 2) }}</span>°C
</div>
<div class="debug-env-2">
<div class="debug-label">
加液蠕动泵
</div>
<div class="debug-bw">
<FtButton>
<div>加液</div>
</FtButton>
</div>
<div>
<FtButton>
<div>排液</div>
</FtButton>
</div>
<div>
<FtButton>
<div>关闭</div>
</FtButton>
</div>
<div>
<span>湿度</span>
<span class="debug-text">{{ roundNumber(insideEnvParams.rh, 2) }}</span>
</div>
<div class="debug-env-2">
<div class="debug-label">
空压机
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton>
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton>
<div>关闭</div>
</FtButton>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">{{ roundNumber(insideEnvParams.rs, 2) }}</span>PPM
</div>
<div class="debug-env-2">
<div class="debug-label">
加热片
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton>
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton>
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">探头1环境</div>
<div>
<span>温度</span>
<span class="debug-text">{{ roundNumber(env1Params.temp, 2) }}</span>°C
</div>
<div class="debug-env-2">
<div class="debug-label">
蒸发仓水浸
</div>
<div class="debug-bw">
<span class="debug-text">没水</span>
</div>
<div>
<FtButton>
<div>读取状态</div>
</FtButton>
</div>
<div>
<span>湿度</span>
<span class="debug-text">{{ roundNumber(env2Params.rh, 2) }}</span>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">60</span>PPM
</div>
</div>
</section>
<section class="debug-right">
<div>
<div class="debug-env-1">
<div class="debug-label">探头2</div>
<div>
<span>温度</span>
<span class="debug-text">28</span>°C
</div>
<div>
<span>湿度</span>
<span class="debug-text">60</span>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">60</span>PPM
</div>
<div class="debug-left-lh">
<div class="debug-label">探头2环境</div>
<div>
<span>温度</span>
<span class="debug-text">{{ roundNumber(env2Params.temp, 2) }}</span>°C
</div>
<div class="debug-env-1">
<div class="debug-label">
&nbsp;
</div>
<div>
<span>湿度</span>
<span class="debug-text">{{ roundNumber(env2Params.rh, 2) }}</span>
</div>
<div class="debug-env-1">
<div class="debug-label">
注射蠕动泵
</div>
<div class="debug-bw">
<FtButton>
<div>加液</div>
</FtButton>
</div>
<div>
<FtButton>
<div>排液</div>
</FtButton>
</div>
<div>
<FtButton>
<div>关闭</div>
</FtButton>
</div>
<div>
<span>过氧化氢浓度</span>
<span class="debug-text">{{ roundNumber(env2Params.rs, 2) }}</span>PPM
</div>
<div class="debug-env-1">
<div class="debug-label">
风机
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton>
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton>
<div>关闭</div>
</FtButton>
</div>
</section>
<div class="debug-lower">
<section >
<div>
<div class="debug-left-lh">
<div class="debug-label">
加液蠕动泵
</div>
<div class="debug-bw">
<FtButton type="primary" @click="onAddLiquid">
<div>加液</div>
</FtButton>
</div>
<div>
<FtButton type="primary" @click="onDisLiquid">
<div>排液</div>
</FtButton>
</div>
<div>
<FtButton @click="onOffLiquidMotor">
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">
空压机
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton type="primary" @click="onOpenAirPump">
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton @click="onCloseAirPump">
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">
加热片
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton type="primary" @click="onOpenHeat">
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton @click="onCloseHeat">
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">
蒸发仓水浸
</div>
<div class="debug-bw">
<span class="debug-text">没水</span>
</div>
<div>
<FtButton type="primary" @click="queryEvaporationState">
<div>读取状态</div>
</FtButton>
</div>
</div>
</div>
<div class="debug-env-1">
<div class="debug-label">
设备底部水浸
</div>
<div class="debug-bw">
<span class="debug-text">没水</span>
</div>
<div>
<FtButton>
<div>读取状态</div>
</FtButton>
</section>
<section class="debug-right">
<div>
<div class="debug-left-lh">
<div class="debug-label">
注射蠕动泵
</div>
<div class="debug-bw">
<FtButton type="primary" @click="onSprayMetor">
<div>喷射</div>
</FtButton>
</div>
<div>
<FtButton type="primary" @click="onRefluxMetor">
<div>回流</div>
</FtButton>
</div>
<div>
<FtButton @click="onClosePump">
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">
风机
</div>
<div class="debug-bw">
<span class="debug-text">0.00A</span>
</div>
<div>
<FtButton type="primary" @click="onOpenFan">
<div>打开</div>
</FtButton>
</div>
<div>
<FtButton @click="onCloseFan">
<div>关闭</div>
</FtButton>
</div>
</div>
<div class="debug-left-lh">
<div class="debug-label">
设备底部水浸
</div>
<div class="debug-bw">
<span class="debug-text">没水</span>
</div>
<div>
<FtButton type="primary" @click="onBottomState">
<div>读取状态</div>
</FtButton>
</div>
</div>
</div>
</div>
</section>
</section>
</div>
</main>
</div>
</template>
<style lang="scss" scoped>
$lineHeight: 12vh;
.main-content{
display: grid;
grid-template-columns: repeat(2,1fr);
height: 100%;
overflow: hidden;
background: $gradient-color;
padding-top: 50px;
padding-left: 30px;
line-height: 6rem;
.debug-upper{
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
}
.debug-lower{
display: grid;
grid-template-columns: repeat(2,1fr);
}
.debug-left{
.debug-inside{
display: flex;
gap: 2rem;
}
.debug-env-2{
display: flex;
gap: 2rem;
}
}
.debug-right{
.debug-env-1{
display: flex;
gap: 2rem;
}
}
}
.debug-left-lh{
display: flex;
gap: 2rem;
height: $lineHeight;
}
.debug-bw{
width: 8vw;
}

2
src/views/formula/index.vue

@ -27,7 +27,7 @@ const onAddFormula = () => {
</div>
</div>
<div class="formula-right">
<FormulaConfig />
<FormulaConfig type="formula" />
</div>
</main>
</template>

68
src/views/home/index.vue

@ -8,9 +8,12 @@ import homeRunSvg from 'assets/images/home/home-run.svg'
import homeSetting from 'assets/images/home/home-setting.svg'
import homeStart from 'assets/images/home/home-start.svg'
import SelectModal from 'components/common/SelectModal/index.vue'
import Config from 'components/home/config.vue'
import Environment from 'components/home/Environment.vue'
import CountdownTimer from 'libs/countdownTimer'
import { sealStateMap } from 'libs/utils'
import { useHomeStore } from 'stores/homeStore'
import { onMounted, ref, watch } from 'vue'
import { onMounted, provide, ref, watch, watchEffect } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
@ -24,20 +27,18 @@ const liquidInfo = ref<Home.LiquidData>(homeStore.liquidData)
const deviceState = ref(1)
const isModalOpen = ref(false)
const formulaInfo = ref()
const options = ref([{
label: '1级',
value: 1,
}, {
label: '2级',
value: 2,
}, {
label: '3级',
value: 3,
}])
const configRef = ref()
const options = ref(homeStore.logLevelList)
const selectedValue = ref()
const disinfectionState = ref(homeStore.disinfectionState)
const curStateRemainTimeS = ref<string>('')
provide<(methods: Home.GrandsonMethods) => void>('registerGrandsonMethods', (methods) => {
configRef.value = methods
})
onMounted(() => {
formulaInfo.value = formulaStore.currentSelectedFormulaInfo || formulaStore.initFormulaInfo
formulaInfo.value = formulaStore.currentSelectedFormulaInfo
})
watch(() => homeStore.h2O2SensorData, (newVal) => {
@ -50,6 +51,10 @@ watch(() => formulaStore.currentSelectedFormulaInfo, (newVal) => {
formulaInfo.value = newVal
})
watchEffect(() => {
disinfectionState.value = homeStore.disinfectionState
})
//
const onStartDisinfect = () => {
const isStart = homeStore.startDisinfect()
@ -57,6 +62,12 @@ const onStartDisinfect = () => {
if (isStart) {
//
}
if (disinfectionState.value.state === 'disinfection') {
const countdownTimer = new CountdownTimer(10 * 60 * 1000, (times: string) => {
curStateRemainTimeS.value = times
})
console.log('countdownTimer--', countdownTimer)
}
}
//
@ -80,13 +91,26 @@ const handleCancel = () => {
}
//
const disinfectFormulaVisible = ref(false)
const onDisinfectConfig = () => {
router.push('/home/config')
// router.push('/home/config')
formulaStore.initFormulaData()
disinfectFormulaVisible.value = true
}
const onShowChart = () => {
router.push('/home/chart')
}
//
const onSave = () => {
const formData = configRef.value?.getFormData()
formulaStore.updateSelectedFormulaData(formData)
onClose()
}
const onClose = () => {
disinfectFormulaVisible.value = false
}
</script>
<template>
@ -171,12 +195,17 @@ const onShowChart = () => {
<div>结束消毒</div>
</div>
</div>
<div class="home-remain-time">
<!-- 开始消毒时显示 -->
<div v-if="disinfectionState.state !== 'idle'" class="home-remain-time">
<div class="home-remaini-label">
预计剩余时间:
</div>
<div class="home-remaini-value">
10:00:20
<!-- 消毒中显示时间其它情况显示状态 -->
<div v-if="disinfectionState.state === 'disinfection'" class="home-remaini-value">
{{ curStateRemainTimeS }}
</div>
<div v-else>
{{ sealStateMap[disinfectionState.state] }}
</div>
</div>
<div class="home-start-opt">
@ -210,6 +239,11 @@ const onShowChart = () => {
<div v-else>
<router-view />
</div>
<ft-dialog v-model="disinfectFormulaVisible" width="80vw" :ok-handle="onSave" @cancel="onClose">
<div>
<Config ref="configRef" />
</div>
</ft-dialog>
</div>
</template>
@ -418,7 +452,7 @@ const onShowChart = () => {
.home-remain-time{
background: #F6FAFE;
margin-top: 5rem;
margin-top: 8rem;
width: 27vw;
height: 8vh;
border-radius: 12px;

50
src/views/liquid/index.vue

@ -1,14 +1,19 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useLiquidStore } from '@/stores/liquidStore'
import homeStop from 'assets/images/home/home-finish.svg'
import homeStart from 'assets/images/home/home-start.svg'
import SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
import LiquidLevel from 'components/liquid/LiquidLevel.vue'
import { onMounted, ref, watch } from 'vue'
import { onMounted, ref, watchEffect } from 'vue'
const liquidStore = useLiquidStore()
const stopatg = ref()
const inputValue = ref('')
const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('number')
const softKeyboardRef = ref()
const workState = ref(liquidStore.liquidWorkState)
onMounted(() => {
document.addEventListener('click', () => {
if (softKeyboardRef.value) {
@ -16,9 +21,13 @@ onMounted(() => {
}
})
})
watch(inputValue, (newVal) => {
stopatg.value = newVal
watchEffect(() => {
stopatg.value = inputValue.value
workState.value = liquidStore.liquidWorkState
console.log('workState.value---', workState.value)
})
const openKeyboard = () => {
setTimeout(() => {
keyboardVisible.value = true
@ -31,10 +40,24 @@ const handleConfirm = (value: string) => {
const onStartAddLiquid = () => {
// TODO
if (!stopatg.value || stopatg.value < 0) {
FtMessage.warning('请输入加液容量')
return
}
liquidStore.updateLiquidWorkState('addingLiquid')
}
const onStopAddLiquid = () => {
liquidStore.updateLiquidWorkState('idle')
}
const onStartDrainLiquid = () => {
// TODO
if (workState.value !== 'idle') {
return
}
liquidStore.updateLiquidWorkState('work')
console.log('正在执行排液')
}
</script>
@ -72,11 +95,17 @@ const onStartDrainLiquid = () => {
<!-- 操作区 -->
<div class="liquid-opt">
<div>
<div class="liquid-add-btn liquid-start" @click="onStartAddLiquid">
<img :src="homeStart" alt="">
<div>开始加液</div>
<div class="liquid-add-btn" >
<div v-if="workState === 'idle'" class="liquid-add-btn liquid-start" @click="onStartAddLiquid">
<img :src="homeStart" alt="">
<div>开始加液</div>
</div>
<div v-else class="liquid-add-btn liquid-stop" @click="onStopAddLiquid">
<img :src="homeStop" alt="">
<div>停止加液</div>
</div>
</div>
<div class="liquid-add-btn liquid-drain" @click="onStartDrainLiquid">
<div class="liquid-add-btn liquid-drain" :class="{ 'liquid-drain-disable': workState !== 'idle' }" @click="onStartDrainLiquid">
<img :src="homeStart" alt="">
<div>开始排液</div>
</div>
@ -165,10 +194,17 @@ const onStartDrainLiquid = () => {
background: #31CB7A;
}
.liquid-stop{
background: #FF6767;
}
.liquid-drain{
background: #2892F3;
margin-top: 1.5rem;
}
.liquid-drain-disable{
background: #cccccc;
}
}
}
.el-button {

125
src/views/seal/index.vue

@ -1,15 +1,24 @@
<script lang="ts" setup>
import { useSealStore } from '@/stores/sealStore'
import homeFinish from 'assets/images/home/home-finish.svg'
import homeStart from 'assets/images/home/home-start.svg'
import SealInstrumentSvg from 'assets/images/seal/seal-instrument.svg'
import DashboardChart from 'components/seal/DashboardChart.vue'
// import { ref } from 'vue'
import { ref, watch } from 'vue'
const sealStore = useSealStore()
const sealState = ref(sealStore.sealState)
watch(() => sealStore.sealState, (newVal) => {
sealState.value = newVal
})
// const pressure = ref()
const onStartTest = () => {
//
sealStore.updateSealState('initDevice')
}
const onFinishTest = () => {
sealStore.updateSealState('idle')
}
</script>
@ -17,18 +26,6 @@ const onFinishTest = () => {
<div class="dashboard-container">
<main class="main-content">
<div class="seal-left">
<div class="left-title">
<div class="title-text-test">
<img :src="SealInstrumentSvg" alt="仪表盘">
<div class="title-text">
测试前气压
</div>
<div class="title-text title-text-kpa">
<span>2</span>
<span class="title-kpa-pl">KPa</span>
</div>
</div>
</div>
<!-- 仪表盘 -->
<div class="seal-chart">
<div class="chart-ml">
@ -42,7 +39,7 @@ const onFinishTest = () => {
</div>
</div>
<div class="seal-status">
<div class="seal-diff-text">气压差值</div>
<div class="seal-diff-text">气压差值</div>
<div class="seal-diff-statue seal-diff-text">
未开始
</div>
@ -51,14 +48,28 @@ const onFinishTest = () => {
</div>
</div>
<div class="seal-right">
<div>
<div class="seal-add-btn seal-start" @click="onStartTest">
<img :src="homeStart" alt="">
<div>启动测试</div>
<div class="left-title">
<div class="title-text-test">
<img :src="SealInstrumentSvg" alt="仪表盘">
<div class="title-text">
测试前气压
</div>
<div class="title-text title-text-kpa">
<span>2</span>
<span class="title-kpa-pl">KPa</span>
</div>
</div>
<div class="seal-add-btn seal-stop" @click="onFinishTest">
<img :src="homeFinish" alt="">
<div>停止测试</div>
</div>
<div class="seal-right-btn">
<div>
<div class="seal-add-btn seal-start" :class="{ 'seal-start-show': sealState !== 'idle' }" @click="onStartTest">
<img :src="homeStart" alt="">
<div>启动测试</div>
</div>
<div class="seal-add-btn seal-stop" :class="{ 'seal-stop-show': sealState !== 'idle' }" @click="onFinishTest">
<img :src="homeFinish" alt="">
<div>停止测试</div>
</div>
</div>
</div>
</div>
@ -78,33 +89,12 @@ const onFinishTest = () => {
grid-column: 1 / 3;
box-shadow: 0px 1px 5px 0px rgba(9, 39, 62, 0.15);
background: $gradient-color;
.left-title{
padding-top: 40px;
padding-left: 28px;
display: flex;
height: 5rem;
.title-text-test{
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
.title-text{
font-size: 20px;
}
.title-text-kpa{
color: #409EFF;
}
.title-kpa-pl{
padding-left: 5px;
font-weight: bold;
}
}
}
.seal-chart{
margin-left: 1rem;
}
.chart-ml{
margin-left: 3rem;
margin: 3rem;
height: 25rem;
}
}
}
@ -119,9 +109,11 @@ const onFinishTest = () => {
align-items: center;
background: #F6FAFE;
width: 15rem;
height: 5rem;
border-radius: 15px;
}
.seal-time-text{
font-size: 1rem;
font-size: 1.5rem;
font-weight: 500;
padding-left: 0.5rem;
}
@ -134,7 +126,7 @@ const onFinishTest = () => {
font-weight: 600;
}
.seal-diff-text{
font-size: 1rem;
font-size: 1.5rem;
font-weight: 500;
padding-left: 0.5rem;
}
@ -164,14 +156,47 @@ const onFinishTest = () => {
background: #31CB7A;
}
.seal-start-show{
background: #e6e6e6;
}
.seal-stop{
background: #e8e8e8;
}
.seal-stop-show {
background: #FF6767;
}
.seal-right{
background: $gradient-color;
display: flex;
justify-content: center;
align-items: center;
display: grid;
grid-template-rows: 1fr 1fr 1fr;
.seal-right-btn{
display: flex;
justify-content: center;
align-items: center;
}
.left-title{
padding-top: 3.5vw;
padding-left: 3.5vw;
display: flex;
.title-text-test{
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
.title-text{
font-size: 20px;
}
.title-text-kpa{
color: #409EFF;
}
.title-kpa-pl{
padding-left: 5px;
font-weight: bold;
}
}
}
}
</style>
Loading…
Cancel
Save