消毒机设备
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
11 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <script lang="ts" setup>
  2. import { sendCmd, syncSendCmd } from 'apis/system'
  3. import homeFinish from 'assets/images/home/home-finish.svg'
  4. import RunFormulaConfig from 'components/formula/RunFormulaConfig.vue'
  5. import LineChart from 'components/home/LineChart.vue'
  6. import { ElLoading } from 'element-plus'
  7. import { stopTimer } from 'libs/countdownTimer'
  8. import { FtMessage } from 'libs/message'
  9. import { FtMessageBox } from 'libs/messageBox'
  10. import { compareJSON, deviceStateMap } from 'libs/utils'
  11. import { computed, onMounted, onUnmounted, provide, ref, watchEffect } from 'vue'
  12. import { useRouter } from 'vue-router'
  13. import { useFormulaStore } from '@/stores/formulaStore'
  14. import { useHomeStore } from '@/stores/homeStore'
  15. const configRef = ref()
  16. provide<(methods: Home.GrandsonMethods) => void>('registerGrandsonMethods', (methods) => {
  17. configRef.value = methods
  18. })
  19. const router = useRouter()
  20. const formulaStore = useFormulaStore()
  21. const homeStore = useHomeStore()
  22. const formulaInfo = ref()
  23. const disinfectionState = ref(homeStore.disinfectionState)
  24. const curStateRemainTime = ref(homeStore.curStateRemainTime)
  25. const disinfectFormulaVisible = ref(false)
  26. const isDeviceIdle = ref(homeStore.isDeviceIdle)
  27. const rate = ref()
  28. const log = ref()
  29. watchEffect(async () => {
  30. formulaInfo.value = formulaStore.currentSelectedFormulaInfo
  31. disinfectionState.value = homeStore.disinfectionState
  32. curStateRemainTime.value = homeStore.curStateRemainTime
  33. isDeviceIdle.value = homeStore.isDeviceIdle
  34. if (['idle', 'finished'].includes(homeStore.disinfectionState.state)) {
  35. formulaInfo.value = formulaStore.selectedFormulaInfo
  36. rate.value = formulaStore.selectedFormulaInfo?.injection_pump_speed
  37. log.value = formulaStore.selectedFormulaInfo?.loglevel
  38. }
  39. else {
  40. const realForm = (await formulaStore.getRealtimeConfig()).rely
  41. rate.value = homeStore.realRate || realForm?.injection_pump_speed
  42. log.value = homeStore.realLog || realForm?.loglevel
  43. }
  44. })
  45. const onDisinfectConfig = () => {
  46. disinfectFormulaVisible.value = true
  47. }
  48. // 结束消毒
  49. const onFinishDisinfect = async () => {
  50. await FtMessageBox.warning('请确认是否结束消毒')
  51. homeStore.setRate(undefined)
  52. homeStore.setLog(undefined)
  53. stopTimer()
  54. const loading = ElLoading.service({
  55. lock: true,
  56. text: '正在结束消毒',
  57. background: 'rgba(255, 255, 255, 0.8)',
  58. })
  59. try {
  60. const stopParams = {
  61. className: 'DisinfectionCtrlServiceExt',
  62. fnName: 'stop',
  63. params: { loglevel: formulaStore.loglevel },
  64. }
  65. await sendCmd(stopParams)
  66. const poll = setInterval(() => {
  67. if (operationState.value) {
  68. loading.close()
  69. clearInterval(poll)
  70. }
  71. }, 100)
  72. }
  73. catch (e) {
  74. console.log(e)
  75. loading.close()
  76. }
  77. }
  78. const chartRef = ref()
  79. const onSave = async () => {
  80. const formData = await chartRef.value?.saveFormData()
  81. if (!formData) {
  82. return
  83. }
  84. // 消毒中更新实时配置
  85. const res = await formulaStore.getRealtimeConfig()
  86. const diff = compareJSON(res.rely, formData)
  87. const diffKeys = Object.keys(diff)
  88. if (diffKeys.length) {
  89. await Promise.all(
  90. diffKeys.map(async (key) => {
  91. await formulaStore.setRealtimeConfig(key, diff[key].newVal)
  92. }),
  93. )
  94. FtMessage.success('设定成功')
  95. homeStore.setRate(formData.injection_pump_speed)
  96. homeStore.setLog(formData.loglevel)
  97. }
  98. }
  99. const goHome = () => {
  100. router.push('/home')
  101. }
  102. const onClose = () => {
  103. disinfectFormulaVisible.value = false
  104. }
  105. const chartList = ref<any[]>([])
  106. const operationState = computed(() => {
  107. return disinfectionState.value.state === 'idle' || disinfectionState.value.state === 'finished'
  108. })
  109. let poll: any = null
  110. const chartRefs = ref([])
  111. const getData = async (type?: string) => {
  112. const data = await syncSendCmd({
  113. className: 'H2O2SensorMgr',
  114. fnName: 'getH2O2SensorList',
  115. })
  116. const list = data.rely
  117. console.log(list)
  118. !type && (chartList.value = list.filter(item => item.isOnline).map(item => ({ ...item, data: [] })))
  119. for (let i = 0; i < list.length; i++) {
  120. const item: any = list[i]
  121. const res = await syncSendCmd({
  122. className: 'H2O2SensorMgr',
  123. fnName: 'getDisinfectionH2O2DataRecordList',
  124. params: {
  125. type: item.type,
  126. id: item.id,
  127. interval: 30,
  128. },
  129. })
  130. item.data = res.rely
  131. }
  132. console.log(list)
  133. chartList.value = list.filter(item => item.isOnline)
  134. console.log(chartList.value)
  135. }
  136. const chartLoading = ref(false)
  137. onMounted(async () => {
  138. chartLoading.value = true
  139. await getData()
  140. chartLoading.value = false
  141. poll = setInterval(() => {
  142. if (operationState.value) {
  143. clearInterval(poll)
  144. return
  145. }
  146. getData('interval')
  147. }, 1000 * 30)
  148. })
  149. onUnmounted(() => {
  150. clearInterval(poll)
  151. })
  152. const formatSeconds = (seconds: number) => {
  153. if (seconds === -1) {
  154. seconds = 0
  155. }
  156. const hours = Math.floor(seconds / 3600)
  157. const minutes = Math.floor((seconds % 3600) / 60)
  158. const remainingSeconds = seconds % 60
  159. // 补零函数
  160. const padZero = (num: number) => num.toString().padStart(2, '0')
  161. return `${padZero(hours)}:${padZero(minutes)}:${padZero(remainingSeconds)}`
  162. }
  163. </script>
  164. <template>
  165. <main class="main-content">
  166. <el-descriptions :column="4">
  167. <!-- <el-descriptions-item label="设定注射速率"> -->
  168. <!-- <el-tag> -->
  169. <!-- <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ rate }}</span>g/min -->
  170. <!-- </el-tag> -->
  171. <!-- </el-descriptions-item> -->
  172. <el-descriptions-item label="实时注射速率">
  173. <el-tag>
  174. <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{
  175. homeStore.disinfectionState.injectedVelocity
  176. }}</span>g/min
  177. </el-tag>
  178. </el-descriptions-item>
  179. <el-descriptions-item label="目标消毒等级">
  180. <el-tag>
  181. <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{ log }}</span>Log
  182. </el-tag>
  183. </el-descriptions-item>
  184. <el-descriptions-item label="实时消毒等级">
  185. <el-tag>
  186. <span style="color: #31cb7a; font-size: 18px; margin: 0 5px">{{
  187. parseInt(homeStore.disinfectionState.nlog?.toString())
  188. }}</span>Log
  189. </el-tag>
  190. </el-descriptions-item>
  191. </el-descriptions>
  192. <div
  193. v-loading="chartLoading"
  194. element-loading-background="rgba(255, 255, 255, 0.1)"
  195. class="line-chart-content"
  196. :style="{ 'grid-template-columns': `repeat(${chartList.length},1fr)` }"
  197. >
  198. <LineChart
  199. v-for="(item, index) in chartList"
  200. ref="chartRefs"
  201. :key="index"
  202. class="chart-box"
  203. :style="{ width: `calc(100% / ${chartList.length} - 10px)` }"
  204. :env-data="item"
  205. />
  206. </div>
  207. <div class="line-chart-bottom">
  208. <div class="home-chart-time">
  209. <div v-if="!homeStore.isDeviceIdle" class="home-remain-time">
  210. <div class="home-chart-label">
  211. <span v-if="disinfectionState.state === 'disinfection'"> 预计剩余时间: </span>
  212. <span v-else> 消毒状态 </span>
  213. </div>
  214. <div v-if="disinfectionState.state === 'disinfection'" class="home-chart-value">
  215. {{ formatSeconds(homeStore.disinfectionState.curStateRemainTimeS) }}
  216. </div>
  217. <div v-else class="home-chart-value">
  218. {{ deviceStateMap[disinfectionState.state] }}
  219. </div>
  220. </div>
  221. </div>
  222. <div class="home-chart-btn">
  223. <el-button v-if="!isDeviceIdle" type="danger" @click="onFinishDisinfect">
  224. <template #icon>
  225. <img :src="homeFinish" alt="" style="height: 20px">
  226. </template>
  227. 结束消毒
  228. </el-button>
  229. <el-button v-if="!isDeviceIdle" type="primary" @click="onDisinfectConfig">
  230. 运行参数
  231. </el-button>
  232. <el-button @click="goHome">
  233. 返回
  234. </el-button>
  235. </div>
  236. </div>
  237. <ft-dialog v-model="disinfectFormulaVisible" title="运行参数" width="80vw">
  238. <div class="formula-config">
  239. <RunFormulaConfig ref="chartRef" />
  240. </div>
  241. <template #footer>
  242. <div class="config-btn">
  243. <el-button @click="onClose">
  244. 关闭
  245. </el-button>
  246. <el-button type="primary" @click="onSave">
  247. 确认
  248. </el-button>
  249. </div>
  250. </template>
  251. </ft-dialog>
  252. </main>
  253. </template>
  254. <style lang="scss" scoped>
  255. .main-content {
  256. overflow: hidden;
  257. background: rgba(147, 203, 255, 0.1);
  258. height: 100%;
  259. border-radius: 10px;
  260. box-shadow: 0 1px 5px 0 rgba(9, 39, 62, 0.15);
  261. padding: 10px;
  262. .formula-config {
  263. display: grid;
  264. padding: 10px;
  265. width: 100%;
  266. }
  267. .line-chart-formula {
  268. width: 40vw;
  269. //background: #E0F0FF;
  270. height: 3.5rem;
  271. display: flex;
  272. justify-content: center;
  273. align-items: center;
  274. //border: 1px solid #E0F0FF;
  275. border-radius: 10px;
  276. margin-left: 2rem;
  277. }
  278. .line-chart-set {
  279. display: flex;
  280. justify-content: end;
  281. align-items: center;
  282. width: 65vw;
  283. padding-right: 20px;
  284. }
  285. .line-chart-title {
  286. height: 15%;
  287. display: flex;
  288. align-items: center;
  289. }
  290. .line-chart-content {
  291. display: flex;
  292. flex-direction: row; // 子元素水平排列(默认值)
  293. align-items: flex-start; // 子元素顶部对齐
  294. overflow-x: auto; // 横向滚动支持
  295. width: 100%;
  296. height: calc(100% - 80px);
  297. gap: 10px;
  298. .chart-box {
  299. flex: 0 0 auto;
  300. width: 100%;
  301. height: 100%;
  302. min-width: 200px;
  303. }
  304. }
  305. .line-chart-bottom {
  306. height: 40px;
  307. display: flex;
  308. padding-right: 20px;
  309. align-items: center;
  310. .home-chart-btn {
  311. display: flex;
  312. justify-content: end;
  313. width: 65%;
  314. }
  315. .home-chart-time {
  316. width: 35%;
  317. .home-remain-time {
  318. background: #fff;
  319. margin: 10px;
  320. height: 40px;
  321. border-radius: 12px;
  322. display: flex;
  323. align-items: center;
  324. justify-content: center;
  325. font-size: 20px;
  326. gap: 10px;
  327. .home-remaini-value {
  328. color: #2892f3;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. :deep(.el-descriptions) {
  335. width: 100%;
  336. height: 40px;
  337. .el-descriptions__body {
  338. background: rgba(0, 0, 0, 0);
  339. .el-descriptions__table {
  340. tr {
  341. display: flex;
  342. justify-content: center;
  343. gap: 20px;
  344. }
  345. }
  346. }
  347. }
  348. :deep(.el-descriptions__cell) {
  349. display: flex;
  350. align-items: center;
  351. //background: #fff;
  352. //margin-bottom: 5px;
  353. //padding: 5px !important;
  354. //.el-descriptions__label {
  355. // display: inline-block;
  356. // width: 120px;
  357. // margin: 0;
  358. // font-size: 15px;
  359. // font-weight: 600;
  360. // color: #606266;
  361. //}
  362. //.el-descriptions__content {
  363. // flex: 1;
  364. // text-align: right;
  365. // display: flex;
  366. // justify-content: flex-end;
  367. //}
  368. }
  369. </style>