Browse Source

实现业务功能

master
LiLongLong 2 months ago
parent
commit
83aa977be8
  1. 44
      src/apis/system.ts
  2. 93
      src/app.vue
  3. 2
      src/assets/styles/variable.scss
  4. 2
      src/components/common/BTButton/index.vue
  5. 2
      src/components/common/FTDialog/index.vue
  6. 37
      src/components/common/SoftKeyboard/index.vue
  7. 100
      src/components/formula/FormulaConfig.vue
  8. 39
      src/components/formula/FormulaTable.vue
  9. 2
      src/components/home/Environment.vue
  10. 2
      src/components/home/HomeFormula.vue
  11. 7
      src/components/home/HomeLogLevel.vue
  12. 129
      src/components/home/HomeOperation.vue
  13. 28
      src/components/home/HomeSetting.vue
  14. 21
      src/components/home/config.vue
  15. 25
      src/components/liquid/LiquidLevel.vue
  16. 11
      src/components/liquid/ScaleMark.vue
  17. 22
      src/components/seal/DashboardChart.vue
  18. 105
      src/components/setting/History.vue
  19. 67
      src/components/setting/HistoryDetail.vue
  20. 95
      src/layouts/default.vue
  21. 13
      src/libs/countdownTimer.ts
  22. 27
      src/libs/messageBox.ts
  23. 398
      src/libs/pinyinDict.json
  24. 65
      src/libs/socket.ts
  25. 47
      src/libs/timer.ts
  26. 11
      src/libs/utils.ts
  27. 2
      src/router/index.ts
  28. 23
      src/stores/deviceStore.ts
  29. 97
      src/stores/formulaStore.ts
  30. 80
      src/stores/homeStore.ts
  31. 68
      src/stores/initHomeData.ts
  32. 39
      src/stores/liquidStore.ts
  33. 12
      src/stores/sealStore.ts
  34. 67
      src/stores/systemStore.ts
  35. 9
      src/types/device.d.ts
  36. 6
      src/types/home.d.ts
  37. 12
      src/types/liquid.d.ts
  38. 9
      src/types/seal.d.ts
  39. 10
      src/types/setting.d.ts
  40. 8
      src/types/websocket.d.ts
  41. 64
      src/views/audit/index.vue
  42. 37
      src/views/debug/index.vue
  43. 58
      src/views/formula/index.vue
  44. 5
      src/views/home/chart.vue
  45. 50
      src/views/home/index.vue
  46. 160
      src/views/liquid/index.vue
  47. 2
      src/views/login/index.vue
  48. 230
      src/views/seal/index.vue
  49. 4
      src/views/setting/index.vue

44
src/apis/system.ts

@ -1,24 +1,36 @@
import { createWebSocket } from 'libs/socket'
const wsClient = createWebSocket()
export async function sendCmd(params: { className: string, fnName: string, params: Record<string, any> }) {
export async function sendCmd(resParams: { className: string, fnName: string, params: Record<string, any> }) {
const res = await wsClient.waitAndSend({
messageType: 'Command',
fnName: params.fnName,
className: params.className,
fnName: resParams.fnName,
className: resParams.className,
messageId: `msg_${Date.now()}`,
params: params.params,
params: resParams.params,
})
if (res.ackcode === 0) {
return res.rely
}
else {
throw new Error(res.message)
}
}
export async function syncSendCmd(resParams: { className: string, fnName: string, params: Record<string, any> }) {
return await wsClient.sendRequest({
messageType: 'Command',
fnName: resParams.fnName,
className: resParams.className,
messageId: `msg_${Date.now()}`,
params: resParams.params,
})
}
export async function subscribeEvent(fromFn: string | '*', callback: (response: Socket.WebSocketResponse) => void) {
wsClient.socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data)
if (data.messageType === 'Report' && data.fromFn === fromFn) {
callback && callback(data)
}
})
return res
}
// export const sendCmd = (params: { className: string, fnName: string }) => async () => {
// const res = await wsClient.waitAndSend({
// messageType: 'Command',
// fnName: params.fnName,
// className: params.className,
// messageId: `msg_${Date.now()}`,
// params: {
// },
// })
// return res
// }

93
src/app.vue

@ -1,16 +1,60 @@
<script setup lang='ts'>
import { sendCmd } from '@/apis/system'
import { useDeviceStore } from 'stores/deviceStore'
import { useHomeStore } from 'stores/homeStore'
import { onMounted, ref } from 'vue'
import { useLiquidStore } from 'stores/liquidStore'
import { onBeforeMount, ref } from 'vue'
import { useFormulaStore } from './stores/formulaStore'
const deviceStore = useDeviceStore()
const homeStore = useHomeStore()
onMounted(async () => {
setInterval(() => {
initData()
}, 1000)
const formulaStore = useFormulaStore()
const liquidStore = useLiquidStore()
onBeforeMount(async () => {
//
startProgress()
})
const initDeviceInfo = async () => {
const deviceParams = {
className: 'DeviceInfoMgrService',
fnName: 'getDeviceInfo',
params: {},
}
const res = await sendCmd(deviceParams)
deviceStore.updateDeviceInfo(res)
return res
}
const getFormualDefaultData = async () => {
const defaultParams = {
className: 'SettingMgrService',
fnName: 'getAllSetting',
params: {},
}
const res = await sendCmd(defaultParams)
formulaStore.updateFormulaConfigData(res)
}
const progress = ref(0)
let timer: any = null
const version = __APP_VERSION__
const startProgress = () => {
timer = setInterval(async () => {
const randomStep = Math.floor(Math.random() * 9 + 1)
progress.value = Math.min(progress.value + randomStep, 100)
await initData()
//
await initLiquidConfig()
await initDeviceInfo()
//
await getFormualDefaultData()
if (progress.value >= 100) {
clearInterval(timer)
}
}, 100)
}
const initData = async () => {
const envParams = {
fnName: 'readH2O2SensorData',
@ -18,30 +62,27 @@ const initData = async () => {
params: {},
}
const resData = await sendCmd(envParams)
if (resData.rely && resData.rely.val.length) {
homeStore.updateHomeData(resData.rely.val)
if (resData.val.length) {
homeStore.updateHomeData(resData.val)
}
//
const disinfectionParams = {
className: 'DisinfectionCtrlService',
className: 'DisinfectionCtrlServiceExt',
fnName: 'getState',
params: {},
}
const disinfectionData = await sendCmd(disinfectionParams)
if (disinfectionData.rely) {
homeStore.updateHomeDisinfectionState(disinfectionData.rely)
}
homeStore.updateHomeDisinfectionState(disinfectionData)
// 使
const liquidParams = {
fnName: 'getState',
className: 'AddLiquidService',
params: {},
}
const liquidData = await sendCmd(liquidParams)
if (liquidData.rely) {
homeStore.updateHomeLiquid(liquidData.rely)
}
liquidStore.updateLiquidState(liquidData)
const deviceParams = {
className: 'AppCore',
@ -50,23 +91,17 @@ const initData = async () => {
}
//
const deviceData = await sendCmd(deviceParams)
if (deviceData.rely) {
homeStore.setDeviceState(deviceData.rely)
}
homeStore.setDeviceState(deviceData)
}
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)
const initLiquidConfig = async () => {
const params = {
className: 'AddLiquidService',
fnName: 'getServiceConfig',
params: {},
}
const liquidConfig = await sendCmd(params)
liquidStore.initLiquidConfig(liquidConfig)
}
</script>

2
src/assets/styles/variable.scss

@ -4,4 +4,4 @@ $danger-color: #DF1515;
$warn-color: #EE8223;
$info-color: #909399;
$gradient-color: linear-gradient(185deg, rgb(175 216 255) -90%, #fff 24%);
$main-container: calc(100vh - 16vh)
$main-container-height: calc(100vh - 16vh)

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

@ -88,7 +88,7 @@ const handleClick = (event: MouseEvent) => {
:loading="loading"
:style="{
backgroundColor: disabled ? '#e8e8e8' : bgColor,
color: textColor,
color: disabled ? '#939393' : textColor,
width,
height,
borderRadius,

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

@ -16,7 +16,6 @@ const props = defineProps({
},
okHandle: {
type: Function,
default: () => {},
},
})
const emits = defineEmits(['update:visible', 'ok', 'cancel'])
@ -65,6 +64,7 @@ watch(
@click="cancel"
/>
<bt-button
v-if="okHandle"
button-text="确认"
type="primary"
@click="okHandle"

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

@ -1,4 +1,5 @@
<script lang="ts" setup>
// import pinyinDict from 'libs/pinyinDict.json'
import { computed, defineEmits, defineProps, onMounted, ref, watch, watchEffect } from 'vue'
const props = defineProps<{
@ -13,7 +14,9 @@ const emits = defineEmits<{
(e: 'confirm', value: string): void
(e: 'close'): void
}>()
const languageType = ref('en')
// const cnList = ref<string[]>([])
// const pinyinMap: Record<string, string> = pinyinDict as unknown as Record<string, string>
onMounted(() => {
document.addEventListener('click', (e: any) => {
if (isOpen.value && !e.target?.name) {
@ -46,10 +49,13 @@ const keyboardLayout = computed(() => {
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '/'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 'enter'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '?', '.', ':'],
[' ', 'close'],
[' ', 'en', 'close'],
]
})
const specialKeys = ['del', 'enter', ' ']
// const handleKeyCn = (cn: string) => {
// console.log('cn===', cn)
// }
const handleKeyPress = (key: string) => {
activeKey.value = key
setTimeout(() => {
@ -66,8 +72,19 @@ const handleKeyPress = (key: string) => {
else if (key === 'close') {
closeKeyboard()
}
else if ((key === 'en' || key === 'cn') && props.keyboardType === 'text') {
languageType.value = key === 'en' ? 'cn' : 'en'
keyboardLayout.value[4][1] = key === 'en' ? 'cn' : 'en'
}
else {
emits('update:modelValue', props.modelValue + key)
const keyValue = props.modelValue + key
emits('update:modelValue', keyValue)
// if (props.keyboardType === 'text' && languageType.value === 'cn') {
// const cn = pinyinMap[keyValue]
// if (cn.length) {
// cnList.value = cn as unknown as string[]
// }
// }
}
}
@ -89,6 +106,18 @@ watch(() => props.isVisible, (newVal) => {
</button>
</div> -->
<div class="keyboard-body">
<!-- <div v-if="keyboardType === 'text' && cnList.length">
<button
v-for="(cnName, cnIndex) in cnList"
:key="cnIndex"
@click="(e) => {
e.stopPropagation()
handleKeyCn(cnName)
}"
>
{{ cnName }}
</button>
</div> -->
<div class="keyboard-row" v-for="(row, index) in keyboardLayout" :key="index">
<button
v-for="(key, keyIndex) in row"
@ -105,7 +134,7 @@ watch(() => props.isVisible, (newVal) => {
handleKeyPress(key)
}"
>
{{ key === ' ' ? '空格' : key === 'del' ? '删除' : key === 'enter' ? '确认' : key === 'close' ? '关闭' : key }}
{{ key === ' ' ? '空格' : key === 'del' ? '删除' : key === 'enter' ? '确认' : key === 'close' ? '关闭' : key === 'en' ? '英文' : key === 'cn' ? '中文' : key }}
</button>
</div>
</div>

100
src/components/formula/FormulaConfig.vue

@ -1,13 +1,17 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useFormulaStore } from '@/stores/formulaStore'
import { syncSendCmd } from 'apis/system'
import SelectModal from 'components/common/SelectModal/index.vue'
import SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
import { formulaNameMap } from 'libs/constant'
import { inject, onMounted, ref, watch } from 'vue'
import { cloneDeep } from 'lodash'
import { inject, onMounted, ref, watch, watchEffect } from 'vue'
const props = defineProps<{
type: string
}>()
const formulaStore = useFormulaStore()
const formData = ref<Record<string, any>>({
...formulaStore.initFormulaInfo,
@ -17,8 +21,7 @@ const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('number')
const softKeyboardRef = ref()
const focusedInput = ref<string | null>(null)
const formdataInputStyle = ref('formdata-input-home')
const registerGrandsonMethods = inject<(methods: any) => void>('registerGrandsonMethods')
const registerGrandsonMethods = inject<(methods: any) => void>('registerGrandsonMethods', () => {})
const formulaConfigList = ref(formulaStore.formulaConfigList)
const options = ref(formulaStore.logLevels)
const labelUnitMap: Record<string, any> = {
@ -32,13 +35,7 @@ const labelUnitMap: Record<string, any> = {
loglevel: 'Log',
}
onMounted(() => {
if (props.type === 'home') {
formdataInputStyle.value = 'formdata-input-home'
if (formulaStore.currentSelectedFormulaInfo) {
formData.value = formulaStore.currentSelectedFormulaInfo
}
}
registerGrandsonMethods?.({ getFormData })
registerGrandsonMethods && registerGrandsonMethods({ getFormData })
})
const selectedValue = ref()
@ -47,16 +44,16 @@ const openModal = () => {
isModalOpen.value = true
}
// store
watch(() => formulaStore.currentSelectedFormulaInfo, (newVal) => {
if (newVal) {
formData.value = newVal
watchEffect(() => {
formulaConfigList.value = formulaStore.formulaConfigList
if (props.type === 'home') {
formData.value = cloneDeep(formulaStore.selectedFormulaInfo) || cloneDeep(formulaStore.defaultFormulaInfo)
}
})
watch(() => formulaStore.initFormulaInfo, (newVal) => {
if (newVal) {
formData.value = newVal
else if (props.type === 'setting') {
formData.value = formulaStore.defaultFormulaInfo
}
else {
formData.value = cloneDeep(formulaStore.currentSelectedFormulaInfo) || cloneDeep(formulaStore.defaultFormulaInfo)
}
})
@ -66,10 +63,24 @@ watch(inputValue, (newVal: string | number) => {
if (focusedInput.value !== 'name') {
newVal = Number(newVal)
}
formData.value[focusedInput.value as keyof typeof formData.value] = newVal
formData.value[focusedInput.value] = newVal
}
})
// const getFormualDefaultData = () => {
// const defaultParams = {
// className: 'SettingMgrService',
// fnName: 'getAllSetting',
// params: {},
// }
// syncSendCmd(defaultParams).then((res) => {
// if (res.ackcode === 0) {
// formulaStore.updateFormulaConfigData(res.rely)
// }
// })
// }
// form
const getFormData = () => {
return formData.value
}
@ -77,12 +88,45 @@ const getFormData = () => {
//
watch(formData, (newValue) => {
if (focusedInput.value) {
inputValue.value = newValue[focusedInput.value as keyof typeof formData.value].toString()
inputValue.value = newValue[focusedInput.value].toString()
}
}, { deep: true })
//
const handleSubmit = () => {
formulaStore.updateFormulaList(formData.value as Formula.FormulaItem)
if (!formData.value.name) {
FtMessage.warning('请输入配方名称')
return
}
if (formData.value.formula_id) {
const editParams = {
className: 'SettingMgrService',
fnName: 'updateFormula',
params: {
formula_id: formData.value.formula_id,
formula: cloneDeep(formData.value),
},
}
syncSendCmd(editParams).then(() => {
FtMessage.success('操作成功')
formulaStore.initFormulaList()
// getFormualDefaultData()
})
}
else {
onAddFormula()
}
}
const onAddFormula = () => {
const params = {
className: 'SettingMgrService',
fnName: 'addNewFormula',
params: {},
}
syncSendCmd(params).then(() => {
formulaStore.initFormulaList()
})
}
const openKeyboard = (e: any) => {
@ -135,8 +179,8 @@ const openKeyboardType = (labelName: string) => {
v-prevent-keyboard
:name="item.setting_id"
:controls="false"
:min="item.val_lower_limit"
:max="item.val_upper_limit"
:min="Number(item.val_lower_limit)"
:max="Number(item.val_upper_limit)"
class="formdata-input-home"
:disabled="!item.is_visible_in_setting_page"
@focus="openKeyboard"
@ -160,8 +204,12 @@ const openKeyboardType = (labelName: string) => {
class="formdata-input-home"
:disabled="!item.is_visible_in_setting_page"
>
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">
</el-radio>
<el-radio :label="false">
</el-radio>
</el-radio-group>
</template>
</el-form-item>

39
src/components/formula/FormulaTable.vue

@ -1,21 +1,32 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useFormulaStore } from '@/stores/formulaStore'
import { syncSendCmd } from 'apis/system'
import { ElMessageBox } from 'element-plus'
import { onMounted, ref, watchEffect } from 'vue'
const formulaStore = useFormulaStore()
const selectedIndex = ref<number | null>(null)
const recipes = ref<Formula.FormulaItem[]>([])
onMounted(() => {
recipes.value = formulaStore.formulaList
initFormulaList()
})
watchEffect(() => {
if (formulaStore.formulaList) {
if (formulaStore.formulaList && formulaStore.formulaList.length) {
recipes.value = formulaStore.formulaList as Formula.FormulaItem[]
}
})
const selectedIndex = ref<number | null>(null)
const initFormulaList = () => {
if (formulaStore.formulaList && formulaStore.formulaList.length) {
recipes.value = formulaStore.formulaList as Formula.FormulaItem[]
}
else {
formulaStore.initFormulaList()
}
}
const selectRecipe = (item: Formula.FormulaItem, index: number) => {
selectedIndex.value = selectedIndex.value === index ? null : index
formulaStore.updateSelectedFormulaData(item)
@ -25,7 +36,7 @@ const viewRecipe = (item: Formula.FormulaItem) => {
formulaStore.updateSelectedFormulaData(item)
}
const deleteRecipe = (index: number) => {
const deleteRecipe = (item: Formula.FormulaItem) => {
ElMessageBox.confirm(
'请确认是否删除?',
'删除',
@ -35,11 +46,19 @@ const deleteRecipe = (index: number) => {
type: 'warning',
},
).then(() => {
if (selectedIndex.value === index) {
selectedIndex.value = null
if (item.formula_id) {
const delParams = {
className: 'SettingMgrService',
fnName: 'delFormula',
params: {
formula_id: item.formula_id,
},
}
syncSendCmd(delParams).then(() => {
FtMessage.success('操作成功')
formulaStore.initFormulaList()
})
}
recipes.value.splice(index, 1)
formulaStore.initFormulaData()
})
}
</script>
@ -53,12 +72,12 @@ const deleteRecipe = (index: number) => {
:class="{ selected: selectedIndex === index }"
@click="selectRecipe(item, index)"
>
<span>{{ item.name ? item.name : `配方${index + 1}` }}</span>
<span>{{ item.name }}</span>
<div class="actions">
<button class="view-button" @click.stop="viewRecipe(item)">
查看
</button>
<button class="delete-button" @click.stop="deleteRecipe(index)">
<button class="delete-button" @click.stop="deleteRecipe(item)">
删除
</button>
<span v-if="selectedIndex === index" class="selected-icon">

2
src/components/home/Environment.vue

@ -88,7 +88,7 @@ onMounted(() => {
.env-row{
display: grid;
grid-template-columns: repeat(2, 1fr);
height: 4.8vw;
height: 7.8vh;
padding: 7px;
.env-row-label{
display: flex;

2
src/components/home/HomeFormula.vue

@ -6,7 +6,7 @@ import { ref, watchEffect } from 'vue'
const formulaStore = useFormulaStore()
const formulaInfo = ref()
watchEffect(() => {
formulaInfo.value = formulaStore.currentSelectedFormulaInfo
formulaInfo.value = formulaStore.selectedFormulaInfo
})
</script>

7
src/components/home/HomeLogLevel.vue

@ -1,11 +1,13 @@
<script lang="ts" setup>
import { useFormulaStore } from '@/stores/formulaStore'
import { useHomeStore } from '@/stores/homeStore'
import SelectModal from 'components/common/SelectModal/index.vue'
import { ref } from 'vue'
const formulaStore = useFormulaStore()
const homeStore = useHomeStore()
const options = ref(formulaStore.logLevels)
const loglevel = ref()
const loglevel = ref(homeStore.loglevel)
const selectedValue = ref()
const isModalOpen = ref(false)
const openModal = () => {
@ -15,6 +17,7 @@ const openModal = () => {
const handleConfirm = (value: any) => {
isModalOpen.value = false
loglevel.value = value
homeStore.updateLogLevel(value)
}
const handleCancel = () => {
@ -59,7 +62,7 @@ $input-height: 3rem;
display: flex;
align-items: center;
justify-content: center;
margin-top: 23vh;
margin-top: 26vh;
font-family: Source Han Sans;
font-size: 24px;
.input{

129
src/components/home/HomeOperation.vue

@ -1,52 +1,122 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useHomeStore } from '@/stores/homeStore'
import { useLiquidStore } from '@/stores/liquidStore'
import { useSealStore } from '@/stores/sealStore'
import { useSystemStore } from '@/stores/systemStore'
import { sendCmd, subscribeEvent } from 'apis/system'
import homeFinish from 'assets/images/home/home-finish.svg'
import homeStart from 'assets/images/home/home-start.svg'
import CountdownTimer from 'libs/countdownTimer'
import { sealStateMap } from 'libs/utils'
import { ref, watchEffect } from 'vue'
import { startTimer, stopTimer } from 'libs/countdownTimer'
import { deviceStateMap } from 'libs/utils'
import { computed, onMounted, ref, watchEffect } from 'vue'
const homeStore = useHomeStore()
const deviceState = ref(1)
const countdownTimer = ref()
const liquidStore = useLiquidStore()
const sealStore = useSealStore()
const systemStore = useSystemStore()
const curStateRemainTimeS = ref<string>('')
const disinfectionState = ref(homeStore.disinfectionState)
let isDisinfection = false
watchEffect(() => {
disinfectionState.value = homeStore.disinfectionState
const time = disinfectionState.value.curStateRemainTimeS
if (disinfectionState.value.state === 'disinfection' && !isDisinfection && time > 0) {
isDisinfection = true
startTimer(time * 1000, (times: string) => {
homeStore.updateHomeRemainTime(times)
curStateRemainTimeS.value = times
})
}
})
onMounted(() => {
subscribeEvent('stateUpdate', handleDisinfectState)
})
const handleDisinfectState = (report: Socket.WebSocketResponse<Home.DisinfectState>) => {
if (report.fromClass === 'DisinfectionCtrlServiceExt') {
homeStore.updateHomeDisinfectionState(report.rely)
}
}
//
const onStartDisinfect = () => {
const isStart = homeStore.startDisinfect()
deviceState.value = 2
if (isStart) {
//
const onStartDisinfect = async () => {
if (!homeStore.loglevel) {
FtMessage.warning('选择消毒等级')
return
}
if (disinfectionState.value.state === 'disinfection') {
countdownTimer.value = new CountdownTimer(10 * 60 * 1000, (times: string) => {
homeStore.updateHomeRemainTime(times)
curStateRemainTimeS.value = times
})
countdownTimer.value.init()
//
if (liquidStore.liquidAddWorkState.workState !== 'idle') {
FtMessage.warning('正在进行加液操作...')
return
}
//
if (liquidStore.liquidDrainWorkState.workState !== 'idle') {
FtMessage.warning('正在进行排液操作...')
return
}
//
if (sealStore.sealInfo.workState !== 'idle') {
FtMessage.warning('正在进行排液操作...')
return
}
const startParams = {
className: 'DisinfectionCtrlServiceExt',
fnName: 'start',
params: {
loglevel: homeStore.loglevel,
},
}
systemStore.updateLoading(true)
await sendCmd(startParams)
//
const subParams = {
className: 'DisinfectionCtrlServiceExt',
fnName: 'startStateReport',
params: {},
}
sendCmd(subParams)
}
//
const onFinishDisinfect = () => {
deviceState.value = 1
countdownTimer.value && countdownTimer.value.stopTimer()
homeStore.updateHomeDisinfectionState({
...disinfectionState.value,
state: 'idle',
})
const onFinishDisinfect = async () => {
stopTimer()
const stopParams = {
className: 'DisinfectionCtrlServiceExt',
fnName: 'stop',
params: {
loglevel: homeStore.loglevel,
},
}
systemStore.updateLoading(true)
await sendCmd(stopParams)
//
const params = {
className: 'DisinfectionCtrlServiceExt',
fnName: 'stopStateReport',
params: {},
}
sendCmd(params)
// homeStore.updateHomeDisinfectionState({
// ...disinfectionState.value,
// state: 'idle',
// })
}
const operationState = computed(() => {
return disinfectionState.value.state === 'idle' || disinfectionState.value.state === 'finished'
})
</script>
<template>
<div class="home-disinfect">
<bt-button
v-if="disinfectionState.state === 'idle'"
v-if="operationState"
button-text="开始消毒"
bg-color="#31CB7A"
text-color="#FFFFFF"
@ -77,16 +147,21 @@ const onFinishDisinfect = () => {
</bt-button>
</div>
<!-- 开始消毒时显示 -->
<div v-if="disinfectionState.state !== 'idle'" class="home-remain-time">
<div v-if="!operationState" class="home-remain-time">
<div class="home-remaini-label">
预计剩余时间:
<span v-if="disinfectionState.state === 'disinfection'">
预计剩余时间:
</span>
<span v-else>
消毒状态
</span>
</div>
<!-- 消毒中显示时间其它情况显示状态 -->
<div v-if="disinfectionState.state === 'disinfection'" class="home-remaini-value">
{{ curStateRemainTimeS }}
</div>
<div v-else>
{{ sealStateMap[disinfectionState.state] }}
{{ deviceStateMap[disinfectionState.state] }}
</div>
</div>
</template>

28
src/components/home/HomeSetting.vue

@ -6,7 +6,8 @@ import homeRunSvg from 'assets/images/home/home-run.svg'
import homeSettingSvg from 'assets/images/home/home-setting.svg'
import CascadingSelectModal from 'components/common/CascadingSelectModal/index.vue'
import Config from 'components/home/Config.vue'
import { onMounted, provide, ref } from 'vue'
import { cloneDeep } from 'lodash'
import { onMounted, provide, ref, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
const configRef = ref()
@ -21,13 +22,26 @@ const isModalOpen = ref(false)
const optionsLeft = ref<System.Option[]>([])
const optionsRight = ref<System.Option[]>([])
const selectedValue = ref()
const disinfectionState = ref(homeStore.disinfectionState)
//
const disinfectFormulaVisible = ref(false)
const selectedByFormulas = ref(cloneDeep(formulaStore.selectedFormulaInfo))
const onDisinfectConfig = () => {
formulaStore.initFormulaData()
if (selectedByFormulas.value) {
formulaStore.updateSelectedFormulaDataByList(cloneDeep(selectedByFormulas.value))
}
else {
formulaStore.updateSelectedFormulaDataByList(cloneDeep(formulaStore.defaultFormulaInfo))
}
disinfectFormulaVisible.value = true
}
watchEffect(() => {
disinfectionState.value = homeStore.disinfectionState
selectedByFormulas.value = formulaStore.selectedFormulaInfo
})
onMounted(() => {
//
homeStore.getPressureConfig()
@ -40,7 +54,7 @@ const onShowChart = () => {
//
const onSave = () => {
const formData = configRef.value?.getFormData()
formulaStore.updateSelectedFormulaData(formData)
formulaStore.updateSelectedFormulaDataByList(cloneDeep(formData))
onClose()
}
@ -48,7 +62,6 @@ const onSave = () => {
const onSetPressure = () => {
//
const pressureConfig = homeStore.pressureConfig
console.log('pressureConfig---', pressureConfig)
const { typeDisplayNames, types } = pressureConfig
const intensitys: Record<string, any> = pressureConfig.intensitys
const left: System.Option[] = []
@ -78,9 +91,9 @@ const onSetPressure = () => {
isModalOpen.value = true
}
const handleConfirm = (value: string[]) => {
console.log('value---', value)
const handleConfirm = (value: string | number[]) => {
isModalOpen.value = false
homeStore.updatePressure(value)
}
const handleCancel = () => {
isModalOpen.value = false
@ -112,6 +125,7 @@ const onClose = () => {
border-radius="5px"
text-color="#1989fa"
padding="0.8vw"
:disabled="disinfectionState.state === 'idle'"
@click="onShowChart"
>
<template #icon>
@ -165,7 +179,7 @@ const onClose = () => {
<style lang="scss" scoped>
.home-start-opt{
position: absolute;
bottom: 10px;
bottom: 0;
margin: 1rem;
gap: 5px;
.home-opt-flex{

21
src/components/home/config.vue

@ -3,13 +3,12 @@ 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 { computed, onMounted, ref, watchEffect } from 'vue'
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)
@ -19,14 +18,25 @@ const selectedValue = ref()
const deviceState = ref(homeStore.deviceStete)
onMounted(() => {
onWatchKeyboard()
// store
if (!formulaStore.formulaList || !formulaStore.formulaList.length) {
formulaStore.initFormulaList()
}
})
watchEffect(() => {
recipes.value = [...formulaStore.formulaList]
})
const options = computed(() => {
const list = formulaStore.formulaList
const optionList: System.Option[] = []
list.forEach((item) => {
options.value.push({
optionList.push({
label: item.name.toString(),
value: item.formula_id,
})
})
return optionList
})
const handleConfirm = (value: any) => {
isModalOpen.value = false
@ -36,7 +46,8 @@ const handleConfirm = (value: any) => {
selectedFormula = item
}
})
formulaStore.updateSelectedFormulaData(selectedFormula as Formula.FormulaItem)
//
formulaStore.updateSelectedFormulaDataByList(selectedFormula as Formula.FormulaItem)
}
const handleCancel = () => {
isModalOpen.value = false
@ -77,7 +88,7 @@ const onWatchKeyboard = () => {
<template>
<div class="main-content">
<div v-if="deviceState.state === 'idle'">
<div v-if="deviceState.state.toLocaleLowerCase() === 'idle'">
<bt-button
type="primary"
button-text="选择配方"

25
src/components/liquid/LiquidLevel.vue

@ -1,14 +1,27 @@
<script lang="ts" setup>
import { useLiquidStore } from '@/stores/liquidStore'
import liquidLevelSvg from 'assets/images/liquid/liquid-container.svg'
import liquidScaleSvg from 'assets/images/liquid/liquid-scale.svg'
import { roundNumber } from 'libs/utils'
import { computed, ref, watchEffect } from 'vue'
import { ref } from 'vue'
const liquidStore = useLiquidStore()
const liquidStateData = ref(liquidStore.liquidStateData)
const liquidTotal = ref(liquidStore.liquidTotal)
watchEffect(() => {
liquidTotal.value = liquidStore.liquidTotal
liquidStateData.value = liquidStore.liquidStateData
})
const nowLiquid = computed(() => {
return roundNumber(liquidStateData.value.nowLiquid, 0)
})
const currentLevel = ref(2000)
//
const totalCapacity = 2500
// 200px
const liquidHeight = ref((currentLevel.value / totalCapacity) * 200)
const liquidHeight = computed(() => {
return (Number(nowLiquid.value) / Number(liquidTotal.value)) * 200
})
</script>
<template>
@ -20,7 +33,7 @@ const liquidHeight = ref((currentLevel.value / totalCapacity) * 200)
<div class="liquid-level" :style="{ height: `${liquidHeight}px` }" />
<div class="current-level">
<span>当前液位</span>
<span class="text">{{ currentLevel }}</span>
<span class="text">{{ nowLiquid }}</span>
<span class="text">g</span>
</div>
</div>

11
src/components/liquid/ScaleMark.vue

@ -1,11 +0,0 @@
<script lang="ts" setup>
</script>
<template>
<div class="scale-container">
1
</div>
</template>
<style scoped>
</style>

22
src/components/seal/DashboardChart.vue

@ -1,29 +1,21 @@
<script lang="ts" setup>
import { useSealStore } from '@/stores/sealStore'
import * as echarts from 'echarts'
import { onMounted, ref } from 'vue'
import { onMounted, ref, watchEffect } from 'vue'
const sealStore = useSealStore()
const option = ref()
const pressure = ref(4.5)
const pressure = ref(0)
onMounted(() => {
const chartDom = document.getElementById('main')!
const myChart = echarts.init(chartDom)
init()
setInterval(() => {
myChart.setOption<echarts.EChartsOption>({
series: [
{
data: [
{
value: +(Math.random() * 6).toFixed(1),
},
],
},
],
})
}, 2000)
option.value && myChart.setOption(option.value)
})
watchEffect(() => {
pressure.value = Number(sealStore.sealInfo.pressure)
})
const init = () => {
option.value = {
tooltip: {

105
src/components/setting/History.vue

@ -1,15 +1,54 @@
<script lang="ts" setup>
import { useSettingStore } from '@/stores/settingStore'
// import { useSettingStore } from '@/stores/settingStore'
import { FtMessage } from '@/libs/message'
import { syncSendCmd } from 'apis/system'
import { ElMessageBox } from 'element-plus'
import { ref } from 'vue'
import { onMounted, ref } from 'vue'
import HistoryDetail from './HistoryDetail.vue'
const settingStore = useSettingStore()
const tableData = ref(settingStore.historyList)
console.log('tableData---', tableData)
// const settingStore = useSettingStore()
const tableData = ref<string[]>([])
const selectedRecords = ref<Setting.History[]>([])
const visible = ref(false)
onMounted(() => {
getRecord()
})
const getRecord = () => {
const params = {
className: 'DisinfectionLogsService',
fnName: 'getRecordList',
params: {},
}
syncSendCmd(params).then((res) => {
if (res.ackcode === 0 && res.rely && res.rely.length) {
const list: string[] = res.rely.map((item: Setting.History) => {
return {
name: item,
}
})
tableData.value = list
}
})
}
const showDetail = (historyItem: Setting.History) => {
console.log('--------', historyItem)
const detailParams = {
className: 'DisinfectionLogsService',
fnName: 'getRecord',
params: {
logName: historyItem.name,
},
}
syncSendCmd(detailParams).then(() => {
visible.value = true
})
}
const onDelHistory = () => {
if (selectedRecords.value.length !== 1) {
FtMessage.warning('请选择一条数据进行删除')
return
}
ElMessageBox.confirm(
'请确认是否删除?',
'删除',
@ -19,9 +58,43 @@ const onDelHistory = () => {
type: 'warning',
},
).then(() => {
const delParams = {
className: 'DisinfectionLogsService',
fnName: 'deleteReport',
params: {
logName: selectedRecords.value.map(item => item.name)[0],
},
}
syncSendCmd(delParams).then((res) => {
if (res.ackcode === 0) {
FtMessage.success('删除成功')
getRecord()
}
})
})
}
const onExportHistory = () => {}
const onExportHistory = () => {
if (!selectedRecords.value.length) {
FtMessage.warning('请选择要导出的数据')
return
}
const exportParams = {
className: 'DisinfectionLogsService',
fnName: 'exportRecord',
params: {
logNames: selectedRecords.value.map(item => item.name),
},
}
syncSendCmd(exportParams)
}
const handleSelectionChange = (rows: Setting.History[]) => {
selectedRecords.value = rows
}
const onClose = () => {
visible.value = false
}
</script>
<template>
@ -38,30 +111,38 @@ const onExportHistory = () => {}
@click="onDelHistory"
/>
</div>
<div>
<el-table :data="tableData" stripe style="width: 100%">
<div class="history-table">
<el-table :data="tableData" stripe style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="操作人" />
<el-table-column prop="detail" label="操作">
<template #default="scoped">
<el-link type="primary" @click="showDetail(scoped.row)">查看</el-link>
<el-link type="primary" @click="showDetail(scoped.row)">
查看
</el-link>
</template>
</el-table-column>
</el-table>
</div>
<ft-dialog v-model="visible" title="消毒详情" width="80vw" @cancel="onClose">
<div>
<HistoryDetail />
</div>
</ft-dialog>
</div>
</template>
<style lang="scss" scoped>
.main-content{
height: 100%;
overflow: hidden;
height: $main-container-height;
padding: 10px;
background: $gradient-color;
.history-export{
margin: 2vw;
}
.history-table{
max-height: 73vh;
overflow: auto;
}
}
</style>

67
src/components/setting/HistoryDetail.vue

@ -0,0 +1,67 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
//
const rawData = ref<string[]>([
'时间,设备状态,HO2O2-0,T-0,RH-0,RS-0,D值,当前LOG,目标LOG,注液速率,消毒剂剩余量(g),预计剩余时间,',
'2023-03-28 12:57:54,预热,N/A,N/A,N/A,N/A,0,\"0.00\",1,10,1000,N/A,',
'2023-03-28 12:57:58,预热,N/A,N/A,N/A,N/A,0,\"0.00\",1,10,1000,00:01:58,',
'2023-03-28 12:57:59,消毒结束,N/A,N/A,N/A,N/A,0,\"0.00\",1,10,1000,00:00:00,',
])
//
const tableHeaders = computed(() => {
if (rawData.value.length === 0) {
return []
}
//
return rawData.value[0].split(',').filter(header => header.trim() !== '')
})
//
const tableData = computed(() => {
if (rawData.value.length <= 1) {
return []
}
return rawData.value.slice(1).map((row) => {
const values = row.split(',')
const item: Record<string, string> = {}
tableHeaders.value.forEach((header, index) => {
//
let value = values[index] || ''
value = value.replace(/^"|"$/g, '') //
item[header] = value.trim()
})
return item
})
})
//
const tableColumns = computed(() => {
return tableHeaders.value.map(header => ({
prop: header,
label: header,
minWidth: 120,
}))
})
</script>
<template>
<div class="data-table-container">
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
/>
</el-table>
</div>
</template>
<style scoped>
.data-table-container {
padding: 20px;
}
</style>

95
src/layouts/default.vue

@ -1,24 +1,68 @@
<script setup lang="ts">
import { FtMessageBox } from '@/libs/messageBox'
import { useHomeStore } from '@/stores/homeStore'
import { useLiquidStore } from '@/stores/liquidStore'
import { useSealStore } from '@/stores/sealStore'
import { useSystemStore } from '@/stores/systemStore'
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'
import { useDeviceStore } from 'stores/deviceStore'
import { onUnmounted, ref, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const liquidStore = useLiquidStore()
const sealStore = useSealStore()
const homeStore = useHomeStore()
const deviceStore = useDeviceStore()
const systemStore = useSystemStore()
const languages = systemStore.$state.languages
const languages = systemStore.languages
const currentTime = ref(formatDateTime())
const languageType = ref('zh-cn')
const deviceInfo = ref<Device.DeviceInfo>(deviceStore.deviceInfo)
const timeInterval = setInterval(() => {
currentTime.value = formatDateTime()
}, 1000)
const wordStateName = ref<string>('空闲')
const disinfectState = ref(homeStore.disinfectionState.state)
const liquidAddState = ref(liquidStore.liquidAddWorkState)
const liquidDrainState = ref(liquidStore.liquidDrainWorkState)
const sealInfo = ref(sealStore.sealInfo)
onMounted(() => {
console.log(111)
const showWorkState = () => {
//
disinfectState.value = homeStore.disinfectionState.state
// /
liquidAddState.value = liquidStore.liquidStateData
liquidDrainState.value = liquidStore.liquidDrainWorkState
//
sealInfo.value = sealStore.sealInfo
if (disinfectState.value !== 'idle' && disinfectState.value !== 'finished') {
wordStateName.value = homeStore.disinfectionState.statedisplayName
}
else if (liquidAddState.value.workState !== 'idle') {
wordStateName.value = liquidAddState.value.workStateDisplay
}
else if (liquidDrainState.value.workState !== 'idle') {
wordStateName.value = liquidDrainState.value.workStateDisplay
}
else if (sealInfo.value.workState !== 'idle') {
wordStateName.value = sealInfo.value.workStateDisplay
}
else {
wordStateName.value = '空闲'
}
}
watchEffect(() => {
deviceInfo.value = deviceStore.deviceInfo
showWorkState()
})
onUnmounted(() => {
clearInterval(timeInterval)
})
@ -26,7 +70,40 @@ const showErrorModal = () => {
ErrorBox.alert('这是一条警告信息')
}
const onLogout = () => {
router.push('/login')
//
const hasEx = getDeviceStatus()
if (hasEx) { //
FtMessageBox.error('退出登录前请停止当前的操作')
}
else { //
FtMessageBox.warning('请确认是否退出登录?').then(() => {
router.push('/login')
})
}
}
const getDeviceStatus = () => {
console.log('disinfectState.value.state---', disinfectState.value)
if (disinfectState.value !== 'idle' && disinfectState.value !== 'finished') {
console.log('---正在消毒---')
return true
}
//
if (liquidAddState.value.workState !== 'idle') {
console.log('---正在加液---')
return true
}
//
if (liquidDrainState.value.workState !== 'idle') {
console.log('---正在排液---')
return true
}
//
if (sealInfo.value.workState !== 'idle') {
console.log('---正在密封性测试---')
return true
}
return false
}
</script>
@ -80,10 +157,12 @@ const onLogout = () => {
</el-main>
</el-container>
<footer class="footer">
<div class="ip-info">IP : 192.0.0.0</div>
<div class="ip-info">
IP : {{ deviceInfo.ip }}
</div>
<div class="alarm-info" @click="showErrorModal">
<img :src="HomeAlarmSvg" width="20" alt="报警">
<div>机器报警信息</div>
<div>{{ wordStateName }}</div>
</div>
<div class="time">
{{ currentTime }}
@ -241,7 +320,7 @@ const onLogout = () => {
}
.content {
width: 100%;
height: $main-container;
height: $main-container-height;
padding: 10px;
}
.footer-expand {

13
src/libs/countdownTimer.ts

@ -53,5 +53,14 @@ class CountdownTimer {
}
}
// 倒计时
export default CountdownTimer
let countdownTimer: CountdownTimer | null = null
export const startTimer = (timestamp: number, callback: (formatTimeStr: string) => void) => {
countdownTimer = new CountdownTimer(timestamp, callback)
countdownTimer.init()
}
export const stopTimer = () => {
if (countdownTimer) {
countdownTimer.stopTimer()
}
}

27
src/libs/messageBox.ts

@ -0,0 +1,27 @@
import { ElMessageBox } from 'element-plus'
export const FtMessageBox = {
error: (message: string) => {
ElMessageBox.confirm(
message,
'错误提示',
{
showCancelButton: false,
confirmButtonText: '确认',
type: 'error',
},
)
},
warning: (message: string) => {
return ElMessageBox.confirm(
message,
'提示',
{
cancelButtonText: '取消',
confirmButtonText: '确认',
type: 'warning',
},
)
},
}

398
src/libs/pinyinDict.json

@ -0,0 +1,398 @@
{
"a": ["啊", "阿", "呵", "腌"],
"ai": ["爱", "唉", "哀", "哎", "癌", "矮", "蔼", "碍", "艾", "隘"],
"an": ["安", "按", "暗", "案", "氨", "岸", "俺", "鞍", "胺", "庵"],
"ang": ["昂", "肮"],
"ao": ["奥", "熬", "傲", "凹", "袄", "澳", "翱", "拗", "敖", "獒"],
"ba": ["把", "八", "爸", "巴", "扒", "疤", "笆", "拔", "跋", "靶"],
"bai": ["白", "百", "拜", "柏", "摆", "败", "稗", "掰", "佰", "呗"],
"ban": ["班", "办", "半", "般", "搬", "板", "伴", "瓣", "斑", "颁"],
"bang": ["帮", "棒", "绑", "磅", "膀", "蚌", "梆", "镑", "榜", "谤"],
"bao": ["宝", "抱", "饱", "保", "爆", "包", "薄", "雹", "堡", "鲍"],
"bei": ["被", "北", "背", "悲", "杯", "贝", "备", "碑", "卑", "辈"],
"ben": ["本", "奔", "笨", "苯", "畚", "坌", "贲"],
"beng": ["蹦", "崩", "甭", "绷", "泵", "迸", "蚌"],
"bi": ["比", "笔", "必", "逼", "鼻", "币", "闭", "碧", "蔽", "壁"],
"bian": ["边", "变", "遍", "编", "便", "扁", "辨", "辩", "辫", "贬"],
"biao": ["表", "标", "彪", "膘", "镖", "飙", "飚", "镳", "婊", "裱"],
"bie": ["别", "憋", "瘪", "蹩", "鳖", "别"],
"bin": ["宾", "滨", "彬", "斌", "濒", "缤", "殡", "膑", "摈", "鬓"],
"bing": ["并", "病", "冰", "兵", "丙", "柄", "秉", "饼", "屏", "禀"],
"bo": ["波", "博", "播", "拨", "薄", "脖", "驳", "帛", "泊", "勃"],
"bu": ["不", "布", "步", "补", "捕", "哺", "堡", "埔", "怖", "埠"],
"ca": ["擦", "嚓", "礤"],
"cai": ["才", "菜", "采", "财", "睬", "踩", "裁", "蔡", "彩", "材"],
"can": ["参", "餐", "残", "蚕", "惭", "惨", "灿", "璨", "孱", "骖"],
"cang": ["仓", "藏", "苍", "舱", "沧", "伧", "鸧", "舱"],
"cao": ["草", "操", "曹", "槽", "糙", "嘈", "漕", "艚", "螬", "艹"],
"ce": ["测", "侧", "册", "策", "厕", "恻", "昳", "敇"],
"ceng": ["层", "蹭", "噌", "嶒"],
"cha": ["茶", "查", "叉", "插", "差", "岔", "察", "刹", "茬", "杈"],
"chai": ["柴", "拆", "差", "豺", "侪", "虿", "袃"],
"chan": ["产", "长", "厂", "场", "尝", "常", "畅", "唱", "倡", "超"],
"chang": ["长", "常", "厂", "场", "唱", "畅", "倡", "超", "偿", "肠"],
"chao": ["超", "抄", "吵", "炒", "潮", "巢", "朝", "嘲", "晁", "剿"],
"che": ["车", "扯", "撤", "掣", "彻", "澈", "砗", "硩"],
"chen": ["陈", "晨", "尘", "臣", "沉", "衬", "趁", "辰", "忱", "谌"],
"cheng": ["成", "城", "称", "程", "乘", "承", "逞", "秤", "惩", "澄"],
"chi": ["吃", "池", "迟", "尺", "齿", "赤", "翅", "斥", "耻", "炽"],
"chong": ["冲", "重", "虫", "宠", "崇", "忡", "茺", "涌"],
"chou": ["抽", "愁", "丑", "臭", "仇", "绸", "畴", "踌", "酬", "瞅"],
"chu": ["出", "初", "除", "厨", "锄", "楚", "础", "储", "矗", "触"],
"chuai": ["揣", "搋", "踹", "啜"],
"chuan": ["穿", "传", "船", "喘", "串", "川", "椽", "氚", "舛", "遄"],
"chuang": ["创", "窗", "床", "闯", "疮", "怆", "幢", "磢"],
"chui": ["吹", "垂", "锤", "炊", "捶", "陲", "棰", "槌", "倕", "菙"],
"chun": ["春", "纯", "蠢", "唇", "淳", "醇", "莼", "鹑", "肫", "踳"],
"chuo": ["戳", "绰", "啜", "踔", "辍", "龊", "淖", "婥"],
"ci": ["次", "词", "此", "刺", "慈", "磁", "雌", "赐", "瓷", "祠"],
"cong": ["从", "聪", "葱", "匆", "丛", "琮", "苁", "枞", "璁", "骢"],
"cu": ["粗", "促", "醋", "卒", "猝", "簇", "蹙", "蹴", "殂", "酢"],
"cuan": ["窜", "蹿", "篡", "攒", "爨", "镩", "撺", "攅"],
"cui": ["催", "脆", "翠", "摧", "崔", "瘁", "淬", "璀", "啐", "悴"],
"cun": ["存", "寸", "村", "忖", "皴", "踆", "吋"],
"cuo": ["错", "搓", "撮", "措", "挫", "磋", "脞", "厝", "锉", "痤"],
"da": ["大", "打", "达", "答", "搭", "瘩", "耷", "嗒", "妲", "怛"],
"dai": ["带", "代", "待", "戴", "袋", "逮", "怠", "傣", "贷", "殆"],
"dan": ["但", "单", "蛋", "丹", "胆", "担", "耽", "郸", "掸", "惮"],
"dang": ["当", "党", "挡", "荡", "档", "铛", "宕", "菪", "凼", "帑"],
"dao": ["到", "道", "刀", "导", "岛", "倒", "蹈", "稻", "悼", "盗"],
"de": ["的", "得", "地", "德", "的", "得", "地", "德", "锝", "嘚"],
"deng": ["等", "灯", "登", "邓", "瞪", "凳", "噔", "嶝", "簦", "蹬"],
"di": ["地", "的", "第", "低", "滴", "敌", "笛", "底", "抵", "帝"],
"dian": ["点", "电", "店", "垫", "殿", "典", "碘", "淀", "佃", "巅"],
"diao": ["调", "掉", "吊", "钓", "雕", "刁", "叼", "碉", "貂", "铞"],
"die": ["爹", "跌", "叠", "蝶", "谍", "碟", "堞", "耋", "牒", "鲽"],
"ding": ["定", "顶", "丁", "钉", "叮", "盯", "鼎", "锭", "町", "玎"],
"diu": ["丢", "铥"],
"dong": ["东", "冬", "动", "懂", "冻", "洞", "董", "侗", "咚", "垌"],
"dou": ["都", "斗", "豆", "抖", "陡", "兜", "痘", "逗", "蚪", "窦"],
"du": ["度", "读", "独", "毒", "堵", "睹", "赌", "杜", "镀", "肚"],
"duan": ["段", "短", "端", "断", "缎", "椴", "煅", "簖", "碫", "蝃"],
"dui": ["对", "堆", "队", "兑", "怼", "碓", "镦", "憝", "濧", "譈"],
"dun": ["吨", "顿", "蹲", "敦", "墩", "盾", "沌", "囤", "盹", "钝"],
"duo": ["多", "朵", "夺", "躲", "剁", "惰", "跺", "舵", "驮", "咄"],
"e": ["饿", "鹅", "蛾", "恶", "俄", "额", "厄", "扼", "鄂", "讹"],
"en": ["恩", "嗯", "蒽", "摁"],
"er": ["而", "二", "儿", "耳", "尔", "饵", "洱", "铒", "鸸", "鲕"],
"fa": ["发", "法", "罚", "伐", "乏", "阀", "珐", "砝", "垡", "茷"],
"fan": ["反", "饭", "翻", "犯", "凡", "烦", "帆", "番", "繁", "泛"],
"fang": ["方", "放", "房", "防", "访", "纺", "芳", "妨", "肪", "钫"],
"fei": ["飞", "非", "肥", "费", "废", "沸", "肺", "匪", "吠", "啡"],
"fen": ["分", "份", "粉", "坟", "奋", "粪", "芬", "焚", "吩", "氛"],
"feng": ["风", "封", "疯", "峰", "丰", "奉", "缝", "讽", "冯", "枫"],
"fo": ["佛", "坲", "梻", "仏"],
"fou": ["否", "缶", "芣", "不"],
"fu": ["服", "副", "付", "富", "福", "扶", "夫", "父", "附", "浮"],
"ga": ["嘎", "伽", "夹", "旮", "尬", "尕", "魀", "呷"],
"gai": ["改", "该", "盖", "钙", "概", "丐", "垓", "赅", "陔", "戤"],
"gan": ["干", "甘", "赶", "敢", "肝", "杆", "竿", "感", "杆", "赣"],
"gang": ["刚", "钢", "缸", "岗", "港", "杠", "扛", "戆", "筻", "肛"],
"gao": ["高", "搞", "告", "糕", "稿", "篙", "皋", "膏", "羔", "镐"],
"ge": ["个", "各", "歌", "哥", "割", "革", "格", "葛", "阁", "隔"],
"gei": ["给", "虼", "哿", "挌"],
"gen": ["根", "跟", "亘", "艮", "茛", "揯"],
"geng": ["更", "耕", "耿", "梗", "哽", "埂", "赓", "羹", "绠", "鲠"],
"gong": ["工", "公", "功", "共", "攻", "供", "宫", "恭", "拱", "巩"],
"gou": ["够", "狗", "沟", "构", "购", "钩", "垢", "苟", "垢", "佝"],
"gu": ["古", "顾", "故", "谷", "骨", "鼓", "估", "孤", "姑", "固"],
"gua": ["瓜", "刮", "挂", "褂", "乖", "寡", "卦", "诖", "胍", "鸹"],
"guai": ["怪", "乖", "拐", "掴", "诓", "佪"],
"guan": ["关", "官", "管", "观", "贯", "灌", "罐", "惯", "棺", "倌"],
"guang": ["光", "广", "逛", "咣", "犷", "桄", "胱", "黆"],
"gui": ["贵", "归", "鬼", "跪", "龟", "轨", "桂", "柜", "诡", "癸"],
"gun": ["滚", "棍", "滚", "磙", "衮", "绲", "鲧", "焜"],
"guo": ["过", "国", "果", "锅", "裹", "郭", "蝈", "聒", "椁", "馘"],
"ha": ["哈", "蛤", "铪", "呵", "嗨", "嗄", "哈"],
"hai": ["还", "海", "害", "孩", "亥", "氦", "骸", "嗨", "胲", "醢"],
"han": ["汉", "含", "喊", "汗", "寒", "韩", "旱", "函", "罕", "翰"],
"hang": ["行", "航", "杭", "巷", "夯", "绗", "吭", "沆", "颃", "珩"],
"hao": ["好", "号", "浩", "豪", "耗", "毫", "郝", "嚎", "壕", "昊"],
"he": ["和", "喝", "河", "合", "何", "贺", "盒", "荷", "赫", "鹤"],
"hei": ["黑", "嘿", "嗨", "黒"],
"hen": ["很", "狠", "恨", "痕", "亨", "佷", "哏", "悙"],
"heng": ["横", "恒", "衡", "亨", "横", "珩", "蘅", "鸻"],
"hong": ["红", "洪", "宏", "哄", "虹", "鸿", "烘", "弘", "讧", "蕻"],
"hou": ["后", "厚", "候", "猴", "吼", "喉", "侯", "堠", "逅", "鲎"],
"hu": ["户", "胡", "呼", "湖", "虎", "互", "弧", "狐", "糊", "壶"],
"hua": ["花", "化", "画", "华", "滑", "话", "划", "哗", "桦", "骅"],
"huai": ["坏", "怀", "槐", "淮", "踝", "褢", "徊", "踝"],
"huan": ["换", "还", "环", "欢", "缓", "唤", "患", "幻", "宦", "豢"],
"huang": ["黄", "晃", "荒", "谎", "皇", "凰", "惶", "煌", "蝗", "簧"],
"hui": ["会", "回", "辉", "汇", "悔", "毁", "慧", "晦", "贿", "秽"],
"hun": ["混", "昏", "婚", "魂", "浑", "荤", "馄", "阍", "诨", "溷"],
"huo": ["火", "活", "或", "伙", "货", "获", "祸", "霍", "惑", "豁"],
"ji": ["机", "及", "集", "几", "既", "级", "极", "急", "际", "基"],
"jia": ["家", "加", "价", "假", "夹", "佳", "甲", "驾", "架", "嫁"],
"jian": ["见", "建", "间", "件", "减", "尖", "检", "剑", "兼", "煎"],
"jiang": ["将", "江", "讲", "奖", "降", "疆", "蒋", "匠", "酱", "桨"],
"jiao": ["叫", "交", "教", "角", "较", "焦", "胶", "郊", "娇", "浇"],
"jie": ["节", "结", "解", "接", "街", "借", "界", "姐", "杰", "劫"],
"jin": ["进", "金", "近", "斤", "今", "尽", "紧", "禁", "仅", "劲"],
"jing": ["经", "静", "京", "惊", "精", "景", "净", "镜", "井", "警"],
"jiong": ["炯", "窘", "扃", "蘓", "熲", "颎"],
"jiu": ["就", "九", "酒", "旧", "救", "纠", "究", "舅", "久", "揪"],
"ju": ["局", "举", "句", "具", "巨", "聚", "据", "距", "居", "菊"],
"juan": ["卷", "捐", "鹃", "倦", "绢", "涓", "镌", "锩", "捲", "蠲"],
"jue": ["觉", "决", "绝", "掘", "角", "撅", "爵", "嚼", "脚", "倔"],
"jun": ["军", "君", "均", "俊", "菌", "郡", "骏", "竣", "浚", "捃"],
"ka": ["卡", "咖", "喀", "咔", "佧", "胩", "鉲"],
"kai": ["开", "凯", "慨", "楷", "铠", "蒈", "忾", "锴", "垲", "恺"],
"kan": ["看", "坎", "砍", "刊", "堪", "勘", "龛", "瞰", "侃", "莰"],
"kang": ["康", "抗", "炕", "慷", "扛", "亢", "闶", "钪", "忼", "砊"],
"kao": ["考", "靠", "烤", "拷", "犒", "铐", "栲", "尻", "洘", "鯌"],
"ke": ["可", "课", "颗", "棵", "科", "壳", "咳", "客", "刻", "克"],
"ken": ["肯", "垦", "恳", "啃", "裉", "肻", "墾"],
"keng": ["坑", "吭", "硁", "铿", "牼", "硻"],
"kong": ["空", "孔", "控", "恐", "崆", "箜", "倥", "埪", "硿", "錓"],
"kou": ["口", "扣", "寇", "叩", "蔻", "芤", "筘", "冦", "㕮", "眍"],
"ku": ["苦", "哭", "库", "裤", "酷", "窟", "骷", "绔", "刳", "喾"],
"kua": ["夸", "跨", "垮", "挎", "胯", "侉", "骻", "咵", "姱", "恗"],
"kuai": ["快", "块", "筷", "会", "侩", "脍", "狯", "浍", "郐", "蒯"],
"kuan": ["宽", "款", "髋", "寛", "欵", "窽"],
"kuang": ["狂", "矿", "框", "筐", "况", "诓", "邝", "圹", "夼", "哐"],
"kui": ["亏", "愧", "奎", "亏", "葵", "窥", "溃", "魁", "馈", "盔"],
"kun": ["困", "昆", "捆", "坤", "琨", "鲲", "醌", "阃", "堃", "锟"],
"kuo": ["扩", "阔", "括", "廓", "蛞", "萿", "适", "霩", "鞟", "鬠"],
"la": ["拉", "辣", "啦", "腊", "蜡", "落", "垃", "喇", "蓝", "旯"],
"lai": ["来", "赖", "莱", "奶", "睐", "濑", "赉", "涞", "铼", "癞"],
"lan": ["兰", "蓝", "拦", "篮", "懒", "烂", "滥", "婪", "澜", "谰"],
"lang": ["浪", "狼", "郎", "朗", "廊", "琅", "榔", "莨", "阆", "锒"],
"lao": ["老", "劳", "捞", "牢", "落", "烙", "姥", "酪", "涝", "铹"],
"le": ["了", "乐", "勒", "雷", "嘞", "楽", "仂", "叻"],
"lei": ["雷", "累", "类", "泪", "垒", "擂", "蕾", "磊", "儡", "镭"],
"leng": ["冷", "愣", "棱", "塄", "堎", "啷", "睖"],
"li": ["里", "力", "立", "李", "例", "理", "离", "梨", "礼", "莉"],
"lia": ["俩", "倆", "喍"],
"lian": ["连", "脸", "练", "联", "莲", "链", "廉", "怜", "帘", "恋"],
"liang": ["亮", "两", "量", "凉", "梁", "粮", "良", "辆", "晾", "粱"],
"liao": ["了", "料", "聊", "疗", "辽", "燎", "寥", "撩", "嘹", "镣"],
"lie": ["列", "裂", "猎", "劣", "烈", "咧", "趔", "捩", "鬣", "躐"],
"lin": ["林", "临", "淋", "邻", "鳞", "磷", "凛", "赁", "吝", "蔺"],
"ling": ["零", "领", "铃", "另", "令", "岭", "灵", "凌", "陵", "羚"],
"liu": ["刘", "流", "留", "六", "柳", "溜", "硫", "瘤", "榴", "镏"],
"long": ["龙", "弄", "笼", "聋", "隆", "垄", "窿", "陇", "茏", "泷"],
"lou": ["楼", "漏", "娄", "陋", "露", "喽", "偻", "蒌", "瘘", "耧"],
"lu": ["路", "录", "陆", "卢", "炉", "鲁", "鹿", "露", "碌", "庐"],
"lv": ["绿", "旅", "吕", "铝", "率", "滤", "氯", "缕", "履", "律"],
"luan": ["乱", "卵", "滦", "峦", "挛", "孪", "娈", "脔", "銮", "圝"],
"lue": ["略", "掠", "锊", "畧", "圙"],
"lun": ["论", "轮", "伦", "抡", "仑", "沦", "纶", "囵", "焞", "碖"],
"luo": ["罗", "落", "洛", "锣", "箩", "骡", "裸", "络", "骆", "烙"],
"ma": ["妈", "马", "吗", "骂", "麻", "码", "玛", "抹", "蚂", "嘛"],
"mai": ["买", "卖", "麦", "埋", "迈", "脉", "霾", "劢", "荬", "唛"],
"man": ["满", "慢", "漫", "蛮", "蔓", "馒", "瞒", "螨", "鳗", "镘"],
"mang": ["忙", "盲", "茫", "芒", "莽", "氓", "硭", "漭", "邙", "铓"],
"mao": ["毛", "矛", "茅", "锚", "猫", "毛", "帽", "貌", "贸", "冒"],
"me": ["么", "呒", "庅", "么"],
"mei": ["没", "眉", "美", "妹", "煤", "每", "枚", "霉", "玫", "酶"],
"men": ["门", "们", "闷", "扪", "钔", "焖", "懑", "们"],
"meng": ["梦", "猛", "蒙", "孟", "盟", "锰", "萌", "檬", "懵", "勐"],
"mi": ["米", "蜜", "迷", "眯", "秘", "谜", "弥", "觅", "泌", "靡"],
"mian": ["面", "棉", "免", "勉", "冕", "眠", "绵", "缅", "腼", "湎"],
"miao": ["苗", "秒", "妙", "庙", "描", "瞄", "藐", "淼", "缈", "鹋"],
"mie": ["灭", "蔑", "咩", "篾", "蠛", "鱴", "櫗"],
"min": ["民", "敏", "闽", "皿", "抿", "岷", "闵", "愍", "泯", "敃"],
"ming": ["明", "名", "命", "鸣", "铭", "冥", "螟", "瞑", "溟", "暝"],
"miu": ["谬", "缪", "纰", "缪"],
"mo": ["摸", "磨", "膜", "魔", "抹", "末", "莫", "墨", "默", "陌"],
"mou": ["某", "谋", "牟", "眸", "侔", "蛑", "鍪", "麰"],
"mu": ["木", "目", "母", "亩", "墓", "幕", "牧", "穆", "慕", "拇"],
"na": ["那", "拿", "哪", "钠", "娜", "捺", "衲", "肭", "笝", "镎"],
"nai": ["奶", "乃", "耐", "奈", "氖", "鼐", "柰", "迺", "艿", "倷"],
"nan": ["南", "难", "男", "楠", "喃", "腩", "赧", "蝻", "囡", "侽"],
"nang": ["囊", "馕", "攮", "齉", "曩"],
"nao": ["脑", "闹", "恼", "挠", "瑙", "淖", "垴", "硇", "铙", "蛲"],
"ne": ["呢", "那", "讷", "哪", "ně", "吶", "抐"],
"nei": ["内", "馁", "腇", "鮾"],
"nen": ["嫩", "恁", "嫰", "哏", "碜"],
"neng": ["能", "néng", "恁", "竜", "贑"],
"ni": ["你", "泥", "尼", "拟", "腻", "逆", "溺", "倪", "匿", "昵"],
"nian": ["年", "念", "粘", "捻", "碾", "撵", "蔫", "鲶", "辇", "廿"],
"niang": ["娘", "酿", "嬢", "酿", "釀"],
"niao": ["鸟", "尿", "袅", "茑", "嫋", "嬲", "脲"],
"nie": ["捏", "聂", "孽", "镍", "涅", "啮", "镊", "陧", "蘖", "蹑"],
"nin": ["您", "拰", "撚", "拰"],
"ning": ["宁", "凝", "拧", "狞", "柠", "泞", "佞", "咛", "聍", "甯"],
"niu": ["牛", "扭", "纽", "钮", "忸", "拗", "狃", "靵", "杻", "炄"],
"nong": ["农", "浓", "弄", "侬", "哝", "禯", "譨", "醲"],
"nu": ["努", "怒", "女", "弩", "胬", "孥", "帑", "伮", "拏", "呶"],
"nv": ["女", "钕", "恧", "衄", "籹", "釹"],
"nuan": ["暖", "餪", "煖", "㬉", "奻"],
"nue": ["虐", "疟", "谑", "硸", "疟"],
"nuo": ["挪", "诺", "懦", "糯", "喏", "锘", "傩", "搦", "糥", "稬"],
"o": ["哦", "喔", "噢", "嚄", "讴", "呕", "偶", "沤"],
"ou": ["欧", "偶", "藕", "呕", "殴", "鸥", "沤", "怄", "瓯", "讴"],
"pa": ["怕", "爬", "帕", "趴", "扒", "啪", "耙", "琶", "葩", "杷"],
"pai": ["拍", "排", "派", "牌", "湃", "俳", "徘", "蒎", "哌", "簰"],
"pan": ["盘", "盼", "判", "攀", "畔", "判", "潘", "磐", "蟠", "蹒"],
"pang": ["胖", "旁", "庞", "磅", "耪", "螃", "彷", "滂", "逄", "徬"],
"pao": ["跑", "泡", "炮", "抛", "袍", "刨", "咆", "庖", "匏", "炮"],
"pei": ["陪", "配", "赔", "佩", "培", "呸", "胚", "沛", "裴", "帔"],
"pen": ["盆", "喷", "奔", "湓", "歕", "喯", "噴"],
"peng": ["朋", "碰", "蓬", "棚", "膨", "鹏", "捧", "烹", "澎", "篷"],
"pi": ["批", "皮", "匹", "屁", "劈", "啤", "脾", "疲", "僻", "辟"],
"pian": ["片", "偏", "骗", "篇", "便", "翩", "胼", "骈", "谝", "犏"],
"piao": ["飘", "票", "漂", "瓢", "朴", "缥", "殍", "瞟", "骠", "螵"],
"pie": ["撇", "瞥", "苤", "丿", "暼", "鐅"],
"pin": ["品", "拼", "贫", "频", "聘", "嫔", "颦", "榀", "牝", "姘"],
"ping": ["平", "评", "凭", "苹", "瓶", "坪", "屏", "萍", "乒", "坪"],
"po": ["破", "坡", "泼", "婆", "迫", "泊", "颇", "魄", "粕", "鄱"],
"pou": ["剖", "抔", "掊", "裒", "箁", "捊", "踣"],
"pu": ["铺", "扑", "普", "仆", "谱", "瀑", "葡", "埔", "蒲", "璞"],
"qi": ["七", "其", "起", "气", "期", "齐", "奇", "器", "汽", "泣"],
"qia": ["恰", "卡", "掐", "洽", "髂", "袷", "愘", "欫", "葜", "拤"],
"qian": ["前", "钱", "千", "浅", "牵", "欠", "签", "谦", "遣", "铅"],
"qiang": ["强", "抢", "墙", "枪", "腔", "呛", "羌", "蔷", "襁", "炝"],
"qiao": ["桥", "瞧", "巧", "敲", "悄", "壳", "翘", "窍", "峭", "橇"],
"qie": ["切", "且", "窃", "怯", "茄", "妾", "锲", "惬", "箧", "挈"],
"qin": ["亲", "琴", "勤", "侵", "秦", "擒", "寝", "沁", "芩", "嗪"],
"qing": ["清", "轻", "情", "请", "晴", "青", "庆", "倾", "氢", "擎"],
"qiong": ["穷", "琼", "穹", "茕", "邛", "筇", "惸", "焪", "琁", "嬛"],
"qiu": ["求", "秋", "球", "丘", "邱", "囚", "酋", "仇", "揪", "泅"],
"qu": ["去", "区", "取", "曲", "趣", "屈", "驱", "渠", "躯", "龋"],
"quan": ["全", "权", "劝", "圈", "泉", "拳", "犬", "痊", "诠", "醛"],
"que": ["却", "确", "缺", "雀", "瘸", "榷", "阕", "悫", "雀", "鹊"],
"qun": ["群", "裙", "麇", "羣", "群", "裠"],
"ran": ["然", "燃", "染", "冉", "髯", "蚺", "袇", "媣", "蒅", "珃"],
"rang": ["让", "嚷", "壤", "瓤", "攘", "禳", "穰", "獽", "瀼", "鬤"],
"rao": ["绕", "饶", "扰", "娆", "荛", "桡", "隢", "饒", "擾", "撓"],
"re": ["热", "惹", "热", "喏", "捼", "玼", "絷"],
"ren": ["人", "认", "任", "仁", "忍", "刃", "韧", "妊", "纫", "荏"],
"reng": ["仍", "扔", "礽", "芿", "陾", "轫"],
"ri": ["日", "驲", "囸", "鈤", "馹"],
"rong": ["容", "荣", "融", "熔", "溶", "蓉", "冗", "茸", "戎", "嵘"],
"rou": ["肉", "揉", "柔", "鞣", "蹂", "煣", "瓇", "腬", "媃", "糅"],
"ru": ["入", "如", "汝", "乳", "儒", "辱", "褥", "蠕", "茹", "孺"],
"ruan": ["软", "阮", "朊", "偄", "軟", "媆"],
"rui": ["瑞", "锐", "蕊", "睿", "芮", "蚋", "枘", "蕤", "汭", "甤"],
"run": ["润", "闰", "橍", "润", "閏", "閠"],
"ruo": ["若", "弱", "箬", "偌", "鄀", "蒻", "鰙", "嵶"],
"sa": ["撒", "洒", "萨", "挲", "卅", "飒", "仨", "脎", "挲", "卅"],
"sai": ["赛", "塞", "腮", "鳃", "噻", "毵", "簑", "簔"],
"san": ["三", "散", "伞", "叁", "糁", "馓", "毵", "鬖", "傘", "散"],
"sang": ["桑", "丧", "嗓", "丧", "搡", "磉", "颡", "鎟", "桑", "喪"],
"sao": ["扫", "嫂", "骚", "扫", "搔", "瘙", "缫", "臊", "鳋", "埽"],
"se": ["色", "涩", "瑟", "塞", "啬", "铯", "穑", "歮", "澁", "濇"],
"sen": ["森", "槮", "篸", "襂", "糁", "燊"],
"seng": ["僧", "鬙", "曽", "噌", "竲", "缯"],
"sha": ["杀", "沙", "啥", "傻", "纱", "刹", "厦", "砂", "莎", "铩"],
"shai": ["晒", "筛", "晒", "色", "酾", "簁", "簛", "曬"],
"shan": ["山", "闪", "衫", "善", "扇", "删", "珊", "陕", "膳", "赡"],
"shang": ["上", "伤", "商", "赏", "晌", "尚", "裳", "墒", "殇", "绱"],
"shao": ["少", "烧", "勺", "稍", "捎", "哨", "绍", "邵", "韶", "苕"],
"she": ["设", "社", "舍", "射", "蛇", "舌", "折", "涉", "赊", "慑"],
"shen": ["深", "身", "神", "审", "沈", "甚", "渗", "申", "肾", "慎"],
"sheng": ["生", "声", "胜", "升", "省", "圣", "盛", "剩", "绳", "笙"],
"shi": ["十", "是", "时", "使", "诗", "失", "狮", "史", "识", "驶"],
"shou": ["手", "首", "收", "守", "受", "寿", "授", "售", "兽", "狩"],
"shu": ["书", "树", "数", "输", "叔", "属", "术", "述", "暑", "鼠"],
"shua": ["刷", "耍", "唰", "誜", "腨", "耍"],
"shuai": ["帅", "摔", "衰", "率", "甩", "蟀", "卛", "卝", "帥", "綏"],
"shuan": ["拴", "栓", "闩", "涮", "腨", "痠", "酸"],
"shuang": ["双", "霜", "爽", "孀", "骦", "鹴", "泷", "欆"],
"shui": ["水", "睡", "税", "谁", "涗", "说", "捝", "説"],
"shun": ["顺", "舜", "瞬", "吮", "蕣", "橓", "瞚", "瞤"],
"shuo": ["说", "硕", "烁", "朔", "妁", "搠", "蒴", "槊", "箾", "鑠"],
"si": ["四", "死", "丝", "寺", "私", "饲", "撕", "思", "斯", "厮"],
"song": ["送", "松", "宋", "颂", "怂", "耸", "讼", "嵩", "淞", "菘"],
"sou": ["搜", "艘", "嗽", "擞", "嗖", "馊", "叟", "薮", "瞍", "锼"],
"su": ["素", "速", "苏", "诉", "俗", "塑", "肃", "宿", "粟", "酥"],
"suan": ["算", "酸", "蒜", "狻", "痠", "筭", "祘", "蒜"],
"sui": ["随", "岁", "碎", "虽", "穗", "遂", "隋", "髓", "隧", "祟"],
"sun": ["孙", "损", "笋", "榫", "荪", "狲", "飧", "飱", "槂", "潠"],
"suo": ["所", "缩", "锁", "索", "唆", "梭", "蓑", "唢", "羧", "睃"],
"ta": ["他", "她", "它", "踏", "塔", "塌", "獭", "挞", "蹋", "闼"],
"tai": ["太", "台", "抬", "胎", "泰", "态", "苔", "酞", "钛", "跆"],
"tan": ["谈", "弹", "坦", "探", "贪", "碳", "瘫", "坛", "痰", "潭"],
"tang": ["唐", "糖", "汤", "躺", "烫", "堂", "塘", "膛", "棠", "搪"],
"tao": ["桃", "逃", "掏", "涛", "滔", "套", "讨", "陶", "绦", "焘"],
"te": ["特", "忑", "铽", "膯", "慝", "鋱"],
"teng": ["疼", "腾", "藤", "誊", "滕", "漛", "膯", "駦", "螣", "縢"],
"ti": ["提", "题", "体", "替", "踢", "梯", "蹄", "啼", "剃", "涕"],
"tian": ["天", "田", "填", "甜", "添", "腆", "舔", "佃", "畋", "钿"],
"tiao": ["条", "跳", "挑", "调", "迢", "眺", "苕", "粜", "铫", "龆"],
"tie": ["铁", "贴", "帖", "餮", "苵", "瓞", "眣", "聑"],
"ting": ["听", "停", "厅", "挺", "庭", "艇", "蜓", "霆", "町", "烃"],
"tong": ["同", "通", "铜", "桶", "痛", "童", "统", "桐", "筒", "瞳"],
"tou": ["头", "投", "透", "骰", "钭", "偷", "侸", "黈"],
"tu": ["图", "土", "兔", "吐", "突", "涂", "徒", "途", "屠", "秃"],
"tuan": ["团", "推", "腿", "退", "吞", "屯", "臀", "饨", "囤", "豚"],
"tui": ["推", "腿", "退", "颓", "蜕", "褪", "忒", "煺", "蹆", "侻"],
"tun": ["吞", "屯", "臀", "饨", "囤", "豚", "暾", "饨", "鲀", "魨"],
"tuo": ["脱", "拖", "托", "驼", "妥", "驮", "陀", "椭", "唾", "驼"],
"wa": ["挖", "哇", "瓦", "袜", "蛙", "娃", "凹", "娲", "腽", "膃"],
"wai": ["外", "崴", "歪", "喎", "竵", "顡"],
"wan": ["万", "玩", "完", "晚", "碗", "弯", "丸", "挽", "腕", "婉"],
"wang": ["王", "网", "往", "忘", "旺", "望", "亡", "枉", "妄", "罔"],
"wei": ["为", "位", "围", "喂", "微", "尾", "违", "委", "威", "伟"],
"wen": ["文", "问", "闻", "温", "稳", "蚊", "吻", "纹", "紊", "雯"],
"weng": ["翁", "嗡", "瓮", "蕹", "鹟", "螉", "瞈", "鎓"],
"wo": ["我", "握", "窝", "卧", "沃", "蜗", "涡", "斡", "龌", "肟"],
"wu": ["无", "五", "屋", "物", "吴", "武", "误", "务", "悟", "午"],
"xi": ["西", "习", "喜", "洗", "戏", "吸", "系", "息", "希", "夕"],
"xia": ["下", "夏", "虾", "瞎", "峡", "狭", "霞", "辖", "匣", "吓"],
"xian": ["先", "线", "现", "限", "险", "县", "闲", "弦", "嫌", "贤"],
"xiang": ["想", "向", "像", "香", "响", "乡", "箱", "相", "祥", "降"],
"xiao": ["小", "笑", "消", "销", "萧", "晓", "效", "校", "削", "硝"],
"xie": ["写", "谢", "些", "歇", "鞋", "斜", "胁", "携", "谐", "械"],
"xin": ["新", "心", "信", "欣", "辛", "芯", "薪", "锌", "忻", "囟"],
"xing": ["行", "星", "兴", "醒", "姓", "幸", "腥", "刑", "型", "邢"],
"xiong": ["熊", "凶", "兄", "胸", "汹", "芎", "讻", "詾", "駉", "夐"],
"xiu": ["修", "休", "绣", "秀", "锈", "羞", "朽", "嗅", "岫", "貅"],
"xu": ["需", "虚", "徐", "许", "续", "序", "蓄", "絮", "婿", "绪"],
"xuan": ["选", "轩", "宣", "悬", "旋", "喧", "玄", "癣", "炫", "绚"],
"xue": ["学", "雪", "血", "穴", "削", "靴", "谑", "踅", "噱", "泶"],
"xun": ["寻", "讯", "训", "迅", "循", "熏", "旬", "询", "殉", "勋"],
"ya": ["呀", "压", "鸭", "牙", "雅", "亚", "哑", "涯", "衙", "蚜"],
"yan": ["眼", "演", "严", "研", "盐", "沿", "烟", "艳", "厌", "宴"],
"yang": ["样", "杨", "阳", "养", "氧", "洋", "仰", "秧", "鸯", "痒"],
"yao": ["要", "药", "摇", "咬", "遥", "腰", "窑", "谣", "姚", "邀"],
"ye": ["也", "叶", "夜", "爷", "野", "业", "冶", "页", "咽", "曳"],
"yi": ["一", "以", "已", "义", "依", "衣", "医", "易", "椅", "益"],
"yin": ["因", "引", "银", "音", "饮", "隐", "印", "吟", "荫", "寅"],
"ying": ["应", "英", "影", "赢", "迎", "蝇", "营", "硬", "荧", "萤"],
"yo": ["哟", "唷", "喲", "唷"],
"yong": ["用", "永", "拥", "勇", "涌", "庸", "雍", "踊", "恿", "蛹"],
"you": ["有", "又", "右", "油", "游", "由", "友", "幼", "悠", "优"],
"yu": ["于", "雨", "鱼", "玉", "语", "育", "余", "预", "欲", "喻"],
"yuan": ["元", "远", "圆", "原", "院", "员", "冤", "怨", "缘", "袁"],
"yue": ["月", "约", "越", "乐", "岳", "阅", "跃", "钥", "曰", "粤"],
"yun": ["云", "运", "晕", "允", "韵", "蕴", "耘", "匀", "陨", "孕"],
"za": ["杂", "砸", "咋", "匝", "咂", "拶", "帀", "沞"],
"zai": ["在", "再", "栽", "载", "灾", "宰", "哉", "崽", "甾", "酨"],
"zan": ["赞", "咱", "暂", "攒", "簪", "趱", "趲", "儹", "儧", "攢"],
"zang": ["脏", "葬", "藏", "赃", "奘", "臧", "贓", "臟", "髒", "葬"],
"zao": ["早", "造", "遭", "糟", "灶", "燥", "澡", "躁", "噪", "皂"],
"ze": ["则", "泽", "择", "责", "咋", "仄", "啧", "帻", "箦", "赜"],
"zei": ["贼", "賊", "蠈", "鱡"],
"zen": ["怎", "谮", "譖", "昝", "吶"],
"zeng": ["增", "赠", "曾", "憎", "甑", "罾", "锃", "缯", "嶒", "矰"],
"zha": ["扎", "炸", "渣", "扎", "眨", "闸", "榨", "乍", "诈", "铡"],
"zhai": ["摘", "宅", "窄", "债", "寨", "斋", "砦", "祭", "夈", "膪"],
"zhan": ["站", "战", "占", "粘", "沾", "盏", "展", "崭", "斩", "绽"],
"zhang": ["张", "章", "涨", "掌", "丈", "账", "仗", "胀", "障", "瘴"],
"zhao": ["找", "照", "招", "着", "召", "兆", "赵", "朝", "爪", "诏"],
"zhe": ["这", "着", "折", "者", "遮", "哲", "浙", "辙", "蔗", "蜇"],
"zhen": ["真", "针", "阵", "镇", "震", "珍", "枕", "诊", "振", "朕"],
"zheng": ["正", "争", "征", "整", "证", "郑", "政", "症", "挣", "睁"],
"zhi": ["只", "知", "指", "制", "纸", "至", "直", "值", "支", "芝"],
"zhong": ["中", "种", "重", "众", "终", "钟", "忠", "肿", "仲", "衷"],
"zhou": ["周", "州", "粥", "轴", "肘", "帚", "咒", "宙", "昼", "骤"],
"zhu": ["主", "住", "猪", "竹", "注", "祝", "筑", "著", "煮", "朱"],
"zhua": ["抓", "挝", "摣", "膼", "簻"],
"zhuai": ["拽", "跩", "啀", "諄", "諑"],
"zhuan": ["转", "赚", "专", "砖", "撰", "篆", "传", "啭", "馔", "瑑"],
"zhuang": ["庄", "装", "壮", "撞", "桩", "妆", "幢", "戆", "壯", "狀"],
"zhui": ["追", "坠", "锥", "缀", "赘", "惴", "膇", "缒", "腏", "骓"],
"zhun": ["准", "谆", "肫", "窀", "準", "稕"],
"zhuo": ["捉", "桌", "着", "啄", "浊", "琢", "卓", "酌", "茁", "灼"],
"zi": ["子", "字", "自", "紫", "资", "籽", "滋", "姿", "滓", "兹"],
"zong": ["总", "宗", "纵", "踪", "棕", "鬃", "综", "腙", "嵕", "偬"],
"zou": ["走", "揍", "邹", "奏", "驺", "诹", "鲰", "鄹", "陬", "棷"],
"zu": ["组", "足", "祖", "阻", "族", "租", "卒", "诅", "俎", "菹"],
"zuan": ["钻", "攥", "缵", "纂", "躜", "鑽", "攥"],
"zui": ["最", "嘴", "醉", "罪", "赘", "啐", "淬", "萃", "瘁", "粹"],
"zun": ["尊", "遵", "樽", "鳟", "僔", "噂", "撙", "墫", "罇", "譐"],
"zuo": ["做", "左", "坐", "座", "作", "昨", "琢", "佐", "唑", "柞"]
}

65
src/libs/socket.ts

@ -1,11 +1,11 @@
// src/utils/websocket.ts
import type { Ref } from 'vue'
import ErrorBox from 'libs/modalUtil'
import { ref, watch } from 'vue'
import { FtMessageBox } from './messageBox'
// WebSocket客户端类
export class WebSocketClient {
private socket: WebSocket
public socket: WebSocket
private url: string
private isConnecting = false
public readonly isConnected: Ref<boolean> = ref(false)
@ -52,7 +52,7 @@ export class WebSocketClient {
return
}
// 处理事件订阅
const listeners = this.eventListeners.get(response.className) || []
const listeners = this.eventListeners.get(response.fromClass) || []
listeners.forEach(listener => listener(response))
// 全局事件监听
const globalListeners = this.eventListeners.get('*') || []
@ -69,21 +69,25 @@ export class WebSocketClient {
request: Socket.WebSocketRequest,
): Promise<Socket.WebSocketResponse & { rely: T }> {
// 等待连接建立
await new Promise<void>((resolve) => {
if (this.isConnected.value) {
resolve()
}
else {
const watcher = watch(this.isConnected, (connected) => {
if (connected) {
watcher() // 停止监听
resolve()
}
})
}
})
// 连接建立后发送请求
return this.sendRequest(request)
if (!this.isConnected.value) {
await new Promise<void>((resolve) => {
if (this.isConnected.value) {
resolve()
}
else {
const watcher = watch(this.isConnected, (connected) => {
if (connected) {
watcher() // 停止监听
resolve()
}
})
}
})
return this.sendRequest(request)
}
else { // 连接建立后发送请求
return this.sendRequest(request)
}
}
// 发送请求
@ -98,8 +102,11 @@ export class WebSocketClient {
resolve(response as Socket.WebSocketResponse & { rely: T })
}
else {
console.error('----error---', response.message)
ErrorBox.alert('这是一条错误信息底部')
// ElMessage.error(response.message)
if (response.message) {
FtMessageBox.error(response.message)
}
resolve(response as Socket.WebSocketResponse & { rely: T })
}
})
// 设置超时
@ -135,6 +142,24 @@ export class WebSocketClient {
}
}
// 类型安全的取消订阅方法
public unsubscribe(messageType: string | '*', callback: (response: Socket.WebSocketResponse) => void) {
const listeners = this.eventListeners.get(messageType)
if (listeners) {
// 过滤掉需要取消的回调函数
const filteredListeners = listeners.filter(cb => cb !== callback)
if (filteredListeners.length === 0) {
this.eventListeners.delete(messageType)
}
else {
// 否则更新监听器数组
this.eventListeners.set(messageType, filteredListeners)
}
return true // 取消成功
}
return false // 未找到监听器
}
// 重连逻辑
private reconnect() {
this.isConnecting = true

47
src/libs/timer.ts

@ -0,0 +1,47 @@
let timerId: ReturnType<typeof setInterval> | null = null
let startTime: number = 0
// 开始计时器
function startPosityveTimer(callback: (timeString: string) => void) {
if (timerId) {
return
}
startTime = Date.now()
timerId = setInterval(() => {
if (callback) {
callback(formatTime())
}
}, 1000)
}
// 停止计时器
function stopPosityveTimer() {
if (timerId) {
clearInterval(timerId)
timerId = null
}
}
// 格式化时间为 HH:MM:SS
function formatTime() {
if (!startTime) {
return '00:00:00'
}
const totalSeconds = Math.floor((Date.now() - startTime) / 1000)
const hours = Math.floor(totalSeconds / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds % 60
return [
padZero(hours),
padZero(minutes),
padZero(seconds),
].join(':')
}
// 数字补零
function padZero(num: number) {
return num < 10 ? `0${num}` : `${num}`
}
export {
startPosityveTimer,
stopPosityveTimer,
}

11
src/libs/utils.ts

@ -13,17 +13,18 @@ export function formatDateTime(template: string = 'YYYY/MM/DD HH:mm:ss'): string
}
export function roundNumber(num: string | number, digits: number) {
if (!num || !digits) {
num = Number(num)
if (!num) {
return num
}
num = Number(num)
return num.toFixed(digits)
digits = digits || 0
return Number(num.toFixed(digits))
}
export const deviceStateMap = {
export const deviceStateMap = <Record<string, any>>{
idle: '空闲',
init: '初始化设备',
preheat: '预热',
preheat: '预热',
disinfection: '消毒中',
degradation: '降解中',
finished: '消毒完成',

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 {

23
src/stores/deviceStore.ts

@ -0,0 +1,23 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const initDeviceInfo = {
appVersion: '',
deviceId: '',
deviceType: '',
deviceTypeInited: '',
ip: '',
projectType: '',
}
export const useDeviceStore = defineStore('device', () => {
const deviceInfo = ref<Device.DeviceInfo>(initDeviceInfo)
const updateDeviceInfo = (info: Device.DeviceInfo) => {
deviceInfo.value = info
}
return {
deviceInfo,
updateDeviceInfo,
}
})

97
src/stores/formulaStore.ts

@ -1,4 +1,5 @@
import { FORMULA_CONFIG_DATA } from 'libs/constant'
import { syncSendCmd } from 'apis/system'
import { cloneDeep } from 'lodash'
import { nanoid } from 'nanoid'
import { defineStore } from 'pinia'
import { ref } from 'vue'
@ -39,24 +40,21 @@ export const useFormulaStore = defineStore('formula', () => {
label: '',
value: '',
}])
const defaultFormulaInfo: Record<string, any> = createDefaultFormulaInfo()
const pressurOptionList = ref<string[]>(['10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'])
const currentSelectedFormulaInfo = ref<Formula.FormulaItem>()
const initFormulaInfo = ref<Formula.FormulaItem>({
...defaultFormulaInfo as Formula.FormulaItem,
formula_id: nanoid(), // 配方ID // 配方ID
})
const defaultFormulaInfo = ref<Formula.FormulaItem>(createDefaultFormulaInfo())
const pressurOptionList = ref<string[]>(['10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'])
const currentSelectedFormulaInfo = ref<Formula.FormulaItem>(cloneDeep(defaultFormulaInfo.value))
const selectedFormulaInfo = ref()
const initFormulaInfo = ref<Formula.FormulaItem>(cloneDeep(defaultFormulaInfo.value))
// 配方可配置数据
const configList = FORMULA_CONFIG_DATA.filter(item => item.is_visible_in_formula_page)
const formulaConfigList = ref<Formula.FormulaConfig[]>(configList)
const formulaConfigList = ref<Formula.FormulaConfig[]>([])
const formulaList = ref<Formula.FormulaItem[]>([])
const names: Record<string, any> = {}
const initData = () => {
// 重置配方默认值 及 消毒等级
formulaConfigList.value.forEach((item) => {
defaultFormulaInfo[item.setting_id] = item.default_val
// defaultFormulaInfo.value[item.setting_id] = item.default_val
names[item.setting_id] = item.name_ch
if (item.val_type === 'enum' && item.setting_id === 'loglevel') {
updateLogLevels(item)
@ -64,6 +62,17 @@ export const useFormulaStore = defineStore('formula', () => {
})
}
const updateFormulaConfigData = (data: Formula.FormulaConfig[]) => {
const config = data.filter(item => item.is_visible_in_formula_page)
formulaConfigList.value = config
const formulaMap: Record<string, any> = {}
config.forEach((item) => {
formulaMap[item.setting_id] = Number(item.val)
})
formulaMap.name = '默认配置'
defaultFormulaInfo.value = formulaMap as Formula.FormulaItem
}
const updateLogLevels = (logLevelItem: Formula.FormulaConfig) => {
const list: System.Option[] = []
const enum_display_names = logLevelItem.enum_display_names
@ -79,55 +88,35 @@ export const useFormulaStore = defineStore('formula', () => {
// 配方中的压力控制
const updatePressurList = (pressurData: string[]) => {
pressurOptionList.value = pressurData
pressurOptionList.value = cloneDeep(pressurData)
}
// 消毒页面及列表中选择的配方
// 配方管理从列表中选择的配方 用来编辑及回显
const updateSelectedFormulaData = (formulaItem: Formula.FormulaItem) => {
currentSelectedFormulaInfo.value = formulaItem
console.error('formulaItem----', formulaItem)
currentSelectedFormulaInfo.value = cloneDeep(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 updateSelectedFormulaDataByList = (formulaItem: Formula.FormulaItem) => {
selectedFormulaInfo.value = cloneDeep(formulaItem)
}
const initFormulaData = () => {
const id = nanoid()
initFormulaInfo.value = {
...defaultFormulaInfo as Formula.FormulaItem,
formula_id: id,
const initFormulaList = () => {
const params = {
className: 'SettingMgrService',
fnName: 'getAllFormula',
params: {},
}
syncSendCmd(params).then((res) => {
if (res.rely) {
formulaList.value = res.rely
}
})
}
const addFormula = () => {
const id = nanoid()
formulaList.value.push({
...defaultFormulaInfo as Formula.FormulaItem,
continued_gs: 300,
name: `配方${formulaList.value.length + 1}`,
formula_id: id, // 配方ID,
})
const initFormulaData = () => {
initFormulaInfo.value = cloneDeep(defaultFormulaInfo.value)
}
initData()
@ -142,12 +131,14 @@ export const useFormulaStore = defineStore('formula', () => {
formulaList,
formulaConfigList,
logLevels,
selectedFormulaInfo,
defaultFormulaInfo,
// 方法
updatePressurList,
updateSelectedFormulaData,
addFormula,
initFormulaData,
updateFormulaList,
initFormulaList,
updateSelectedFormulaDataByList,
updateFormulaConfigData,
}
})

80
src/stores/homeStore.ts

@ -23,30 +23,23 @@ const h2O2Data: Home.DisplayrelyMgrParams[] = [{
}, {
type: 'probe1',
title: '探头1',
temp: '20',
rh: '30',
rs: '40',
h2o2: '20',
temp: '0',
rh: '0',
rs: '0',
h2o2: '0',
}, {
type: 'probe2',
title: '探头2',
temp: '20',
rh: '30',
rs: '40',
h2o2: '20',
temp: '0',
rh: '0',
rs: '0',
h2o2: '0',
}]
const liquidItem: Home.LiquidData = {
nowLiquid: 840,
workState: 'idle',
workStateDisplay: '空闲',
}
export const useHomeStore = defineStore('home', () => {
const h2O2SensorData = ref(h2O2Data)
const liquidData = ref(liquidItem)
const liquidTotal = ref(2500)
const curStateRemainTime = ref<string>()
const loglevel = ref('1')
const logLevelList = [{
label: '1级',
value: 1,
@ -80,29 +73,39 @@ export const useHomeStore = defineStore('home', () => {
params: {},
}
const res = await sendCmd(pressureParams)
if (res.rely) {
pressureConfig.value = res.rely
}
pressureConfig.value = res
}
const updateHomeLiquid = (liquidInfo: Home.LiquidData) => {
liquidData.value = liquidInfo
// 设置压力
const updatePressure = async (pressureData: string | number[]) => {
if (pressureData && pressureData.length) {
const type = pressureData[0]
const pressureTypeParams = {
className: 'PipelinePressureControl',
fnName: 'setType',
params: {
type,
},
}
await sendCmd(pressureTypeParams)
// type 是正压或负压时保存设置的压力值
if (type === 'positivePressure' || type === 'positivePressure') {
const intensity = pressureData[1]
const intensityParams = {
className: 'PipelinePressureControl',
fnName: 'setIntensity',
params: {
intensity,
},
}
await sendCmd(intensityParams)
}
}
}
// 开始消毒时的逻辑判断
const startDisinfect = () => {
// 是否进行设备的初始化 return false
// 是否选择了配方 return false
// 未选择配方时,是否配置了默认值 return false
// 检查消毒液余量是否满足此次消毒 return false
// 检查压力是否正常 return false
// 是否选择了消毒级别 return false
/*
*className: "DisinfectionCtrlService",
*fnName: "start",
**/
disinfectionState.value.state = 'disinfection'
return true
// 设置消毒等级
const updateLogLevel = (level: string) => {
loglevel.value = String(level)
}
const stopDisinfect = () => {
@ -145,17 +148,16 @@ export const useHomeStore = defineStore('home', () => {
return {
// 变量
h2O2SensorData,
liquidData,
liquidTotal,
logLevelList,
loglevel,
deviceStete,
disinfectionState,
pressureConfig,
curStateRemainTime,
// 方法
updateHomeData,
updateHomeLiquid,
startDisinfect,
updatePressure,
updateLogLevel,
stopDisinfect,
setDeviceState,
updateHomeDisinfectionState,

68
src/stores/initHomeData.ts

@ -0,0 +1,68 @@
import { sendCmd } from '@/apis/system'
import { useHomeStore } from 'stores/homeStore'
import { useLiquidStore } from 'stores/liquidStore'
const homeStore = useHomeStore()
const liquidStore = useLiquidStore()
export const initData = async () => {
// 初始化消毒液桶数据
console.log('--------1-------')
await initLiquidConfig()
// 轮询设备状态、消毒状态、消毒液使用状态
// setInterval(() => {
// }, 1000)
console.log('--------2-------')
await initBaseData()
console.log('--------3-------')
}
const initLiquidConfig = async () => {
const params = {
className: 'AddLiquidService',
fnName: 'getServiceConfig',
params: {},
}
const liquidConfig = await sendCmd(params)
console.log('liquidConfig----------', liquidConfig)
liquidStore.initLiquidConfig(liquidConfig)
}
const initBaseData = async () => {
const envParams = {
fnName: 'readH2O2SensorData',
className: 'FrontEndRealtimeDisplayContentMgr',
params: {},
}
const resData = await sendCmd(envParams)
if (resData.val.length) {
homeStore.updateHomeData(resData.val)
}
// 获取消毒时状态
const disinfectionParams = {
className: 'DisinfectionCtrlServiceExt',
fnName: 'getState',
params: {},
}
const disinfectionData = await sendCmd(disinfectionParams)
homeStore.updateHomeDisinfectionState(disinfectionData)
// 消毒液使用状态
const liquidParams = {
fnName: 'getState',
className: 'AddLiquidService',
params: {},
}
const liquidData = await sendCmd(liquidParams)
liquidStore.updateLiquidState(liquidData)
const deviceParams = {
className: 'AppCore',
fnName: 'getState',
params: {},
}
// 当前设备状态
const deviceData = await sendCmd(deviceParams)
homeStore.setDeviceState(deviceData)
}

39
src/stores/liquidStore.ts

@ -1,21 +1,46 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const liquidItem: Liquid.LiquidData = {
nowLiquid: 840,
workState: 'idle',
workStateDisplay: '空闲',
}
export const useLiquidStore = defineStore('Liquid', () => {
const liquidAddWorkState = ref<Liquid.LiquidState>('idle')
const updateAddLiquidWorkState = (state: Liquid.LiquidState) => {
liquidAddWorkState.value = state
const liquidAddWorkState = ref<Liquid.LiquidData>(liquidItem)
const liquidDrainWorkState = ref<Liquid.LiquidData>(liquidItem)
const liquidStateData = ref(liquidItem)
const liquidTotal = ref<number>(2500)
const liquidPeriod = ref(300)
const updateAddLiquidWorkState = (item: Liquid.LiquidData) => {
liquidAddWorkState.value = item
liquidStateData.value = item
}
const updateDrainLiquidWorkState = (item: Liquid.LiquidData) => {
liquidDrainWorkState.value = item
liquidStateData.value = item
}
const liquidDrainWorkState = ref<Liquid.LiquidState>('idle')
const updateDrainLiquidWorkState = (state: Liquid.LiquidState) => {
liquidDrainWorkState.value = state
const updateLiquidState = (liquidInfo: Liquid.LiquidData) => {
liquidStateData.value = liquidInfo
}
const initLiquidConfig = async (liquidConfig: Liquid.LiquidConfig) => {
liquidTotal.value = liquidConfig.maxLiquid
liquidPeriod.value = liquidConfig.updatePeriod
}
return {
liquidAddWorkState,
updateAddLiquidWorkState,
liquidDrainWorkState,
liquidTotal,
liquidPeriod,
liquidStateData,
updateAddLiquidWorkState,
updateDrainLiquidWorkState,
initLiquidConfig,
updateLiquidState,
}
})

12
src/stores/sealStore.ts

@ -3,12 +3,18 @@ import { ref } from 'vue'
export const useSealStore = defineStore('seal', () => {
const sealState = ref('idle')
const updateSealState = (status: Seal.SealState) => {
sealState.value = status
const sealInfo = ref<Seal.SealStateItem>({
pressure: '0',
workState: 'idle',
workStateDisplay: '空闲',
})
const updateSealInfo = (dataInfo: Seal.SealStateItem) => {
sealInfo.value = dataInfo
}
return {
sealState,
updateSealState,
sealInfo,
updateSealInfo,
}
})

67
src/stores/systemStore.ts

@ -1,27 +1,46 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useSystemStore = defineStore('system', {
state: () => ({
systemUser: {
username: '',
},
loginForm: {
name: import.meta.env.FT_NODE_ENV !== 'prod' ? 'admin' : '',
pwd: import.meta.env.FT_NODE_ENV !== 'prod' ? '9973' : '',
},
languages: [{
name: '中文',
value: 'zh-cn',
}, {
name: '英文',
value: 'en',
}],
menuExpand: true,
isDebug: import.meta.env.FT_NODE_ENV !== 'prod',
streamVisible: false,
systemList: [],
}),
actions: {
},
persist: false,
export const useSystemStore = defineStore('system', () => {
const systemUser = ref({
username: '',
})
const loginForm = ref({
name: import.meta.env.FT_NODE_ENV !== 'prod' ? 'admin' : '',
pwd: import.meta.env.FT_NODE_ENV !== 'prod' ? '9973' : '',
})
const languages = [{
name: '中文',
value: 'zh-cn',
}, {
name: '英文',
value: 'en',
}]
const menuExpand = true
const isDebug = import.meta.env.FT_NODE_ENV !== 'prod'
const streamVisible = false
const systemList = ref([])
const loading = ref(false)
const updateLoading = (loadVal: boolean) => {
loading.value = loadVal
setTimeout(() => {
loading.value = false
}, 1500)
}
return {
systemUser,
loginForm,
languages,
menuExpand,
isDebug,
streamVisible,
systemList,
loading,
updateLoading,
}
})

9
src/types/device.d.ts

@ -4,4 +4,13 @@ declare namespace Device {
loginUser: User.LoginUsr
state: string
}
interface DeviceInfo {
appVersion: string
deviceId: string
deviceType: string
deviceTypeInited: string
ip: string
projectType: string
}
}

6
src/types/home.d.ts

@ -14,12 +14,6 @@ declare namespace Home {
val: DisplayrelyMgr[]
}
interface LiquidData {
nowLiquid: number
workState: string
workStateDisplay: string
}
interface GrandsonMethods {
getFormData: () => void
}

12
src/types/liquid.d.ts

@ -2,4 +2,16 @@ declare namespace Liquid {
// idle,addingLiquid,emptyLineLiquid 空闲,加液中,清空管路中
// idle,work 空闲,排液中
type LiquidState = 'idle' | 'addingLiquid' | 'emptyLineLiquid' | 'work'
interface LiquidData {
nowLiquid: number
workState: string
workStateDisplay: string
}
interface LiquidConfig {
show: boolean
maxLiquid: number
updatePeriod: number
}
}

9
src/types/seal.d.ts

@ -2,7 +2,12 @@ declare namespace Seal {
// idle // 空闲
// initDevice //初始化设备
// inflating //打压中
// leakTesting  // 检漏中
// stopping   // 停止中
// leakTesting // 检漏中
// stopping // 停止中
type SealState = 'idle' | 'initDevice' | 'inflating' | 'leakTesting' | 'stopping'
interface SealStateItem {
pressure: string
workState: SealState
workStateDisplay: string
}
}

10
src/types/setting.d.ts

@ -5,9 +5,9 @@ declare namespace Setting {
interface User {
uid: string
id?: string | number,
passwd: string,
confirmPassword?: string,
is_admin: string | boolean,
id?: string | number
passwd: string
confirmPassword?: string
is_admin: string | boolean
}
}
}

8
src/types/websocket.d.ts

@ -11,11 +11,11 @@ declare namespace Socket {
// 接收响应的基础数据结构类型
interface WebSocketResponse<T = any> {
ackcode: number
rely?: T
rely: T
messageId: string
messageType: 'Ack'
className: string
fnName: string
messageType: 'Ack' | 'Report'
fromClass: string
fromFn: string
timeStamp: number
message?: string
}

64
src/views/audit/index.vue

@ -1,38 +1,62 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { syncSendCmd } from 'apis/system'
import { onMounted, ref } from 'vue'
const pageNum = ref(1)
const pageSize = ref(10)
onMounted(() => {
//
getAuditList()
})
const getAuditList = () => {
const params = {
className: 'AuditMgrService',
fnName: 'getRecords',
params: {
page: pageNum.value,
page_size: pageSize.value,
},
}
syncSendCmd(params).then((res) => {
console.log('audit === ', res)
})
}
const tableData = ref()
const onExportRecord = () => {}
</script>
<template>
<main class="main-content">
<div>
<div class="audit-export">
<bt-button
type="primary"
button-text="导出"
@click="onExportRecord"
/>
</div>
<div class="dashboard-container">
<main class="main-content">
<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="content" label="操作内容" />
<el-table-column prop="createTime" label="操作时间" />
</el-table>
<div class="audit-export">
<bt-button
type="primary"
button-text="导出"
@click="onExportRecord"
/>
</div>
<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="content" label="操作内容" />
<el-table-column prop="createTime" label="操作时间" />
</el-table>
</div>
</div>
</div>
</main>
</main>
</div>
</template>
<style lang="scss" scoped>
.main-content{
height: 100%;
height: $main-container-height;
overflow: hidden;
padding: 10px;
background: $gradient-color;
box-shadow: 0px 1px 5px 0px rgba(9, 39, 62, 0.15);
.audit-export{
margin: 2vw;
}

37
src/views/debug/index.vue

@ -1,4 +1,5 @@
<script lang="ts" setup>
import { sendCmd } from 'apis/system'
import { roundNumber } from 'libs/utils'
import { useHomeStore } from 'stores/homeStore'
import { ref, watch } from 'vue'
@ -15,13 +16,40 @@ watch(() => homeStore.h2O2SensorData, (newVal) => {
}, { deep: true })
//
const onAddLiquid = () => {
const onAddLiquid = async () => {
const params = {
className: 'AddLiquidService',
fnName: 'start',
params: {
stopatg: 100,
},
}
await sendCmd(params)
}
//
const onDisLiquid = () => {
const onDisLiquid = async () => {
const params = {
className: 'DrainLiquidService',
fnName: 'start',
params: {},
}
sendCmd(params)
}
//
const onOffLiquidMotor = () => {
const stopAddparams = {
className: 'AddLiquidService',
fnName: 'stop',
params: {},
}
sendCmd(stopAddparams)
const params = {
className: 'DrainLiquidService',
fnName: 'stop',
params: {},
}
sendCmd(params)
}
//
@ -33,7 +61,8 @@ const onCloseAirPump = () => {
}
//
const onOpenHeat = () => {}
const onOpenHeat = () => {
}
//
const onCloseHeat = () => {}
@ -272,7 +301,7 @@ const onBottomState = () => {}
<style lang="scss" scoped>
$lineHeight: 12vh;
.main-content{
height: 100%;
height: $main-container-height;
overflow: hidden;
background: $gradient-color;
padding-top: 50px;

58
src/views/formula/index.vue

@ -1,46 +1,56 @@
<script lang="ts" setup>
import { useFormulaStore } from '@/stores/formulaStore'
import { syncSendCmd } from 'apis/system'
import FormulaConfig from 'components/formula/FormulaConfig.vue'
import FormulaTable from 'components/formula/FormulaTable.vue'
const formulaStore = useFormulaStore()
const onAddFormula = () => {
formulaStore.addFormula()
const params = {
className: 'SettingMgrService',
fnName: 'addNewFormula',
params: {},
}
syncSendCmd(params).then(() => {
formulaStore.initFormulaList()
})
}
</script>
<template>
<main class="main-content">
<div class="formula-left">
<div class="formula-add">
<bt-button
button-text="新增配方"
border-radius="10px"
@click="onAddFormula"
>
<template #icon>
<el-icon>
<Plus />
</el-icon>
</template>
</bt-button>
<div class="dashboard-container">
<main class="main-content">
<div class="formula-left">
<div class="formula-add">
<bt-button
button-text="新增配方"
border-radius="10px"
@click="onAddFormula"
>
<template #icon>
<el-icon>
<Plus />
</el-icon>
</template>
</bt-button>
</div>
<div class="formula-list">
<FormulaTable />
</div>
</div>
<div class="formula-list">
<FormulaTable />
<div class="formula-right">
<FormulaConfig type="formula" />
</div>
</div>
<div class="formula-right">
<FormulaConfig type="formula" />
</div>
</main>
</main>
</div>
</template>
<style lang="scss" scoped>
.main-content{
display: grid;
grid-template-columns: repeat(3,1fr);
height: 100%;
gap: 10px;
height: $main-container-height;
gap: 5px;
overflow: hidden;
.formula-left{
box-shadow: 0px 1px 5px 0px rgba(9, 39, 62, 0.15);

5
src/views/home/chart.vue

@ -5,6 +5,7 @@ import homeFinish from 'assets/images/home/home-finish.svg'
import homeSettingSvg from 'assets/images/home/home-setting.svg'
import HomeFormula from 'components/home/HomeFormula.vue'
import LineChart from 'components/home/LineChart.vue'
import { stopTimer } from 'libs/countdownTimer'
import { ref, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
@ -25,7 +26,7 @@ const onDisinfectConfig = () => {
}
//
const onFinishDisinfect = () => {
// countdownTimer.value && countdownTimer.value.stopTimer()
stopTimer()
homeStore.updateHomeDisinfectionState({
...disinfectionState.value,
state: 'idle',
@ -130,7 +131,7 @@ const goHome = () => {
.main-content{
overflow: hidden;
background: $gradient-color;
height: $main-container;
height: $main-container-height;
.line-chart-formula{
width: 25vw;
background: #E0F0FF;

50
src/views/home/index.vue

@ -1,47 +1,52 @@
<script lang="ts" setup>
import { roundNumber } from '@/libs/utils'
import { useFormulaStore } from '@/stores/formulaStore'
import { useSystemStore } from '@/stores/systemStore'
import homeLiquid from 'assets/images/home/home-liquid.svg'
import Environment from 'components/home/Environment.vue'
import HomeFormula from 'components/home/HomeFormula.vue'
import HomeLogLevel from 'components/home/HomeLogLevel.vue'
import HomeOperation from 'components/home/HomeOperation.vue'
import HomeSetting from 'components/home/HomeSetting.vue'
import { useHomeStore } from 'stores/homeStore'
import { computed, ref, watch, watchEffect } from 'vue'
import { useLiquidStore } from 'stores/liquidStore'
import { computed, ref, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const homeStore = useHomeStore()
const liquidStore = useLiquidStore()
const formulaStore = useFormulaStore()
const systemStore = useSystemStore()
const environmentParams = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[0])
const probe1Params = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[1])
const probe2Params = ref<Home.DisplayrelyMgrParams>(homeStore.h2O2SensorData[2])
const liquidInfo = ref<Home.LiquidData>(homeStore.liquidData)
const liquidTotal = ref<number>(homeStore.liquidTotal)
const liquidInfo = ref<Liquid.LiquidData>(liquidStore.liquidStateData)
const liquidTotal = ref<number>(liquidStore.liquidTotal)
const formulaInfo = ref()
watch(() => homeStore.h2O2SensorData, (newVal) => {
if (newVal && newVal.length) {
environmentParams.value = newVal[0]
}
}, { deep: true })
watch(() => formulaStore.currentSelectedFormulaInfo, (newVal) => {
formulaInfo.value = newVal
})
const loading = ref(false)
watchEffect(() => {
liquidTotal.value = homeStore.liquidTotal
formulaInfo.value = formulaStore.currentSelectedFormulaInfo
liquidTotal.value = liquidStore.liquidTotal
liquidInfo.value = liquidStore.liquidStateData
loading.value = systemStore.loading
if (homeStore.h2O2SensorData && homeStore.h2O2SensorData.length) {
environmentParams.value = homeStore.h2O2SensorData[0]
}
})
const nowLiquidProgress = computed(() => {
return Number(((liquidInfo.value.nowLiquid / liquidTotal.value) * 100).toFixed(2))
const nl = roundNumber(Number(((liquidInfo.value.nowLiquid / liquidTotal.value) * 100)), 0)
return nl
})
const nowLiquid = computed(() => {
return roundNumber(liquidInfo.value.nowLiquid, 0)
})
</script>
<template>
<div class="home">
<div v-loading="loading" class="home">
<div v-if="route.path === '/home'" class="home-grid-container">
<div class="home-grid-item merged-cell">
<div class="home-left">
@ -57,18 +62,18 @@ const nowLiquidProgress = computed(() => {
</div>
</div>
<div class="card-num-g">
{{ liquidInfo.nowLiquid }}g
{{ nowLiquid }}g
</div>
</template>
</el-card>
<el-card class="card">
<Environment :env-params="environmentParams" lineColor="#ffb380"/>
<Environment :env-params="environmentParams" line-color="#ffb380" />
</el-card>
<el-card class="card">
<Environment :env-params="probe1Params" lineColor="#80ffb3"/>
<Environment :env-params="probe1Params" line-color="#80ffb3" />
</el-card>
<el-card class="card">
<Environment :env-params="probe2Params" lineColor="#cc99ff"/>
<Environment :env-params="probe2Params" line-color="#cc99ff" />
</el-card>
</div>
</div>
@ -92,7 +97,7 @@ const nowLiquidProgress = computed(() => {
<style lang="scss" scoped>
$input-height: 3rem;
.home{
min-height: 84vh;
min-height: $main-container-height;
}
.home-grid-container {
display: grid;
@ -150,7 +155,6 @@ const nowLiquidProgress = computed(() => {
.home-right{
width: 31vw;
height: 83vh;
background: $gradient-color;
position: relative;
}

160
src/views/liquid/index.vue

@ -1,74 +1,185 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useHomeStore } from '@/stores/homeStore'
import { useLiquidStore } from '@/stores/liquidStore'
import { useSealStore } from '@/stores/sealStore'
import { useSystemStore } from '@/stores/systemStore'
import { sendCmd, subscribeEvent, syncSendCmd } from 'apis/system'
import homeFinish 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 { ElMessage } from 'element-plus'
import { ref, watchEffect } from 'vue'
import { roundNumber } from 'libs/utils'
import { computed, onMounted, ref, watchEffect } from 'vue'
const liquidStore = useLiquidStore()
const homeStore = useHomeStore()
const sealStore = useSealStore()
const systemStore = useSystemStore()
const stopatg = ref()
const inputValue = ref('')
const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('number')
const softKeyboardRef = ref()
const addWorkState = ref(liquidStore.liquidAddWorkState)
const drainWorkState = ref(liquidStore.liquidDrainWorkState)
const liquidStateData = ref(liquidStore.liquidStateData)
//
const addWorkState = ref(liquidStore.liquidStateData)
//
const drainWorkState = ref(liquidStore.liquidStateData)
//
const liquidTotoal = ref(liquidStore.liquidTotal)
const disinfectionState = ref(homeStore.disinfectionState)
const sealInfo = ref(sealStore.sealInfo)
const loading = ref(false)
onMounted(() => {
subScribeLiquid()
})
watchEffect(() => {
stopatg.value = inputValue.value
addWorkState.value = liquidStore.liquidAddWorkState
drainWorkState.value = liquidStore.liquidDrainWorkState
liquidTotoal.value = liquidStore.liquidTotal
liquidStateData.value = liquidStore.liquidStateData
disinfectionState.value = homeStore.disinfectionState
sealInfo.value = sealStore.sealInfo
loading.value = systemStore.loading
})
const subScribeLiquid = () => {
subscribeEvent('stateUpdate', (data: Socket.WebSocketResponse<Liquid.LiquidData>) => {
if (data.fromClass === 'AddLiquidService') {
liquidStore.updateAddLiquidWorkState(data.rely)
}
if (data.fromClass === 'DrainLiquidService') {
liquidStore.updateDrainLiquidWorkState(data.rely)
}
})
}
const openKeyboard = () => {
keyboardVisible.value = true
}
const nowLiquid = computed(() => {
return roundNumber(liquidStateData.value.nowLiquid, 0)
})
const handleConfirm = (value: string) => {
console.log('确认输入:', value)
}
const onStartAddLiquid = () => {
// TODO
const onStartAddLiquid = async () => {
const statusName = getDeviceStatus()
if (statusName) {
ElMessage.warning(statusName)
return
}
if (!stopatg.value || stopatg.value < 0) {
FtMessage.warning('请输入加液容量')
return
}
if (drainWorkState.value !== 'idle') {
ElMessage.warning('正在排液中,不可进行加液操作')
if (stopatg.value > liquidTotoal.value) {
FtMessage.warning('加液容量不能大于总容量')
return
}
liquidStore.updateAddLiquidWorkState('addingLiquid')
const params = {
className: 'AddLiquidService',
fnName: 'start',
params: {
stopatg: Number(stopatg.value),
},
}
systemStore.updateLoading(true)
syncSendCmd(params)
//
const subParams = {
className: 'AddLiquidService',
fnName: 'startStateReport',
params: {},
}
syncSendCmd(subParams)
}
const onStopAddLiquid = () => {
liquidStore.updateAddLiquidWorkState('idle')
const params = {
className: 'AddLiquidService',
fnName: 'stop',
params: {},
}
systemStore.updateLoading(true)
syncSendCmd(params)
}
const onStartDrainLiquid = () => {
// TODO
if (drainWorkState.value !== 'idle' || addWorkState.value !== 'idle') {
ElMessage.warning('正在加液中,不可进行排液操作')
//
const onStartDrainLiquid = async () => {
const statusName = getDeviceStatus()
if (statusName) {
ElMessage.warning(statusName)
return
}
liquidStore.updateDrainLiquidWorkState('work')
console.log('正在执行排液')
const params = {
className: 'DrainLiquidService',
fnName: 'start',
params: {},
}
systemStore.updateLoading(true)
await sendCmd(params)
//
const subParams = {
className: 'DrainLiquidService',
fnName: 'startStateReport',
params: {},
}
syncSendCmd(subParams)
// liquidStore.updateDrainLiquidWorkState('work')
}
const onStopDrainLiquid = () => {
if (drainWorkState.value === 'idle') {
const onStopDrainLiquid = async () => {
if (drainWorkState.value.workState === 'idle') {
ElMessage.warning('正在加液中,不可进行排液操作')
return
}
liquidStore.updateDrainLiquidWorkState('idle')
const params = {
className: 'DrainLiquidService',
fnName: 'stop',
params: {},
}
systemStore.updateLoading(true)
await syncSendCmd(params)
}
const getDeviceStatus = () => {
let statusName = ''
if (disinfectionState.value.state !== 'idle' && disinfectionState.value.state !== 'finished') {
statusName = '正在进行消毒,不可操作'
return statusName
}
//
if (addWorkState.value.workState !== 'idle') {
statusName = '正在进行加液操作...'
return statusName
}
//
if (drainWorkState.value.workState !== 'idle') {
statusName = '正在进行排液操作...'
return statusName
}
//
if (sealInfo.value.workState !== 'idle') {
statusName = '正在进行排液操作...'
return statusName
}
return statusName
}
</script>
<template>
<div>
<div v-loading="loading">
<main class="main-content">
<div class="liquid-left">
<LiquidLevel />
@ -78,7 +189,7 @@ const onStopDrainLiquid = () => {
<div class="liquid-surplus-title">
<div>当前液位</div>
<div class="liquid-title-g">
2000g
{{ nowLiquid }}g
</div>
</div>
@ -97,7 +208,8 @@ const onStopDrainLiquid = () => {
<bt-button
type="primary"
button-text="2500g"
color="#ffffff"
bg-color="#2892F3"
text-color="#ffffff"
height="4rem"
text-size="24px"
/>
@ -110,7 +222,7 @@ const onStopDrainLiquid = () => {
<div>
<div class="liquid-add-btn">
<bt-button
v-if="addWorkState === 'idle'"
v-if="addWorkState.workState === 'idle'"
button-text="开始加液"
bg-color="#31CB7A"
text-color="#FFFFFF"
@ -142,7 +254,7 @@ const onStopDrainLiquid = () => {
</div>
<div class="liquid-drain">
<bt-button
v-if="drainWorkState === 'idle'"
v-if="drainWorkState.workState === 'idle'"
button-text="开始排液"
bg-color="#2892F3"
text-color="#FFFFFF"
@ -192,7 +304,7 @@ const onStopDrainLiquid = () => {
.main-content{
display: grid;
grid-template-columns: repeat(3,1fr);
height: 100%;
height: $main-container-height;
gap: 10px;
overflow: hidden;
.liquid-left{

2
src/views/login/index.vue

@ -25,13 +25,11 @@ const rules = {
const loginHandle = async () => {
try {
console.log('--sys.loginForm--', sys.loginForm)
const valid = await formRef.value.validate()
if (!valid) {
return
}
login(sys.loginForm).then(async (res) => {
console.log('res.ackcode---', res.ackcode)
if (res.ackcode === 0) {
setToken('login success')
await router.push('/home')

230
src/views/seal/index.vue

@ -1,29 +1,126 @@
<script lang="ts" setup>
import { FtMessage } from '@/libs/message'
import { useSealStore } from '@/stores/sealStore'
import { useSystemStore } from '@/stores/systemStore'
import { subscribeEvent, syncSendCmd } from 'apis/system'
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 SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
import DashboardChart from 'components/seal/DashboardChart.vue'
import { ref, watch } from 'vue'
import { startPosityveTimer, stopPosityveTimer } from 'libs/timer'
import { computed, onMounted, ref, watchEffect } from 'vue'
const sealStore = useSealStore()
const sealState = ref(sealStore.sealState)
const systemStore = useSystemStore()
const sealInfo = ref(sealStore.sealInfo)
const inputValue = ref('')
const keyboardVisible = ref(false)
const keyboardType = ref<'text' | 'number'>('number')
const softKeyboardRef = ref()
const inflationTime = ref()
const sealRemainTimeS = ref<string>()
const currentPressure = ref(sealStore.sealInfo.pressure)
const realTimePressure = ref(sealStore.sealInfo.pressure)
const loading = ref(false)
watch(() => sealStore.sealState, (newVal) => {
sealState.value = newVal
onMounted(() => {
// initData()
subscribeSealState()
})
const getFirstPressure = () => {
// leakTesting6
if (sealInfo.value.workState === 'leakTesting' && !currentPressure.value) {
setTimeout(() => {
currentPressure.value = realTimePressure.value
}, 6000)
}
}
watchEffect(() => {
sealInfo.value = sealStore.sealInfo
inflationTime.value = inputValue.value
realTimePressure.value = sealStore.sealInfo.pressure
getFirstPressure()
})
const subscribeSealState = () => {
//
subscribeEvent('stateUpdate', (data: Socket.WebSocketResponse<Seal.SealStateItem>) => {
if (data.fromClass === 'AirLeakDetectTest') {
//
sealStore.updateSealInfo(data.rely)
}
})
}
const stopText = computed(() => {
return sealInfo.value.workState === 'stopping' ? '停止中...' : '停止测试'
})
const openKeyboard = () => {
keyboardVisible.value = true
}
// const pressure = ref()
const onStartTest = () => {
//
sealStore.updateSealState('initDevice')
if (!inflationTime.value) {
FtMessage.warning('请输入测试时间')
return
}
const params = {
className: 'AirLeakDetectTest',
fnName: 'start',
params: {
inflationTimeMs: Number(inflationTime.value),
},
}
loading.value = true
syncSendCmd(params).then(() => {
loading.value = false
FtMessage.success('开始执行密封测试')
//
startPosityveTimer((time) => {
sealRemainTimeS.value = time
})
//
const subParams = {
className: 'AirLeakDetectTest',
fnName: 'startStateReport',
params: {},
}
syncSendCmd(subParams)
})
}
const onFinishTest = () => {
sealStore.updateSealState('idle')
// sealStore.updateSealState('idle')
const stopParams = {
className: 'AirLeakDetectTest',
fnName: 'stop',
params: {},
}
//
stopPosityveTimer()
systemStore.updateLoading(true)
loading.value = true
syncSendCmd(stopParams).then(() => {
loading.value = false
FtMessage.success('测试已停止')
})
}
const handleConfirm = (value: string) => {
console.log('确认输入:', value)
}
const stopDisabled = computed(() => {
return sealInfo.value.workState === 'stopping' || sealInfo.value.workState === 'idle'
})
</script>
<template>
<div class="dashboard-container">
<div v-loading="loading" class="dashboard-container">
<main class="main-content">
<div class="seal-left">
<!-- 仪表盘 -->
@ -33,16 +130,26 @@ const onFinishTest = () => {
</div>
<div class="seal-opt">
<div class="seal-status">
<div class="seal-time-text">测试时间</div>
<div class="seal-time-statue seal-time-text">
<div class="seal-time-text">
测试时间
</div>
<div v-if="sealInfo.workState === 'idle'" class="seal-time-statue seal-time-text">
未开始
</div>
<div v-else>
{{ sealRemainTimeS }}
</div>
</div>
<div class="seal-status">
<div class="seal-diff-text">气压差值</div>
<div class="seal-diff-statue seal-diff-text">
<div class="seal-diff-text">
实时值
</div>
<div v-if="sealInfo.workState === 'idle'" class="seal-diff-statue seal-diff-text">
未开始
</div>
<div v-else>
{{ realTimePressure }}
</div>
</div>
</div>
</div>
@ -55,12 +162,37 @@ const onFinishTest = () => {
测试前气压
</div>
<div class="title-text title-text-kpa">
<span>2</span>
<span>{{ currentPressure }}</span>
<span class="title-kpa-pl">KPa</span>
</div>
</div>
</div>
<div class="seal-right-btn">
<div class="seal-input">
<div class="inflation-time">
测试时间
</div>
<el-input
v-model="inflationTime"
v-prevent-keyboard
class="input"
name="inflation"
placeholder="请输入"
style="height: 4rem"
@focus="openKeyboard"
>
<template #append>
<bt-button
type="primary"
button-text="ms"
bg-color="#2892F3"
text-color="#ffffff"
height="4rem"
text-size="24px"
/>
</template>
</el-input>
</div>
<div>
<div class="seal-add-btn">
<bt-button
@ -71,7 +203,7 @@ const onFinishTest = () => {
height="7vh"
text-size="24px"
border-radius="12px"
:disabled="sealState !== 'idle'"
:disabled="sealInfo.workState !== 'idle'"
@click="onStartTest"
>
<template #icon>
@ -81,14 +213,14 @@ const onFinishTest = () => {
</div>
<div class="seal-add-btn">
<bt-button
button-text="停止加液"
:button-text="stopText"
bg-color="#FF6767"
text-color="#FFFFFF"
width="27vw"
height="7vh"
text-size="24px"
border-radius="12px"
:disabled="sealState === 'idle'"
:disabled="stopDisabled"
@click="onFinishTest"
>
<template #icon>
@ -100,6 +232,15 @@ const onFinishTest = () => {
</div>
</div>
</main>
<SoftKeyboard
ref="softKeyboardRef"
v-model="inputValue"
:is-visible="keyboardVisible"
:keyboard-type="keyboardType"
@update-keyboard-visible="(visible: boolean) => keyboardVisible = visible"
@confirm="handleConfirm"
@close="keyboardVisible = false"
/>
</div>
</template>
@ -107,9 +248,8 @@ const onFinishTest = () => {
.main-content{
display: grid;
grid-template-columns: repeat(3,1fr);
height: 100%;
height: $main-container-height;
gap: 10px;
height: 100%;
.seal-left{
background: #FFFFFF;
grid-column: 1 / 3;
@ -165,32 +305,16 @@ const onFinishTest = () => {
font-weight: 600;
}
}
.seal-add-btn{
width: 24vw;
height: 8vh;
border-radius: 12px;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
margin: 2rem;
}
.seal-right{
background: $gradient-color;
display: grid;
grid-template-rows: 1fr 1fr 1fr;
.seal-right-btn{
display: flex;
justify-content: center;
align-items: center;
}
grid-template-rows: repeat(3, 1fr);
.left-title{
padding-top: 3.5vw;
padding-left: 3.5vw;
padding-left: 2.5vw;
display: flex;
height: 25vh;
.title-text-test{
display: flex;
justify-content: center;
@ -208,5 +332,37 @@ const onFinishTest = () => {
}
}
}
.seal-right-btn{
height: 30vh;
grid-row: 2 / 4;
margin-top: -2rem;
.seal-input{
padding-left: 2rem;
height: 10rem;
font-size: 2.143vw;
font-weight: 400;
.inflation-time{
height: 4rem;
}
.seal-diff-text{
height: 4rem;
}
.input{
width: 25vw;
}
}
.seal-add-btn{
width: 24vw;
height: 8vh;
border-radius: 12px;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
margin: 2rem;
}
}
}
</style>

4
src/views/setting/index.vue

@ -34,7 +34,7 @@ const selectItem = (menuCode: string) => {
<div class="setting-right">
<History v-if="selectedMenuCode === 'history'"/>
<div v-if="selectedMenuCode === 'defaultFormula'">
<FormulaConfig />
<FormulaConfig type="setting" />
</div>
<div v-if="selectedMenuCode === 'user'">
<User />
@ -54,7 +54,7 @@ const selectItem = (menuCode: string) => {
.main-content{
display: grid;
grid-template-columns: repeat(3,1fr);
height: 100%;
height: $main-container-height;
gap: 10px;
overflow: hidden;
.setting-left{

Loading…
Cancel
Save