华西海圻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.

602 lines
15 KiB

  1. import { parseTime } from './ruoyi'
  2. import { encrypt, decrypt } from '@/utils/encryptUtil'
  3. import moment from 'moment'
  4. /**
  5. * 表格时间格式化
  6. */
  7. export function formatDate(cellValue) {
  8. if (cellValue == null || cellValue == '') return ''
  9. var date = new Date(cellValue)
  10. var year = date.getFullYear()
  11. var month =
  12. date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
  13. var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
  14. var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
  15. var minutes =
  16. date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
  17. var seconds =
  18. date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
  19. return (
  20. year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
  21. )
  22. }
  23. /**
  24. * @param {number} time
  25. * @param {string} option
  26. * @returns {string}
  27. */
  28. export function formatTime(time, option) {
  29. if (('' + time).length === 10) {
  30. time = parseInt(time) * 1000
  31. } else {
  32. time = +time
  33. }
  34. const d = new Date(time)
  35. const now = Date.now()
  36. const diff = (now - d) / 1000
  37. if (diff < 30) {
  38. return '刚刚'
  39. } else if (diff < 3600) {
  40. // less 1 hour
  41. return Math.ceil(diff / 60) + '分钟前'
  42. } else if (diff < 3600 * 24) {
  43. return Math.ceil(diff / 3600) + '小时前'
  44. } else if (diff < 3600 * 24 * 2) {
  45. return '1天前'
  46. }
  47. if (option) {
  48. return parseTime(time, option)
  49. } else {
  50. return (
  51. d.getMonth() +
  52. 1 +
  53. '月' +
  54. d.getDate() +
  55. '日' +
  56. d.getHours() +
  57. '时' +
  58. d.getMinutes() +
  59. '分'
  60. )
  61. }
  62. }
  63. /**
  64. * @param {string} url
  65. * @returns {Object}
  66. */
  67. export function getQueryObject(url) {
  68. url = url == null ? window.location.href : url
  69. const search = url.substring(url.lastIndexOf('?') + 1)
  70. const obj = {}
  71. const reg = /([^?&=]+)=([^?&=]*)/g
  72. search.replace(reg, (rs, $1, $2) => {
  73. const name = decodeURIComponent($1)
  74. let val = decodeURIComponent($2)
  75. val = String(val)
  76. obj[name] = val
  77. return rs
  78. })
  79. return obj
  80. }
  81. /**
  82. * @param {string} input value
  83. * @returns {number} output value
  84. */
  85. export function byteLength(str) {
  86. // returns the byte length of an utf8 string
  87. let s = str.length
  88. for (var i = str.length - 1; i >= 0; i--) {
  89. const code = str.charCodeAt(i)
  90. if (code > 0x7f && code <= 0x7ff) s++
  91. else if (code > 0x7ff && code <= 0xffff) s += 2
  92. if (code >= 0xdc00 && code <= 0xdfff) i--
  93. }
  94. return s
  95. }
  96. /**
  97. * @param {Array} actual
  98. * @returns {Array}
  99. */
  100. export function cleanArray(actual) {
  101. const newArray = []
  102. for (let i = 0; i < actual.length; i++) {
  103. if (actual[i]) {
  104. newArray.push(actual[i])
  105. }
  106. }
  107. return newArray
  108. }
  109. /**
  110. * @param {Object} json
  111. * @returns {Array}
  112. */
  113. export function param(json) {
  114. if (!json) return ''
  115. return cleanArray(
  116. Object.keys(json).map((key) => {
  117. if (json[key] === undefined) return ''
  118. return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
  119. })
  120. ).join('&')
  121. }
  122. /**
  123. * @param {string} url
  124. * @returns {Object}
  125. */
  126. export function param2Obj(url) {
  127. const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
  128. if (!search) {
  129. return {}
  130. }
  131. const obj = {}
  132. const searchArr = search.split('&')
  133. searchArr.forEach((v) => {
  134. const index = v.indexOf('=')
  135. if (index !== -1) {
  136. const name = v.substring(0, index)
  137. const val = v.substring(index + 1, v.length)
  138. obj[name] = val
  139. }
  140. })
  141. return obj
  142. }
  143. /**
  144. * @param {string} val
  145. * @returns {string}
  146. */
  147. export function html2Text(val) {
  148. const div = document.createElement('div')
  149. div.innerHTML = val
  150. return div.textContent || div.innerText
  151. }
  152. /**
  153. * Merges two objects, giving the last one precedence
  154. * @param {Object} target
  155. * @param {(Object|Array)} source
  156. * @returns {Object}
  157. */
  158. export function objectMerge(target, source) {
  159. if (typeof target !== 'object') {
  160. target = {}
  161. }
  162. if (Array.isArray(source)) {
  163. return source.slice()
  164. }
  165. Object.keys(source).forEach((property) => {
  166. const sourceProperty = source[property]
  167. if (typeof sourceProperty === 'object') {
  168. target[property] = objectMerge(target[property], sourceProperty)
  169. } else {
  170. target[property] = sourceProperty
  171. }
  172. })
  173. return target
  174. }
  175. /**
  176. * @param {HTMLElement} element
  177. * @param {string} className
  178. */
  179. export function toggleClass(element, className) {
  180. if (!element || !className) {
  181. return
  182. }
  183. let classString = element.className
  184. const nameIndex = classString.indexOf(className)
  185. if (nameIndex === -1) {
  186. classString += '' + className
  187. } else {
  188. classString =
  189. classString.substr(0, nameIndex) +
  190. classString.substr(nameIndex + className.length)
  191. }
  192. element.className = classString
  193. }
  194. /**
  195. * @param {string} type
  196. * @returns {Date}
  197. */
  198. export function getTime(type) {
  199. if (type === 'start') {
  200. return new Date().getTime() - 3600 * 1000 * 24 * 90
  201. } else {
  202. return new Date(new Date().toDateString())
  203. }
  204. }
  205. /**
  206. * @param {Function} func
  207. * @param {number} wait
  208. * @param {boolean} immediate
  209. * @return {*}
  210. */
  211. export function debounce(func, wait, immediate) {
  212. let timeout, args, context, timestamp, result
  213. const later = function () {
  214. // 据上一次触发时间间隔
  215. const last = +new Date() - timestamp
  216. // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
  217. if (last < wait && last > 0) {
  218. timeout = setTimeout(later, wait - last)
  219. } else {
  220. timeout = null
  221. // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
  222. if (!immediate) {
  223. result = func.apply(context, args)
  224. if (!timeout) context = args = null
  225. }
  226. }
  227. }
  228. return function (...args) {
  229. context = this
  230. timestamp = +new Date()
  231. const callNow = immediate && !timeout
  232. // 如果延时不存在,重新设定延时
  233. if (!timeout) timeout = setTimeout(later, wait)
  234. if (callNow) {
  235. result = func.apply(context, args)
  236. context = args = null
  237. }
  238. return result
  239. }
  240. }
  241. /**
  242. * This is just a simple version of deep copy
  243. * Has a lot of edge cases bug
  244. * If you want to use a perfect deep copy, use lodash's _.cloneDeep
  245. * @param {Object} source
  246. * @returns {Object}
  247. */
  248. export function deepClone(source) {
  249. if (!source && typeof source !== 'object') {
  250. throw new Error('error arguments', 'deepClone')
  251. }
  252. const targetObj = source.constructor === Array ? [] : {}
  253. Object.keys(source).forEach((keys) => {
  254. if (source[keys] && typeof source[keys] === 'object') {
  255. targetObj[keys] = deepClone(source[keys])
  256. } else {
  257. targetObj[keys] = source[keys]
  258. }
  259. })
  260. return targetObj
  261. }
  262. /**
  263. * @param {Array} arr
  264. * @returns {Array}
  265. */
  266. export function uniqueArr(arr) {
  267. return Array.from(new Set(arr))
  268. }
  269. /**
  270. * @returns {string}
  271. */
  272. export function createUniqueString() {
  273. const timestamp = +new Date() + ''
  274. const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  275. return (+(randomNum + timestamp)).toString(32)
  276. }
  277. /**
  278. * Check if an element has a class
  279. * @param {HTMLElement} elm
  280. * @param {string} cls
  281. * @returns {boolean}
  282. */
  283. export function hasClass(ele, cls) {
  284. return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
  285. }
  286. /**
  287. * Add class to element
  288. * @param {HTMLElement} elm
  289. * @param {string} cls
  290. */
  291. export function addClass(ele, cls) {
  292. if (!hasClass(ele, cls)) ele.className += ' ' + cls
  293. }
  294. /**
  295. * Remove class from element
  296. * @param {HTMLElement} elm
  297. * @param {string} cls
  298. */
  299. export function removeClass(ele, cls) {
  300. if (hasClass(ele, cls)) {
  301. const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
  302. ele.className = ele.className.replace(reg, ' ')
  303. }
  304. }
  305. export function makeMap(str, expectsLowerCase) {
  306. const map = Object.create(null)
  307. const list = str.split(',')
  308. for (let i = 0; i < list.length; i++) {
  309. map[list[i]] = true
  310. }
  311. return expectsLowerCase ? (val) => map[val.toLowerCase()] : (val) => map[val]
  312. }
  313. export const exportDefault = 'export default '
  314. export const beautifierConf = {
  315. html: {
  316. indent_size: '2',
  317. indent_char: ' ',
  318. max_preserve_newlines: '-1',
  319. preserve_newlines: false,
  320. keep_array_indentation: false,
  321. break_chained_methods: false,
  322. indent_scripts: 'separate',
  323. brace_style: 'end-expand',
  324. space_before_conditional: true,
  325. unescape_strings: false,
  326. jslint_happy: false,
  327. end_with_newline: true,
  328. wrap_line_length: '110',
  329. indent_inner_html: true,
  330. comma_first: false,
  331. e4x: true,
  332. indent_empty_lines: true
  333. },
  334. js: {
  335. indent_size: '2',
  336. indent_char: ' ',
  337. max_preserve_newlines: '-1',
  338. preserve_newlines: false,
  339. keep_array_indentation: false,
  340. break_chained_methods: false,
  341. indent_scripts: 'normal',
  342. brace_style: 'end-expand',
  343. space_before_conditional: true,
  344. unescape_strings: false,
  345. jslint_happy: true,
  346. end_with_newline: true,
  347. wrap_line_length: '110',
  348. indent_inner_html: true,
  349. comma_first: false,
  350. e4x: true,
  351. indent_empty_lines: true
  352. }
  353. }
  354. // 首字母大小
  355. export function titleCase(str) {
  356. return str.replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
  357. }
  358. // 下划转驼峰
  359. export function camelCase(str) {
  360. return str.replace(/_[a-z]/g, (str1) => str1.substr(-1).toUpperCase())
  361. }
  362. export function isNumberStr(str) {
  363. return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
  364. }
  365. // 编码
  366. export function caesarCipher(str) {
  367. return btoa(encrypt(str))
  368. }
  369. // 解码
  370. export function caesarDecipher(str) {
  371. return decrypt(atob(str))
  372. }
  373. //比较值是否相等,不需要比较类型
  374. export const isEqual = (oldValue, nowValue) => {
  375. if (oldValue === null || nowValue === null) {
  376. return oldValue === nowValue
  377. }
  378. if (typeof oldValue === 'object' && typeof nowValue === 'object') {
  379. return JSON.stringify(oldValue) === JSON.stringify(nowValue)
  380. }
  381. return oldValue == nowValue
  382. }
  383. //计算过期时间
  384. export const getExpireDate = (
  385. startDate,
  386. effectivePeriod,
  387. effectivePeriodUnit
  388. ) => {
  389. const start = moment(startDate)
  390. const unit = effectivePeriodUnit === '天' ? 'days' : 'hours'
  391. const end = start
  392. .add(Number(effectivePeriod), unit)
  393. .format('YYYY-MM-DD HH:mm:ss')
  394. return end
  395. }
  396. export function getuuid() {
  397. return Math.random().toString(36).substring(2) + Date.now().toString(36)
  398. }
  399. // 判断值是否为空
  400. export function isValueEmpty(value) {
  401. if (value === null || value === undefined || value === '' || value === false) {
  402. return true
  403. }
  404. if (typeof value === 'string' && value.trim() === '') {
  405. return true
  406. }
  407. if (Array.isArray(value) && value.length === 0) {
  408. return true
  409. }
  410. if (Object.keys(value).length === 0 && typeof value=='object') {
  411. return true;
  412. }
  413. return false
  414. }
  415. //去重步骤里面的试剂(需要计算总和)和仪器;
  416. export function duplicateResource(sj, yq) {
  417. // 对sj数组根据type和value值去重,并将yl按单位换算后累加
  418. const sjMap = new Map()
  419. // 体积单位转换为基本单位L的倍数
  420. const volumeUnits = {
  421. pL: 1e-12,
  422. nL: 1e-9,
  423. uL: 1e-6,
  424. mL: 1e-3,
  425. L: 1
  426. }
  427. // 质量单位转换为基本单位g的倍数
  428. const massUnits = {
  429. pg: 1e-12,
  430. ng: 1e-9,
  431. ug: 1e-6,
  432. mg: 1e-3,
  433. g: 1,
  434. kg: 1e3
  435. }
  436. for (const item of sj) {
  437. const key = `${item.type}_${item.value}`
  438. console.log(item, 'item')
  439. if (sjMap.has(key)) {
  440. // 如果已存在相同type和value的项,累加yl值
  441. const existingItem = sjMap.get(key)
  442. console.log(existingItem, 'existingItem')
  443. // 根据类型选择合适的单位转换
  444. let currentItemYlInBaseUnit, existingItemYlInBaseUnit
  445. if (item.type === '1') {
  446. // 体积单位转换
  447. const currentItemYl = isNaN(parseFloat(item.yl))
  448. ? 0
  449. : parseFloat(item.yl)
  450. const existingItemYl = isNaN(parseFloat(existingItem.yl))
  451. ? 0
  452. : parseFloat(existingItem.yl)
  453. currentItemYlInBaseUnit = currentItemYl * volumeUnits[item.dw] || 0
  454. existingItemYlInBaseUnit =
  455. existingItemYl * volumeUnits[existingItem.dw] || 0
  456. } else if (item.type === '7') {
  457. // 质量单位转换
  458. const currentItemYl = isNaN(parseFloat(item.yl))
  459. ? 0
  460. : parseFloat(item.yl)
  461. const existingItemYl = isNaN(parseFloat(existingItem.yl))
  462. ? 0
  463. : parseFloat(existingItem.yl)
  464. currentItemYlInBaseUnit = currentItemYl * massUnits[item.dw] || 0
  465. existingItemYlInBaseUnit =
  466. existingItemYl * massUnits[existingItem.dw] || 0
  467. } else {
  468. // 其他类型暂不处理单位转换,直接相加
  469. const currentItemYl = isNaN(parseFloat(item.yl))
  470. ? 0
  471. : parseFloat(item.yl)
  472. const existingItemYl = isNaN(parseFloat(existingItem.yl))
  473. ? 0
  474. : parseFloat(existingItem.yl)
  475. currentItemYlInBaseUnit = currentItemYl || 0
  476. existingItemYlInBaseUnit = existingItemYl || 0
  477. }
  478. // 计算总和
  479. const totalYlInBaseUnit =
  480. currentItemYlInBaseUnit + existingItemYlInBaseUnit
  481. // 更新existingItem的yl值,保持使用第一个项目的单位作为基准单位
  482. if (item.type === '1') {
  483. existingItem.yl = (
  484. totalYlInBaseUnit / volumeUnits[existingItem.dw]
  485. ).toString()
  486. } else if (item.type === '7') {
  487. existingItem.yl = (
  488. totalYlInBaseUnit / massUnits[existingItem.dw]
  489. ).toString()
  490. } else {
  491. existingItem.yl = totalYlInBaseUnit.toString()
  492. }
  493. } else {
  494. // 如果不存在,添加新项
  495. sjMap.set(key, { ...item })
  496. }
  497. }
  498. // 将Map中的值转换回数组
  499. sj.length = 0 // 清空原数组
  500. for (const value of sjMap.values()) {
  501. sj.push(value)
  502. }
  503. // 对yq数组根据value去重
  504. yq = yq.filter(
  505. (item, index, self) =>
  506. self.findIndex((obj) => obj.value === item.value) === index
  507. )
  508. return { sj, yq }
  509. }
  510. //是不是试剂/仪器等弹窗类型
  511. export function isRegent(item, fieldCode = 'type') {
  512. const type = item[fieldCode]
  513. const typeList = [
  514. 'sj',
  515. 'gsp',
  516. 'mix',
  517. 'xj',
  518. 'xb',
  519. 'gyzj',
  520. 'mjy',
  521. 'yq',
  522. 'jcb',
  523. 'qxbd'
  524. ]
  525. return typeList.includes(type)
  526. }
  527. /**
  528. * 估算字符串在 Excel 中的显示宽度简单规则中文字符算2英文字符算1
  529. * @param {string} str 要计算的字符串
  530. * @returns {number} 估算宽度
  531. */
  532. export function getStringWidth(str) {
  533. if (!str) return 0
  534. let width = 0
  535. for (let char of str.toString()) {
  536. // 中文字符范围(可根据需要扩展)
  537. if (/[\u4e00-\u9fa5]/.test(char)) {
  538. width += 2
  539. } else {
  540. width += 1
  541. }
  542. }
  543. return width
  544. }
  545. //根据选项获取默认值,为了
  546. export const getDefaultValueByOptions = (options = []) => {
  547. const arr = [];
  548. options.forEach(item => {
  549. const { children = [], label } = item;
  550. //目前只考虑2层,也不考虑label值重复的问题;
  551. if (children.length > 0) {
  552. children.forEach(child => {
  553. arr.push({ label: child.label, checked: undefined })
  554. })
  555. } else {
  556. arr.push({ label, checked: undefined })
  557. }
  558. })
  559. return arr;
  560. }