华西海圻ELN前端工程
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.

531 lines
18 KiB

  1. import moment from 'moment'
  2. import { getLatestSn, getLatestSnArr } from '@/api/template';
  3. import { isValueEmpty } from '@/utils/index';
  4. import { isCommonUnit } from "@/utils/conTools";
  5. import { sj_subpackage, sj_startConfiguration, sj_configurationCompleted } from '@/api/business/sj/sj';
  6. import {convertConcentration} from "@/utils/conConverter";//浓度单位转换
  7. import {volumeConverter} from "@/utils/volConverter";//体积单位转换
  8. export default {
  9. dicts: [
  10. 'business_pztj',
  11. 'business_cctj',
  12. 'business_nddw',
  13. 'business_tjdw',
  14. 'business_yxqdw',
  15. 'business_rqcz',
  16. 'business_sp_jmdyzqdyp', //色谱-编号-准确度与精密度
  17. 'business_sp_nbgzy', //色谱-编号-内标工作液
  18. 'business_sp_zkgzy', //色谱-编号-质控工作液
  19. 'business_sp_bqgzy', //色谱-编号-标曲工作液
  20. 'business_sp_bzqxzkypzbb', //色谱-编号-标准曲线/质控样品制备表
  21. 'business_sp_tqhsl', //色谱-编号-提取回收率
  22. 'business_sp_qxwdx', //色谱-编号-全血稳定性
  23. 'business_sp_cbyhgzywdx', //色谱-编号-储备液和工作液稳定性
  24. 'business_sp_rxjzxy', //色谱-编号-溶血基质效应
  25. 'business_sp_jzxy', //色谱-编号-基质效应
  26. 'business_sp_xzxytyx', //色谱-编号-选择性与特异性
  27. 'business_sp_zdybs', //色谱-编号-最大样本数
  28. 'business_sp_xskkx', //色谱-编号-稀释可靠性
  29. 'business_sp_cbydb', //色谱-编号-储备液对比
  30. 'business_pcr_gzy', // PCR-编号-工作液
  31. 'business_lba_jg' // LBA006-结果
  32. ],
  33. props: {
  34. templateData: {
  35. type: Object,
  36. default: () => { }
  37. }
  38. },
  39. watch: {
  40. templateData: {
  41. immediate: true,
  42. deep: true,
  43. handler(v) {
  44. if (v) {
  45. const {
  46. studySubjectId,
  47. studyId,
  48. studyMc,
  49. studySn,
  50. templateMc,
  51. templateMcEn,
  52. templateSn,
  53. startDate,
  54. bdmc,
  55. endDate,
  56. id
  57. } = v
  58. if (v.resource) {
  59. //type 类型:1:试剂;3:给药制剂;5:麻精药;7:供试品
  60. //试验试剂信息 试剂
  61. this.resource = _.filter(JSON.parse(v.resource), function (o) {
  62. return o.type == 1 || o.elnType == 1
  63. })
  64. //试验物资信息 供试品、给药制剂、麻精药
  65. this.resourceWz = _.filter(JSON.parse(v.resource), function (o) {
  66. return o.type == 3 || o.type == 5 || o.type == 7 || o.elnType == 3|| o.elnType == 5 || o.elnType == 7
  67. })
  68. }
  69. if (v.yqResource) {
  70. //仪器信息
  71. this.yqResource = JSON.parse(v.yqResource)
  72. }
  73. this.product = v.product || null
  74. if (v.bdnr) {
  75. this.formData = {
  76. ...JSON.parse(v.bdnr),
  77. studySubjectId,
  78. studyId,
  79. studyMc,
  80. studySn,
  81. templateMc,
  82. templateMcEn,
  83. templateSn,
  84. startDate,
  85. bdmc,
  86. endDate,
  87. id
  88. }
  89. } else {
  90. this.formData = {
  91. studySubjectId,
  92. studyId,
  93. studyMc,
  94. studySn,
  95. templateMc,
  96. templateMcEn,
  97. templateSn,
  98. startDate,
  99. bdmc,
  100. endDate,
  101. id
  102. }
  103. }
  104. const { effectivePeriod, effectivePeriodUnit, expireDate } =
  105. this.formData
  106. //实际填报的时候,如果有了开始时间,并且有有效周期,但是没有失效日,就计算失效日为开始时间+有效周期
  107. if (
  108. startDate &&
  109. this.fillType === 'actFill' &&
  110. effectivePeriod &&
  111. effectivePeriodUnit &&
  112. !expireDate
  113. ) {
  114. const start = moment(startDate)
  115. const unit = effectivePeriodUnit === '天' ? 'days' : 'hours'
  116. const end = start
  117. .add(Number(effectivePeriod), unit)
  118. .format('YYYY-MM-DD HH:mm:ss')
  119. this.formData = { ...this.formData, expireDate: end }
  120. }
  121. console.log(this.formData, 'formData from templateData')
  122. this.setTemplateData(v)
  123. }
  124. }
  125. }
  126. },
  127. data() {
  128. return {
  129. formData: {},
  130. templateDetail: {},
  131. product: null,//产物:1:试剂;3:给药制剂;5:麻精药
  132. resource: [], //试验试剂信息
  133. resourceWz: [], //物资信息
  134. yqResource: [], //仪器信息
  135. jcbList: [], //检测板信息
  136. resourceTmp: [],//试验试剂信息提交用
  137. yqResourceTmp: [], //仪器信息提交用
  138. jcbListTmp: [], //检测板信息提交用
  139. sysjColumns: [
  140. { label: 'template.common.reagentName', prop: "mc" },//名称
  141. { label: 'template.common.reagentCode', prop: "bh" },//编号
  142. { label: 'template.common.reagentNo', prop: "ph" },//批号 试剂,供试品才有
  143. { label: 'template.common.concentration', prop: "ndz" },//浓度
  144. { label: 'template.common.source', prop: "ly" },//来源
  145. { label: 'template.common.reagentExpireDate', prop: "sxrq" },//失效日期
  146. ],
  147. sywzColumns: [
  148. { label: 'template.common.wzName', prop: "mc" },//名称
  149. { label: 'template.common.wzCode', prop: "bh" },//编号
  150. { label: 'template.common.wzSource', prop: "ly" },//来源
  151. { label: 'template.common.wzConcentration', prop: "ndz" },//浓度
  152. { label: 'template.common.wzExpireDate', prop: "sxrq" },//失效日期
  153. ],
  154. yqColumns: [
  155. { label: 'template.common.instrumentName', prop: "mc" },//仪器名称
  156. { label: 'template.common.instrumentModel', prop: "xh" },//仪器型号
  157. { label: 'template.common.instrumentCode', prop: "bh" },//仪器编号
  158. { label: 'template.common.nextTestDate', prop: "jzrq" },//下次测试/校准/检定日期
  159. ],
  160. }
  161. },
  162. mounted() { },
  163. unmounted() {
  164. this.setTemplateData({})
  165. },
  166. methods: {
  167. //试剂弹窗确认前
  168. onBeforeReagentSubmit(data){
  169. const {selectData, callback, key, formFields} = data;
  170. const {subTargetStartSolution} = formFields;
  171. //判断选择的浓度单位和预制的浓度单位是否一致
  172. if(key === "subStartSolution" && !isCommonUnit(selectData.row.nddw,[subTargetStartSolution])){//选择起始源溶液需要同步更新table的变更记录。
  173. callback.prevent('浓度单位与预制不符,请重新选择或申请废止并重新预制。');
  174. }
  175. },
  176. getFormDataByTemplateData() {
  177. let o = {};
  178. const { bdnr } = this.templateData
  179. if (bdnr) {
  180. o = JSON.parse(bdnr)
  181. }
  182. return o;
  183. },
  184. //开始配置
  185. //postData: {bh: '123456'}
  186. async startConfigRequest(postData) {
  187. const res = await sj_startConfiguration(postData)
  188. if (res.code === 200) {
  189. this.$message.success('开始配置成功')
  190. } else {
  191. this.$message.error('开始配置失败')
  192. }
  193. },
  194. //完成配置
  195. async configCompleteRequest(postData) {
  196. const res = await sj_configurationCompleted(postData)
  197. if (res.code === 200) {
  198. this.$message.success('完成配置成功')
  199. } else {
  200. this.$message.error('完成配置失败')
  201. }
  202. },
  203. //分装
  204. async subPackageRequest(postData) {
  205. const res = await sj_subpackage(postData)
  206. if (res.code === 200) {
  207. this.$message.success('分装成功')
  208. } else {
  209. this.$message.error('分装失败')
  210. }
  211. },
  212. //获取打印配置
  213. getBasePrintConfig(data = {}) {
  214. const { printCode, type, row = {} } = data;
  215. const { stepStorageCondition, startDate, expireDate } = this.formData;;
  216. const { nickName, name } = this.$store.getters;
  217. const lang = this.$i18n.locale;
  218. const printConfig = {
  219. "品名": "暂时还不知道品名是哪个字段",
  220. "存储条件": stepStorageCondition,
  221. "配制日期": moment(startDate).format("YYYY-MM-DD"),
  222. "有效期至": moment(expireDate).format("YYYY-MM-DD HH:mm"),
  223. "配置者": lang === "zh_CN" ? nickName : name,
  224. //type==="subPackage"从分装打印过来的,
  225. //type==="row" 从列表上点过来的
  226. "编号": type === "subPackage" ? printCode : (row.targetSolutionCode + row.subTargetSolutionCode),
  227. }
  228. return printConfig
  229. },
  230. async getLatestSn(count = 1) {
  231. const res = await getLatestSn({ count })
  232. if (res.code === 200) {
  233. return res.data
  234. }
  235. return []
  236. },
  237. //获取最新的多个编号
  238. async getLatestSnArr(params) {
  239. const res = await getLatestSnArr(params)
  240. if (res.code === 200) {
  241. return res.data
  242. }
  243. return []
  244. },
  245. getResource() {
  246. return this.resourceTmp
  247. },
  248. getYqResource() {
  249. return this.yqResourceTmp
  250. },
  251. getJcbList() {
  252. return this.jcbListTmp
  253. },
  254. //根据ref数组获取直接formData
  255. getFilledFormDataByRefs(refArr = []) {
  256. let result = {}
  257. refArr.map((ref) => {
  258. const refData = this.$refs[ref]?.getFilledFormData() || {}
  259. for (const [key, value] of Object.entries(refData)) {
  260. let index = 0
  261. // 如果当前键是可能重复的字段,且结果对象中已经存在该键,则添加索引后缀
  262. if (
  263. (key === 'headerSelectFields' || key === 'stepTableFormData') &&
  264. result.hasOwnProperty(key)
  265. ) {
  266. // 为重复字段生成带索引的键名,从1开始(因为第一个组件不需要后缀)
  267. const newKey = `${key}_${++index}`
  268. result[newKey] = value
  269. } else {
  270. // 正常合并其他字段
  271. result[key] = value
  272. }
  273. }
  274. })
  275. return result
  276. },
  277. //统一校验form表单是否填写
  278. async validFormFields(refArr = []) {
  279. let result = {}
  280. const refs = refArr.map((ref) => {
  281. let refData = {}
  282. if (this.$refs[ref][0]) {
  283. refData = this.$refs[ref][0]?.getFormData() || {}
  284. } else {
  285. refData = this.$refs[ref]?.getFormData() || {}
  286. }
  287. return refData
  288. })
  289. const validFormData = await Promise.all(refs).catch((err) => {
  290. // this.$message.error(err);
  291. if (err.errorType && err.errorType === 'step') {
  292. this.$message.error('请添加步骤')
  293. return
  294. }
  295. this.$message.error('表单内容未填完,请填写后再提交')
  296. })
  297. if (validFormData) {
  298. validFormData.forEach((item) => {
  299. for (const [key, value] of Object.entries(item)) {
  300. let index = 0
  301. // 如果当前键是可能重复的字段,且结果对象中已经存在该键,则添加索引后缀
  302. if (
  303. (key === 'headerSelectFields' || key === 'stepTableFormData') &&
  304. result.hasOwnProperty(key)
  305. ) {
  306. // 为重复字段生成带索引的键名,从1开始(因为第一个组件不需要后缀)
  307. const newKey = `${key}_${++index}`
  308. result[newKey] = value
  309. } else {
  310. // 正常合并其他字段
  311. result[key] = value
  312. }
  313. }
  314. })
  315. return result
  316. }
  317. return false
  318. },
  319. //试验配制条件options
  320. getDictOptions(dictType) {
  321. return this.dict.type[dictType] || []
  322. },
  323. setTemplateData(data) {
  324. this.$store.commit('template/SET_TEMPLATE_DATA', data)
  325. },
  326. //统一处理删除行
  327. deleteRow(index) {
  328. this.$refs.stepTableRef.deleteRow(index)
  329. },
  330. //统一处理浓度校验异常
  331. onCommonVerifyNdException(formFields,data){
  332. const {callback,rowIndex, key,dataSource} = data;
  333. const keys = [
  334. 'actStartSolutionVolume',
  335. 'actDiluentVolume',
  336. ]
  337. if(keys.includes(key)){
  338. const {targetAcSolution} = formFields;
  339. if (rowIndex === 0) {//第一行的浓度取选择的溶液,
  340. if (isValueEmpty(targetAcSolution)) {
  341. callback.prevent("请先选择起始源溶液")
  342. }
  343. } else {
  344. //非第一行的浓度取上一行的浓度
  345. const prevItem = dataSource[rowIndex - 1]
  346. const prevConcentration = prevItem.actSolutionConcentration;
  347. if (!prevConcentration) {
  348. callback.prevent("请先计算上一行的实际目标溶液浓度")
  349. }
  350. }
  351. }
  352. },
  353. //统一处理blur事件,因为有效周期和过期日期是相关的,所以需要在有效周期失焦时更新过期日期
  354. onHandleBlur(fields) {
  355. const {
  356. key,
  357. codeSTD,
  358. targetStartSolution,
  359. subTargetStartSolution,//预设起始源溶液浓度单位
  360. } = fields
  361. const { startDate } = this.formData
  362. if (key === 'codeSTD') {
  363. //起始编号STD失焦时,更新stepDataSource
  364. const arr = Array.from({ length: codeSTD }, (item, index) => ({
  365. actSolutionVolumePrecision: 3, //小数点精度默认为3
  366. actSolutionConcentrationPrecision: 3, //小数点精度默认为3
  367. targetDiluentVolumePrecision: 3, //小数点精度默认为3
  368. targetStartSolutionVolumePrecision: 3, //小数点精度默认为3
  369. targetSolutionCode: `STD${Number(codeSTD) - index}`
  370. }))
  371. this.$refs.stepTableRef.updateDataSource(arr)
  372. } else if (key === 'targetStartSolution' || key === 'subTargetStartSolution') {
  373. //起始溶液体积失焦时,更新目标溶液预计浓度
  374. const arr = this.$refs.stepTableRef?.getDataSource();
  375. const {headerSelectFields} = this.$refs.stepTableRef?.getFilledFormData();
  376. const params = {
  377. subTargetStartSolution,
  378. headerSelectFields
  379. }
  380. arr.forEach((item, rowIndex) => {
  381. this.updateTargetStartSolutionVolume(
  382. item,
  383. targetStartSolution,
  384. params
  385. )
  386. })
  387. }
  388. },
  389. //批量更新目标起始源溶液体积
  390. batchUpdateTargetStartSolutionVolume(arr, startConcentration, params) {
  391. arr.forEach((item, rowIndex) => {
  392. this.updateTargetStartSolutionVolume(
  393. item,
  394. startConcentration,
  395. params
  396. )
  397. })
  398. },
  399. //统一处理table失焦事件
  400. onHandleTableBlur(params) {
  401. const { rowIndex, colKey, value, item, dataSource,headerSelectFields } = params
  402. if (
  403. colKey === 'targetSolutionVolume' ||
  404. colKey === 'targetSolutionConcentration' ||
  405. colKey === 'targetStartSolutionVolumePrecision' ||
  406. colKey === 'targetDiluentVolumePrecision'
  407. ) {
  408. const volume =
  409. this.$refs.stepFormPackageRef?.getFormDataByKey(
  410. 'targetStartSolution'
  411. )
  412. const subTargetStartSolution =
  413. this.$refs.stepFormPackageRef?.getFormDataByKey(
  414. 'subTargetStartSolution'
  415. )
  416. const params = {
  417. subTargetStartSolution:subTargetStartSolution,
  418. headerSelectFields
  419. }
  420. if (isValueEmpty(volume)) {
  421. this.$message.error('请先选择预设起始源溶液浓度')
  422. } else {
  423. this.updateTargetStartSolutionVolume(item, volume,params)
  424. }
  425. } else if (
  426. colKey === 'actStartSolutionVolume' ||
  427. colKey === 'actDiluentVolume'
  428. ) {
  429. //实际起始溶液体积和实际目标溶液体积
  430. const {targetAcSolution} =
  431. this.$refs.stepFormPackageRef?.getFilledFormData() //获取实际起始溶液浓度
  432. if (isValueEmpty(targetAcSolution)) {
  433. this.$message.error('请先选择起始源溶液')
  434. return
  435. } else {
  436. const volResult = this.updateSjmbrynd(item, targetAcSolution)
  437. if (!volResult) {
  438. return
  439. }
  440. this.$refs.stepTableRef?.updateDataSourceByRowIndex(rowIndex, { actSolutionVolume: volResult.actVol, actSolutionConcentration: volResult.actNd }, "blur")
  441. }
  442. }
  443. },
  444. //计算目标溶液浓度
  445. calcNd(item, targetAcSolution) {
  446. //实际源溶液浓度÷(实际终体积÷源溶液加入体积);
  447. const precision = item.actSolutionConcentrationPrecision
  448. const volPrecision = item.actSolutionVolumePrecision || 0
  449. //实际稀释液体积
  450. const actDiluentVolume = item.actDiluentVolume
  451. const actStartSolutionVolume = item.actStartSolutionVolume
  452. //实际高源溶液加入体积+实际稀释液加入体积
  453. const actVol = (
  454. Number(actStartSolutionVolume) + Number(actDiluentVolume)
  455. ).toFixed(volPrecision)
  456. //实际目标溶液体积
  457. // item.actSolutionVolume = actVol
  458. //实际目标溶液浓度
  459. if (isValueEmpty(actDiluentVolume) && isValueEmpty(actStartSolutionVolume)) {
  460. return false
  461. }
  462. // 实际目标溶液浓度 = 实际源溶液浓度÷(实际终体积÷源溶液加入体积);
  463. const actNd = (
  464. parseFloat(targetAcSolution) / (
  465. parseFloat(actVol)/parseFloat(actStartSolutionVolume)
  466. )
  467. ).toFixed(precision)
  468. const nd = actNd === 'Infinity' ? 0 : Number(actNd)
  469. console.log(actNd, targetAcSolution, actStartSolutionVolume, actVol, "actNd")
  470. // item.actSolutionConcentration = actNd === 'Infinity' ? 0 : actNd
  471. return { actVol: Number(actVol), actNd: nd }
  472. },
  473. //计算并更新实际目标溶液浓度 先计算实际目标溶液体积再计算实际目标溶液浓度
  474. updateSjmbrynd(item, targetAcSolution) {
  475. return this.calcNd(item, targetAcSolution)
  476. },
  477. //更新起始溶液体积时,计算预设起始溶液体积和预设稀释液体积
  478. updateTargetStartSolutionVolume(item, volume,unitParams) {
  479. const precision = item.targetStartSolutionVolumePrecision || 0
  480. const concentration = item.targetSolutionConcentration
  481. const targetVolume = item.targetSolutionVolume
  482. const {subTargetStartSolution,headerSelectFields} = unitParams
  483. const {targetSolutionConcentrationUnit,targetSolutionVolumeUnit,targetStartSolutionVolumeUnit,targetDiluentVolumeUnit} = headerSelectFields
  484. const targetStartVolUnit = targetSolutionConcentrationUnit.split("/")[1];//先按照预设目标溶液浓度的单位标准
  485. if(
  486. isValueEmpty(concentration) ||
  487. isValueEmpty(targetVolume)||
  488. isValueEmpty(subTargetStartSolution)||
  489. isValueEmpty(targetSolutionConcentrationUnit)||
  490. isValueEmpty(targetSolutionVolumeUnit)||
  491. isValueEmpty(targetStartSolutionVolumeUnit)||
  492. isValueEmpty(targetDiluentVolumeUnit)
  493. ){
  494. return;
  495. }
  496. //将起始溶液浓度转换为和预设目标溶液浓度一样的单位再计算;
  497. const converStartCon = convertConcentration.convert(volume+subTargetStartSolution,targetSolutionConcentrationUnit)
  498. //将预设目标溶液体积转换为和预设目标溶液浓度单位的分母一样的单位再计算;如:预设目标溶液浓度单位为mg/mL,预设目标溶液体积单位为uL,则将预设目标溶液体积转换为mL
  499. const convertTargetVol = volumeConverter.convert(targetVolume+targetSolutionVolumeUnit,targetStartVolUnit)
  500. //预设起始溶液体积:(目标溶液预计浓度 乘以 目标溶液预计体积)除以 起始溶液浓度
  501. const result = Number(((concentration * convertTargetVol) / converStartCon).toFixed(
  502. precision
  503. ))
  504. //根据预设起始溶液体积单位转换计算后的数据
  505. const convertYsVol = volumeConverter.convert(result+targetStartVolUnit,targetStartSolutionVolumeUnit)
  506. item.targetStartSolutionVolume = convertYsVol
  507. // this.$refs.stepTableRef.updateDataSourceByRowIndex(rowIndex, { targetStartSolutionVolume: result });
  508. if (targetVolume) {
  509. //预设稀释液体积:目标溶液预计体积 减去 源溶液预计体积;
  510. const precision1 = item.targetDiluentVolumePrecision || 0;
  511. //将预设目标溶液体积转换为和预设起始溶液体积单位一样的单位再计算;
  512. const convertTargetVol1 = volumeConverter.convert(targetVolume+targetSolutionVolumeUnit,targetStartSolutionVolumeUnit)
  513. const result1 = (convertTargetVol1 - convertYsVol).toFixed(precision1)
  514. item.targetDiluentVolume = volumeConverter.convert(result1+targetStartSolutionVolumeUnit,targetDiluentVolumeUnit)
  515. // this.$refs.stepTableRef.updateDataSourceByRowIndex(rowIndex, { targetDiluentVolume: result1 });
  516. }
  517. }
  518. }
  519. }