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

538 lines
14 KiB

2 months ago
2 months ago
2 months ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
4 weeks ago
2 months ago
2 months ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
4 weeks ago
2 months ago
3 weeks ago
2 months ago
2 months ago
2 months ago
1 month ago
3 weeks ago
2 months ago
4 weeks ago
4 weeks ago
2 months ago
2 months ago
4 weeks ago
3 weeks ago
2 months ago
2 months ago
4 weeks ago
2 months ago
2 months ago
3 weeks ago
4 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
2 months ago
4 weeks ago
3 weeks ago
4 weeks ago
2 months ago
4 weeks ago
3 weeks ago
2 months ago
3 weeks ago
3 weeks ago
2 months ago
  1. <script lang="ts" setup>
  2. import { FtMessage } from '@/libs/message'
  3. import { compareJSON, convertValuesToInt, convertValuesToString } from '@/libs/utils'
  4. import { useFormulaStore } from '@/stores/formulaStore'
  5. import { sendCmd, syncSendCmd } from 'apis/system'
  6. import SelectModal from 'components/common/SelectModal/index.vue'
  7. import SoftKeyboard from 'components/common/SoftKeyboard/index.vue'
  8. import { formulaNameMap } from 'libs/constant'
  9. import { cloneDeep } from 'lodash'
  10. import { inject, onMounted, ref, watch, watchEffect } from 'vue'
  11. /**
  12. * 配方表单组件 - 用于配置和管理各种配方参数
  13. * 支持三种模式主页模式(home)设置模式(setting)和配方管理模式(formula)
  14. * @component
  15. * @props {string} type - 组件使用模式可选值'home' | 'setting' | 'formula'
  16. */
  17. const props = defineProps<{
  18. type: string
  19. }>()
  20. const nameLength = 10
  21. const formulaStore = useFormulaStore()
  22. const targetInputRef = ref<HTMLInputElement | null>(null)
  23. const isFlip = ref(true)
  24. /**
  25. * 当前表单数据
  26. * 不同模式下有不同的初始值
  27. * - home: 当前选中的配方
  28. * - setting: 默认配方信息
  29. * - formula: 当前选中的配方或默认配方
  30. */
  31. const formData = ref<Record<string, any>>({
  32. ...formulaStore.defaultFormulaInfo,
  33. })
  34. /**
  35. * 软键盘当前输入值
  36. */
  37. const inputValue = ref<string>('')
  38. /**
  39. * 软键盘是否可见
  40. */
  41. const keyboardVisible = ref(false)
  42. /**
  43. * 软键盘类型'text' 'number'
  44. */
  45. const keyboardType = ref<'text' | 'number'>('number')
  46. /**
  47. * 软键盘组件引用
  48. */
  49. const softKeyboardRef = ref()
  50. /**
  51. * 当前聚焦的输入字段名称
  52. */
  53. const focusedInput = ref<string | null>(null)
  54. /**
  55. * 注册孙子组件方法的注入函数
  56. */
  57. const registerGrandsonMethods = inject<(methods: any) => void>('registerGrandsonMethods', () => {})
  58. /**
  59. * 配方配置列表
  60. */
  61. const formulaConfigList = ref(formulaStore.formulaConfigList)
  62. /**
  63. * 日志级别选项列表
  64. */
  65. const options = ref(formulaStore.logLevelOptions)
  66. /**
  67. * 标签单位映射表用于显示各参数的单位
  68. */
  69. const labelUnitMap: Record<string, any> = {
  70. injection_pump_speed: 'g/min',
  71. continued_gs: 'ppm',
  72. stoped_gs: 'ppm',
  73. continued_humi: '%RH',
  74. stoped_humi: '%RH',
  75. continued_satur: '%RS',
  76. stoped_satur: '%RS',
  77. loglevel: 'Log',
  78. }
  79. const currentFormulaItem = ref()
  80. /**
  81. * 组件挂载时注册方法供父组件调用
  82. */
  83. onMounted(() => {
  84. registerGrandsonMethods && registerGrandsonMethods({ getFormData })
  85. })
  86. /**
  87. * 模态框是否打开
  88. */
  89. const isModalOpen = ref(false)
  90. /**
  91. * 打开模态框
  92. */
  93. const openModal = () => {
  94. isModalOpen.value = true
  95. }
  96. /**
  97. * 监听配方配置列表和表单数据变化
  98. * 根据不同的type属性值初始化表单数据
  99. */
  100. watchEffect(() => {
  101. formulaConfigList.value = formulaStore.formulaConfigList
  102. if (props.type === 'home') {
  103. formData.value = cloneDeep(formulaStore.selectedFormulaInfo) || cloneDeep(formulaStore.defaultFormulaInfo)
  104. }
  105. else if (props.type === 'setting') {
  106. formData.value = cloneDeep(formulaStore.defaultFormulaInfo)
  107. }
  108. else {
  109. formData.value = cloneDeep(formulaStore.currentSelectedFormulaInfo) || cloneDeep(formulaStore.defaultFormulaInfo)
  110. }
  111. isFlip.value = formulaStore.flip
  112. // 后端给的数据类型是字符串型,前端需要的是int型,后端开发说(赵贺)后端不好转换,由前端进行转换.
  113. formData.value = convertValuesToInt(formData.value)
  114. })
  115. /**
  116. * 监听软键盘输入值变化更新表单数据
  117. * @param {string | number} newVal - 新的输入值
  118. */
  119. watch(inputValue, (newVal: string | number) => {
  120. console.log('inputValue-----------', inputValue)
  121. if (focusedInput.value) {
  122. if (focusedInput.value !== 'name') {
  123. newVal = Number(newVal)
  124. if (currentFormulaItem.value && newVal > currentFormulaItem.value.val_upper_limit) {
  125. newVal = currentFormulaItem.value.val_upper_limit
  126. }
  127. formData.value[focusedInput.value] = newVal
  128. }
  129. else {
  130. if (formData.value.name && formData.value.name.length > nameLength) {
  131. inputValue.value = formData.value.name
  132. }
  133. }
  134. }
  135. })
  136. // watch(formData, (newVal) => {
  137. // if (newVal && newVal.length > nameLength) {
  138. // formData.value.name = newVal.name.slice(0, nameLength)
  139. // }
  140. // }, { deep: true })
  141. /**
  142. * 获取当前表单数据将值转换为字符串格式
  143. * @returns {Record<string, string>} 转换后的表单数据
  144. */
  145. const getFormData = () => {
  146. return convertValuesToString(formData.value, 'name')
  147. }
  148. /**
  149. * 监听表单数据变化同步更新软键盘输入值
  150. * @param {Record<string, any>} newValue - 新的表单数据
  151. */
  152. watch(formData, (newValue) => {
  153. if (focusedInput.value) {
  154. inputValue.value = newValue[focusedInput.value].toString()
  155. }
  156. }, { deep: true })
  157. /**
  158. * 处理表单提交
  159. * 根据不同的type属性值执行不同的保存逻辑
  160. */
  161. const handleSubmit = () => {
  162. if (props.type !== 'setting' && !formData.value.name) {
  163. FtMessage.warning('请输入配方名称')
  164. return
  165. }
  166. // 配方管理的【确定】
  167. if (props.type === 'formula') {
  168. onSaveFormula()
  169. }
  170. // 设置中的【确定】
  171. if (props.type === 'setting') {
  172. onSaveSetting()
  173. }
  174. }
  175. /**
  176. * 保存配方
  177. * 根据是否有formula_id决定是添加新配方还是编辑已有配方
  178. */
  179. const onSaveFormula = () => {
  180. if (formData.value.formula_id) {
  181. const formulaForm: Record<string, any> = convertValuesToString(formData.value, 'name')
  182. onEditFormula(formulaForm.formula_id, formulaForm as Formula.FormulaItem)
  183. }
  184. else {
  185. onAddFormula()
  186. }
  187. }
  188. /**
  189. * 保存设置
  190. * 比较当前表单数据与默认配方数据的差异只更新有变化的字段
  191. */
  192. const onSaveSetting = async () => {
  193. // 修改默认值
  194. const diff = compareJSON(formulaStore.defaultFormulaInfo, formData.value)
  195. const diffKeys = Object.keys(diff)
  196. if (diffKeys.length) {
  197. await Promise.all(diffKeys.map(async (key) => {
  198. await setSettingFormulaConfig(key, diff[key].newVal)
  199. }))
  200. FtMessage.success('配方修改成功')
  201. }
  202. }
  203. /**
  204. * 设置配方配置项值
  205. * @param {string} settingName - 设置名称
  206. * @param {string} settingVal - 设置值
  207. * @returns {Promise<void>}
  208. */
  209. const setSettingFormulaConfig = async (settingName: string, settingVal: string) => {
  210. await sendCmd({
  211. className: 'SettingMgrService',
  212. fnName: 'setSettingVal',
  213. params: {
  214. settingName,
  215. settingVal: settingVal.toString(),
  216. },
  217. })
  218. formulaStore.getFormualDefaultData()
  219. }
  220. /**
  221. * 添加新配方
  222. * 先调用API创建新配方然后更新配方数据
  223. */
  224. const onAddFormula = () => {
  225. const params = {
  226. className: 'SettingMgrService',
  227. fnName: 'addNewFormula',
  228. }
  229. syncSendCmd(params).then((res) => {
  230. if (res.ackcode === 0) {
  231. const item = res.rely
  232. const formulaForm: Record<string, any> = convertValuesToString(formData.value, 'name')
  233. // formulaForm.name = item.name
  234. formulaForm.formula_id = item.formula_id
  235. onEditFormula(item.formula_id, formulaForm as Formula.FormulaItem)
  236. }
  237. })
  238. }
  239. /**
  240. * 编辑配方
  241. * @param {string} formula_id - 配方ID
  242. * @param {Formula.FormulaItem} formulaForm - 配方表单数据
  243. */
  244. const onEditFormula = (formula_id: string, formulaForm: Formula.FormulaItem) => {
  245. const editParams = {
  246. className: 'SettingMgrService',
  247. fnName: 'updateFormula',
  248. params: {
  249. formula_id,
  250. formula: cloneDeep(formulaForm),
  251. },
  252. }
  253. syncSendCmd(editParams).then(() => {
  254. FtMessage.success('操作成功')
  255. formulaStore.initFormulaList()
  256. // formData.value = formulaForm
  257. formulaStore.updateSelectedFormulaData(formulaForm)
  258. })
  259. }
  260. /**
  261. * 打开软键盘
  262. * @param {Event} e - 事件对象
  263. * @param item
  264. */
  265. const openKeyboard = (e: any, item: Formula.FormulaItem) => {
  266. setTimeout(() => {
  267. keyboardVisible.value = true
  268. const labelName: string = e.target.name
  269. openKeyboardType(labelName)
  270. const formValue = formData.value[labelName]
  271. inputValue.value = formValue.toString()
  272. focusedInput.value = e.target.name
  273. currentFormulaItem.value = item
  274. const inputDom = e.target as HTMLInputElement
  275. targetInputRef.value = inputDom
  276. }, 100)
  277. }
  278. /**
  279. * 取消操作重置配方数据
  280. */
  281. const handleCancel = () => {
  282. formulaStore.initFormulaData()
  283. }
  284. /**
  285. * 恢复默认设置
  286. */
  287. const handleResetDefault = async () => {
  288. await sendCmd({
  289. className: 'SettingMgrService',
  290. fnName: 'factoryResetSettings',
  291. })
  292. await formulaStore.getFormualDefaultData()
  293. }
  294. /**
  295. * 确认输入值
  296. * @param {string} value - 输入值
  297. */
  298. const handleConfirm = (value: string) => {
  299. console.log('确认输入:', value)
  300. }
  301. /**
  302. * 确认日志级别选择
  303. * @param {any} value - 选择的值
  304. */
  305. const handleLogConfirm = (value: any) => {
  306. isModalOpen.value = false
  307. formData.value.loglevel = value
  308. formulaStore.loglevel = value
  309. }
  310. /**
  311. * 取消日志级别选择
  312. */
  313. const handleLogCancel = () => {
  314. isModalOpen.value = false
  315. }
  316. /**
  317. * 根据标签名称确定软键盘类型
  318. * @param {string} labelName - 标签名称
  319. */
  320. const openKeyboardType = (labelName: string) => {
  321. keyboardType.value = labelName === 'name' ? 'text' : 'number'
  322. }
  323. </script>
  324. <template>
  325. <transition name="slide-right">
  326. <div v-if="isFlip" class="formula-form">
  327. <el-form :model="formData" label-width="auto" label-position="right" class="formulaFormItem" inline>
  328. <el-form-item v-if="type !== 'setting'" label="配方名称" style="margin-top:20px">
  329. <el-input
  330. v-model="formData.name"
  331. v-prevent-keyboard
  332. name="name"
  333. placeholder="配方名称"
  334. :disabled="type === 'home'"
  335. class="formdata-input-home"
  336. @focus="openKeyboard"
  337. />
  338. </el-form-item>
  339. <el-form-item
  340. v-for="(item) in formulaConfigList"
  341. :key="item.setting_id"
  342. :label="formulaNameMap[item.setting_id]"
  343. style="margin-top:20px"
  344. >
  345. <template v-if="item.val_type === 'int'">
  346. <el-input
  347. v-model.number="formData[item.setting_id]"
  348. v-prevent-keyboard
  349. type="number"
  350. :name="item.setting_id"
  351. :controls="false"
  352. class="formdata-input-home"
  353. :disabled="!item.is_visible_in_setting_page"
  354. @focus="(e) => openKeyboard(e, item)"
  355. >
  356. <template v-if="labelUnitMap[item.setting_id]" #append>{{ labelUnitMap[item.setting_id] }}</template>
  357. </el-input>
  358. </template>
  359. <template v-else-if="item.val_type === 'enum'">
  360. <el-input
  361. v-model="formData[item.setting_id]"
  362. v-prevent-keyboard
  363. placeholder="请选择"
  364. class="formdata-input-home"
  365. readonly
  366. @focus="openModal"
  367. >
  368. <template #append>{{ labelUnitMap[item.setting_id] }}</template>
  369. </el-input>
  370. </template>
  371. <template v-else-if="item.val_type === 'boolean'">
  372. <el-radio-group
  373. v-model="formData[item.setting_id]"
  374. class="formdata-input-home"
  375. :disabled="!item.is_visible_in_setting_page"
  376. >
  377. <el-radio :label="true">
  378. </el-radio>
  379. <el-radio :label="false">
  380. </el-radio>
  381. </el-radio-group>
  382. </template>
  383. </el-form-item>
  384. </el-form>
  385. <div v-if="type !== 'home'" class="formula-form-btn" :style="{ marginLeft: '33%' }">
  386. <slot name="formulaBtn">
  387. <div class="default-btn">
  388. <el-button type="primary" class="config-btn" @click="handleSubmit">
  389. 确定
  390. </el-button>
  391. <el-button v-if="type === 'setting'" class="config-btn" @click="handleResetDefault">
  392. 恢复默认值
  393. </el-button>
  394. <el-button v-else class="config-btn" @click="handleCancel">
  395. 取消
  396. </el-button>
  397. </div>
  398. </slot>
  399. </div>
  400. <Teleport to="body">
  401. <SoftKeyboard
  402. ref="softKeyboardRef"
  403. v-model="inputValue"
  404. :is-visible="keyboardVisible"
  405. :keyboard-type="keyboardType"
  406. :target-input="targetInputRef"
  407. @confirm="handleConfirm"
  408. @update-keyboard-visible="(visible) => keyboardVisible = visible"
  409. @close="keyboardVisible = false"
  410. />
  411. </Teleport>
  412. <SelectModal
  413. v-if="isModalOpen"
  414. :options="options"
  415. :selected-value="formData.loglevel"
  416. placeholder="请选择"
  417. @confirm="handleLogConfirm"
  418. @cancel="handleLogCancel"
  419. />
  420. </div>
  421. </transition>
  422. </template>
  423. <style lang="scss" scoped>
  424. .formula-form{
  425. font-size: 20px !important;
  426. padding: 5px;
  427. padding-left: 15px;
  428. height: 81vh;
  429. overflow: auto;
  430. .formulaFormItem{
  431. display: grid;
  432. grid-template-columns: 1fr 1fr;
  433. }
  434. .formula-form-btn{
  435. }
  436. .default-btn{
  437. margin-top: 1rem;
  438. }
  439. .config-btn{
  440. height: 3rem;
  441. width: 8rem;
  442. }
  443. }
  444. .formdata-input-home{
  445. width: 15vw;
  446. }
  447. .formula-form-item{
  448. display: grid;
  449. grid-template-columns: 1fr 1fr;
  450. }
  451. .formData-input-config{
  452. width: 10vw;
  453. }
  454. ::v-deep .el-input__inner{
  455. text-align: left;
  456. }
  457. ::v-deep .el-form-item{
  458. margin-right: 0;
  459. }
  460. ::v-deep .el-input__inner{
  461. height: 45px;
  462. }
  463. ::v-deep .el-form-item{
  464. align-items: center;
  465. }
  466. /* 进入动画的初始状态 */
  467. .slide-right-enter-from {
  468. transform: translateX(100%);
  469. }
  470. /* 进入动画的结束状态(也可以理解为激活状态) */
  471. .slide-right-enter-to {
  472. transform: translateX(0);
  473. }
  474. /* 进入动画的过渡曲线等 */
  475. .slide-right-enter-active {
  476. transition: transform 0.3s ease-in-out;
  477. }
  478. /* 离开动画的初始状态(激活状态) */
  479. .slide-right-leave-from {
  480. transform: translateX(0);
  481. }
  482. /* 离开动画的结束状态 */
  483. .slide-right-leave-to {
  484. transform: translateX(100%);
  485. }
  486. /* 离开动画的过渡曲线等 */
  487. .slide-right-leave-active {
  488. transition: transform 0.3s ease-in-out;
  489. }
  490. </style>