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

542 lines
14 KiB

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