消毒机设备
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.

353 lines
9.8 KiB

1 month ago
3 weeks ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
2 months ago
3 weeks ago
2 months ago
2 months ago
2 months ago
2 months ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
1 month ago
1 month ago
3 weeks ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
2 months ago
2 months ago
1 month ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
  1. <script lang="ts" setup>
  2. import { sendCmd, syncSendCmd } from 'apis/system'
  3. // import homeChart from 'assets/images/home/home-chart.svg'
  4. import BtButton from 'components/common/BTButton/index.vue'
  5. import CascadingSelectModal from 'components/common/CascadingSelectModal/index.vue'
  6. import Config from 'components/home/config.vue'
  7. import { getDeviceStatus } from 'libs/deviceComm'
  8. import { FtMessageBox } from 'libs/messageBox'
  9. import { compareJSON } from 'libs/utils'
  10. import { cloneDeep } from 'lodash'
  11. import { computed, onMounted, provide, ref, watchEffect } from 'vue'
  12. import { useRouter } from 'vue-router'
  13. import { FtMessage } from '@/libs/message'
  14. import { useDeviceStore } from '@/stores/deviceStore'
  15. import { useFormulaStore } from '@/stores/formulaStore'
  16. import { useHomeStore } from '@/stores/homeStore'
  17. /**
  18. * 主页操作控制组件
  19. * @description 负责处理压力控制消毒设置图表导航等功能协调组件间通信
  20. */
  21. // 依赖注入
  22. const configRef = ref()
  23. provide<(methods: Home.GrandsonMethods) => void>('registerGrandsonMethods', (methods) => {
  24. configRef.value = methods
  25. })
  26. // 状态管理
  27. const router = useRouter()
  28. const formulaStore = useFormulaStore()
  29. const deviceStore = useDeviceStore()
  30. const homeStore = useHomeStore()
  31. // 组件状态
  32. const isModalOpen = ref(false) // 级联选择模态框是否打开
  33. const optionsLeft = ref<System.Option[]>([]) // 压力类型选项(左列)
  34. const optionsRight = ref<System.Option[]>([]) // 压力强度选项(右列)
  35. const selectedValue = ref() // 选中的压力配置值
  36. const disinfectionState = ref(homeStore.disinfectionState) // 消毒状态
  37. const disinfectFormulaVisible = ref(false) // 消毒设置对话框是否显示
  38. const selectedByFormulas = ref(cloneDeep(formulaStore.selectedFormulaInfo)) // 当前选中的配方信息
  39. const pressureConfig = ref(homeStore.pressureConfig)
  40. const defaultIntensityValue = ref()
  41. /**
  42. * @hook 响应式依赖监听
  43. * @description 监听消毒状态和配方变化同步更新组件状态
  44. */
  45. watchEffect(() => {
  46. disinfectionState.value = homeStore.disinfectionState
  47. selectedByFormulas.value = formulaStore.selectedFormulaInfo
  48. pressureConfig.value = homeStore.pressureConfig
  49. })
  50. /**
  51. * @hook 生命周期钩子 - 组件挂载完成时执行
  52. * @description 初始化压力配置
  53. */
  54. onMounted(async () => {
  55. const deviceInfo: Device.DeviceInfo = deviceStore.deviceInfo
  56. if (deviceInfo.deviceType !== useDeviceStore().deviceTypeMap.LargeSpaceDM_B) {
  57. await getPressureConfig() // 获取压力配置
  58. }
  59. })
  60. /**
  61. * @function getPressureConfig
  62. * @desc 获取当前压力配置信息
  63. */
  64. const getPressureConfig = async () => {
  65. const pressureParams = {
  66. className: 'PipelinePressureControl',
  67. fnName: 'getConfig',
  68. }
  69. const res = await syncSendCmd(pressureParams)
  70. if (res.ackcode === 0) {
  71. homeStore.updatePressureConfig(res.rely)
  72. }
  73. }
  74. /**
  75. * @computed 计算属性 - 设备状态判断
  76. * @returns {boolean} - 设备是否处于空闲或已完成状态
  77. * @desc 控制按钮可用状态
  78. */
  79. const deviceState = computed(() => {
  80. return disinfectionState.value.state === 'idle' || disinfectionState.value.state === 'finished'
  81. })
  82. const disinfectFormulaTitle = ref<string>('消毒设置')
  83. /**
  84. * @function 打开消毒设置对话框
  85. * @desc 根据当前选中配方或默认配方初始化设置
  86. */
  87. const onDisinfectConfig = (title: string) => {
  88. formulaStore.updateSelectedFormulaDataByList(selectedByFormulas.value || cloneDeep(formulaStore.defaultFormulaInfo))
  89. disinfectFormulaVisible.value = true
  90. disinfectFormulaTitle.value = title
  91. }
  92. /**
  93. * @function 导航到图表页面
  94. * @desc 路由跳转至消毒图表页面
  95. */
  96. const onShowChart = () => {
  97. router.push('/home/chart')
  98. }
  99. /**
  100. * @function 保存消毒参数
  101. * @desc 处理表单数据保存逻辑区分消毒中与非消毒状态
  102. */
  103. const onSave = async () => {
  104. const formData = configRef.value?.getFormData()
  105. if (!formData) {
  106. return
  107. }
  108. formulaStore.updateSelectedFormulaDataByList(cloneDeep(formData)) // 更新选中配方
  109. if (!homeStore.isDeviceIdle) {
  110. // 消毒中时更新实时配置
  111. const res = await sendCmd({
  112. className: 'DisinfectionCtrlServiceExt',
  113. fnName: 'getRealtimeConfig',
  114. params: {},
  115. })
  116. const diff = compareJSON(res, formData)
  117. const diffKeys = Object.keys(diff)
  118. if (diffKeys.length) {
  119. await Promise.all(
  120. diffKeys.map(async (key) => {
  121. await setRealtimeConfig(key, diff[key].newVal)
  122. }),
  123. )
  124. FtMessage.success('配方修改成功')
  125. }
  126. }
  127. else {
  128. // 非消毒时保存配方
  129. if (formData.formula_id) {
  130. formulaStore.updateSelectedFormulaDataByList(formData)
  131. }
  132. }
  133. onClose() // 关闭对话框
  134. }
  135. console.log(onSave)
  136. /**
  137. * @function 设置实时消毒参数
  138. * @param {string} key - 参数键名
  139. * @param {string} val - 参数值
  140. * @desc 向设备发送实时参数修改指令
  141. */
  142. const setRealtimeConfig = async (key: string, val: string) => {
  143. await syncSendCmd({
  144. className: 'DisinfectionCtrlServiceExt',
  145. fnName: 'setRealtimeConfig',
  146. params: { key, val },
  147. })
  148. }
  149. /**
  150. * @function 打开压力控制模态框
  151. * @desc 初始化压力类型和强度选项
  152. */
  153. const onSetPressure = () => {
  154. // 校验设备状态
  155. const statusName = getDeviceStatus()
  156. if (statusName) {
  157. FtMessageBox.error(statusName)
  158. return
  159. }
  160. const pressureVal = pressureConfig.value
  161. const { typeDisplayNames, types, intensitys } = pressureVal || {}
  162. // 构建压力类型选项(左列)
  163. const leftOptions: System.Option[]
  164. = typeDisplayNames?.map((name: string, index: number) => ({
  165. label: name,
  166. value: types[index],
  167. })) || []
  168. // 构建压力强度选项(右列)
  169. const rightOptions: System.Option[] = []
  170. if (types?.includes('positivePressure')) {
  171. intensitys?.positivePressure?.forEach((intensity: string) => {
  172. rightOptions.push({ label: `${intensity}%`, value: intensity })
  173. })
  174. }
  175. optionsLeft.value = leftOptions
  176. optionsRight.value = rightOptions
  177. // 获取已经设置的压力值
  178. syncSendCmd({
  179. className: 'PipelinePressureControl',
  180. fnName: 'getState',
  181. }).then((res) => {
  182. if (res.ackcode === 0) {
  183. defaultIntensityValue.value = res.rely.intensity
  184. homeStore.updateDefaultIntensityValue(res.rely.intensity)
  185. homeStore.updateDefaultIntensityTypeValue(res.rely.type)
  186. isModalOpen.value = true
  187. }
  188. })
  189. }
  190. /**
  191. * @function 确认压力选择
  192. * @param {string|number[]} value - 选中的压力配置值[类型, 强度]
  193. * @desc 关闭模态框并更新压力配置
  194. */
  195. const handleConfirm = (value: string | number[]) => {
  196. isModalOpen.value = false
  197. homeStore.updatePressure(value)
  198. }
  199. /**
  200. * @function 取消压力选择
  201. * @desc 关闭模态框
  202. */
  203. const handleCancel = () => {
  204. isModalOpen.value = false
  205. }
  206. /**
  207. * @function 关闭消毒设置对话框
  208. * @desc 重置对话框状态
  209. */
  210. const onClose = () => {
  211. disinfectFormulaVisible.value = false
  212. }
  213. const settingWidth = computed(() => {
  214. if (__DEVICE_TYPE__ === deviceStore.deviceTypeMap.LargeSpaceDM_B) {
  215. return '12rem'
  216. }
  217. return '7.5rem'
  218. })
  219. </script>
  220. <template>
  221. <div class="home-start-opt">
  222. <div class="home-opt-flex">
  223. <div v-if="__DEVICE_TYPE__ === deviceStore.deviceTypeMap.PipeDM">
  224. <BtButton
  225. button-text="压力控制"
  226. text-size="1.3rem"
  227. border-radius="5px"
  228. width="7.5rem"
  229. text-color="#1989fa"
  230. height="3rem"
  231. @click="onSetPressure"
  232. />
  233. </div>
  234. <div class="home-opt-ml">
  235. <BtButton
  236. button-text="查看图表"
  237. text-size="1.3rem"
  238. border-radius="5px"
  239. :width="settingWidth"
  240. height="3rem"
  241. text-color="#1989fa"
  242. :disabled="deviceState"
  243. @click="onShowChart"
  244. />
  245. </div>
  246. <div class="home-opt-ml">
  247. <BtButton
  248. v-if="deviceState"
  249. button-text="消毒设置"
  250. text-size="1.3rem"
  251. border-radius="5px"
  252. :width="settingWidth"
  253. text-color="#1989fa"
  254. height="3rem"
  255. @click="onDisinfectConfig('消毒设置')"
  256. />
  257. <BtButton
  258. v-else
  259. button-text="运行参数"
  260. text-size="1.3rem"
  261. border-radius="5px"
  262. :width="settingWidth"
  263. text-color="#1989fa"
  264. height="3rem"
  265. @click="onDisinfectConfig('运行参数')"
  266. />
  267. </div>
  268. </div>
  269. </div>
  270. <ft-dialog v-model="disinfectFormulaVisible" :title="disinfectFormulaTitle" width="80vw">
  271. <div style="height: 80vh; overflow: auto">
  272. <Config ref="configRef" />
  273. </div>
  274. <template #footer>
  275. <div class="config-btn">
  276. <BtButton
  277. button-text="关闭"
  278. border-radius="5px"
  279. width="7rem"
  280. text-size="1.5rem"
  281. text-color="#1989fa"
  282. height="3rem"
  283. @click="onClose"
  284. />
  285. <!-- <BtButton -->
  286. <!-- bg-color="#1989fa" -->
  287. <!-- button-text="确认" -->
  288. <!-- border-radius="5px" -->
  289. <!-- width="7rem" -->
  290. <!-- text-size="1.5rem" -->
  291. <!-- text-color="#ffffff" -->
  292. <!-- height="3rem" -->
  293. <!-- @click="onSave" -->
  294. <!-- /> -->
  295. </div>
  296. </template>
  297. </ft-dialog>
  298. <CascadingSelectModal
  299. v-if="isModalOpen"
  300. :options-left="optionsLeft"
  301. :options="optionsRight"
  302. :selected-value="selectedValue"
  303. :default-value="defaultIntensityValue"
  304. placeholder="请选择"
  305. @confirm="handleConfirm"
  306. @cancel="handleCancel"
  307. />
  308. </template>
  309. <style lang="scss" scoped>
  310. .home-start-opt {
  311. margin: 0.5rem;
  312. gap: 5px;
  313. width: 100%;
  314. .home-opt-flex {
  315. display: grid;
  316. grid-template-columns: 1fr 1fr 1fr;
  317. .home-opt-ml {
  318. margin-left: 5px;
  319. }
  320. }
  321. }
  322. .config {
  323. border: 1px solid red;
  324. }
  325. .config-btn {
  326. //margin-top: -3rem
  327. }
  328. </style>