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

341 lines
9.3 KiB

2 months ago
1 month ago
4 weeks ago
2 months 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
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
4 weeks ago
2 months ago
2 months ago
1 month ago
1 month ago
4 weeks ago
  1. <script lang="ts" setup>
  2. import { FtMessage } from '@/libs/message'
  3. import { useFormulaStore } from '@/stores/formulaStore'
  4. import { useHomeStore } from '@/stores/homeStore'
  5. import { sendCmd, syncSendCmd } from 'apis/system'
  6. // import homeChart from 'assets/images/home/home-chart.svg'
  7. import homeRunSvg from 'assets/images/home/home-run.svg'
  8. import homeSettingSvg from 'assets/images/home/home-setting.svg'
  9. import BtButton from 'components/common/BTButton/index.vue'
  10. import CascadingSelectModal from 'components/common/CascadingSelectModal/index.vue'
  11. import Config from 'components/home/Config.vue'
  12. import { compareJSON } from 'libs/utils'
  13. import { cloneDeep } from 'lodash'
  14. import { computed, onMounted, provide, ref, watchEffect } from 'vue'
  15. import { useRouter } from 'vue-router'
  16. /**
  17. * 主页操作控制组件
  18. * @description 负责处理压力控制消毒设置图表导航等功能协调组件间通信
  19. */
  20. // 依赖注入
  21. const configRef = ref()
  22. provide<(methods: Home.GrandsonMethods) => void>('registerGrandsonMethods', (methods) => {
  23. configRef.value = methods
  24. })
  25. // 状态管理
  26. const router = useRouter()
  27. const formulaStore = useFormulaStore()
  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. isModalOpen.value = true
  167. }
  168. })
  169. }
  170. /**
  171. * @function 确认压力选择
  172. * @param {string|number[]} value - 选中的压力配置值[类型, 强度]
  173. * @desc 关闭模态框并更新压力配置
  174. */
  175. const handleConfirm = (value: string | number[]) => {
  176. isModalOpen.value = false
  177. homeStore.updatePressure(value)
  178. }
  179. /**
  180. * @function 取消压力选择
  181. * @desc 关闭模态框
  182. */
  183. const handleCancel = () => {
  184. isModalOpen.value = false
  185. }
  186. /**
  187. * @function 关闭消毒设置对话框
  188. * @desc 重置对话框状态
  189. */
  190. const onClose = () => {
  191. disinfectFormulaVisible.value = false
  192. }
  193. </script>
  194. <template>
  195. <div class="home-start-opt">
  196. <div class="home-opt-flex">
  197. <div>
  198. <BtButton
  199. button-text="压力控制"
  200. text-size="1.3rem"
  201. border-radius="5px"
  202. width="7.5rem"
  203. text-color="#1989fa"
  204. height="3rem"
  205. @click="onSetPressure"
  206. >
  207. <template #icon>
  208. <el-icon><Sort /></el-icon>
  209. </template>
  210. </BtButton>
  211. </div>
  212. <div class="home-opt-ml">
  213. <BtButton
  214. button-text="查看图表"
  215. text-size="1.3rem"
  216. border-radius="5px"
  217. width="7.5rem"
  218. height="3rem"
  219. text-color="#1989fa"
  220. :disabled="deviceState"
  221. @click="onShowChart"
  222. >
  223. <template #icon>
  224. <el-icon><Picture /></el-icon>
  225. </template>
  226. </BtButton>
  227. </div>
  228. <div class="home-opt-ml">
  229. <BtButton
  230. v-if="deviceState"
  231. button-text="消毒设置"
  232. text-size="1.3rem"
  233. border-radius="5px"
  234. width="7.5rem"
  235. text-color="#1989fa"
  236. height="3rem"
  237. @click="onDisinfectConfig"
  238. >
  239. <template #icon>
  240. <img :src="homeSettingSvg" width="12" style="margin-right: 5px" alt="">
  241. </template>
  242. </BtButton>
  243. <BtButton
  244. v-else
  245. button-text="运行参数"
  246. text-size="1rem"
  247. border-radius="5px"
  248. width="7rem"
  249. text-color="#1989fa"
  250. height="3rem"
  251. @click="onDisinfectConfig"
  252. >
  253. <template #icon>
  254. <img :src="homeRunSvg" width="15" alt="">
  255. </template>
  256. </BtButton>
  257. </div>
  258. </div>
  259. </div>
  260. <ft-dialog v-model="disinfectFormulaVisible" width="80vw" style="height: 95vh">
  261. <div>
  262. <Config ref="configRef" />
  263. </div>
  264. <template #footer>
  265. <div class="config-btn">
  266. <BtButton
  267. bgColor="#1989fa"
  268. button-text="确认"
  269. text-size="1rem"
  270. border-radius="5px"
  271. width="7rem"
  272. textSize="1.5rem"
  273. text-color="#ffffff"
  274. height="3rem"
  275. @click="onSave"
  276. />
  277. <BtButton
  278. button-text="取消"
  279. text-size="1rem"
  280. border-radius="5px"
  281. width="7rem"
  282. textSize="1.5rem"
  283. text-color="#1989fa"
  284. height="3rem"
  285. @click="onClose"
  286. />
  287. </div>
  288. </template>
  289. </ft-dialog>
  290. <CascadingSelectModal
  291. v-if="isModalOpen"
  292. :options-left="optionsLeft"
  293. :options="optionsRight"
  294. :selected-value="selectedValue"
  295. :default-value="defaultIntensityValue"
  296. placeholder="请选择"
  297. @confirm="handleConfirm"
  298. @cancel="handleCancel"
  299. />
  300. </template>
  301. <style lang="scss" scoped>
  302. .home-start-opt{
  303. position: absolute;
  304. bottom: 0;
  305. margin: 0.5rem;
  306. gap: 5px;
  307. .home-opt-flex{
  308. display: grid;
  309. grid-template-columns: 1fr 1fr 1fr;
  310. .home-opt-ml{
  311. margin-left: 5px;
  312. }
  313. }
  314. }
  315. .config-btn{
  316. margin-top: -3rem
  317. }
  318. </style>