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

332 lines
9.0 KiB

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