index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. import {
  2. rangeOptions,
  3. pageTypes,
  4. cardTypes,
  5. chartTypes,
  6. MULTI_COLORS_OPTIONS,
  7. MULTI_SINGLE_COLORS_OPTIONS,
  8. rangeAndIntervalOptions,
  9. useLATEST_OPTIONS
  10. } from './constant'
  11. import * as echarts from 'echarts'
  12. import { useEffect, useMemo, useState, useCallback } from 'react'
  13. import html2canvas from 'html2canvas'
  14. import { useSearchParams } from 'react-router-dom'
  15. import dayjs from 'dayjs'
  16. function childValue(arr) {
  17. return (arr ?? []).reduce((acc, cur) => {
  18. const childValues = (cur?.['child'] ?? []).map((item) => item?.value
  19. )
  20. return acc.concat(childValues)
  21. }, [])
  22. }
  23. function hasValue(val) {
  24. return val !== null && val != void 0
  25. }
  26. function debounce(fn, delay = 1000) {
  27. // 实现防抖函数的核心是使用setTimeout
  28. // time变量用于保存setTimeout返回的Id
  29. let time = null
  30. // 将回调接收的参数保存到args数组中
  31. function _debounce(...args) {
  32. // 如果time不为0,也就是说有定时器存在,将该定时器清除
  33. if (time !== null) {
  34. clearTimeout(time)
  35. }
  36. time = setTimeout(() => {
  37. // 使用apply改变fn的this,同时将参数传递给fn
  38. fn.apply(this, args)
  39. }, delay)
  40. }
  41. // 防抖函数会返回另一个函数,该函数才是真正被调用的函数
  42. return _debounce
  43. }
  44. function throttle(fn, interval = 100) {
  45. //该变量用于记录上一次函数的执行事件
  46. let lastTime = 0
  47. const _throttle = function (...args) {
  48. // 获取当前时间
  49. const nowTime = new Date().getTime()
  50. // cd剩余时间
  51. const remainTime = nowTime - lastTime
  52. // 如果剩余时间大于间隔时间,也就是说可以再次执行函数
  53. if (remainTime - interval >= 0) {
  54. fn.apply(this, args)
  55. // 将上一次函数执行的时间设置为nowTime,这样下次才能重新进入cd
  56. lastTime = nowTime
  57. }
  58. }
  59. // 返回_throttle函数
  60. return _throttle
  61. }
  62. const useResizeEchart = dom => {
  63. useEffect(() => {
  64. if (dom) {
  65. const resizeObserver = new ResizeObserver(() => {
  66. let instance = echarts.getInstanceByDom(dom)
  67. if (instance) {
  68. instance.resize()
  69. }
  70. })
  71. resizeObserver.observe(dom)
  72. return () => {
  73. resizeObserver.unobserve(dom)
  74. }
  75. }
  76. }, [dom])
  77. return []
  78. }
  79. const useObjVals = (vals, onChange, minObjectNum = 0) => {
  80. const arr = useMemo(() => {
  81. return vals
  82. .map((val, index) => {
  83. return {
  84. value: val,
  85. onChange: nVal =>
  86. onChange(Object.assign(vals.concat(), { [index]: nVal }))
  87. }
  88. })
  89. .concat(
  90. vals.length < minObjectNum
  91. ? new Array(minObjectNum - vals.length).fill(null).map(() => {
  92. return {
  93. value: null,
  94. onChange: () => { }
  95. }
  96. })
  97. : []
  98. )
  99. }, [vals, onChange, minObjectNum])
  100. return arr
  101. }
  102. const downloadCanvas = title => {
  103. return canvas => {
  104. if (canvas) {
  105. let dataURL = canvas.toDataURL('image/png')
  106. // dataURL = dataURL.replace('data:image/png;base64,', '')
  107. const a = document.createElement('a')
  108. document.body.appendChild(a)
  109. a.href = dataURL
  110. a.download = title
  111. a.click()
  112. document.body.removeChild(a)
  113. }
  114. }
  115. }
  116. const parseDom = targetDom => {
  117. return html2canvas(targetDom, {
  118. width: targetDom.offsetWidth, //画布的宽
  119. height: targetDom.offsetHeight, //画布的高
  120. scale: 1, //处理模糊问题
  121. useCORS: true //开启跨域,这个是必须的
  122. //scrollX:0,//图片x轴的位置
  123. //scrollY:0,//图片Y轴的位置
  124. //x:0,//x轴的偏移量
  125. //Y:0//Y轴的便宜量
  126. })
  127. }
  128. function downloadDomImg(targetDom, title) {
  129. if (targetDom) {
  130. parseDom(targetDom).then(downloadCanvas(title))
  131. }
  132. }
  133. const usePopupContainer = () => {
  134. const [props, setProps] = useState(null)
  135. const refFunc = useCallback(d => {
  136. setProps(
  137. d
  138. ? {
  139. getPopupContainer: () => d
  140. }
  141. : null
  142. )
  143. }, [])
  144. return [refFunc, props]
  145. }
  146. const useDomRect = dom => {
  147. const [rect, setRect] = useState(null)
  148. useEffect(() => {
  149. if (dom) {
  150. const resizeObserver = new ResizeObserver(
  151. throttle(entries => {
  152. entries.forEach(entry => {
  153. // console.log('大小位置', entry.contentRect);
  154. setRect({
  155. height: entry?.contentRect?.height ?? 0,
  156. width: entry?.contentRect?.width ?? 0
  157. })
  158. })
  159. })
  160. )
  161. resizeObserver.observe(dom)
  162. return () => {
  163. resizeObserver.unobserve(dom)
  164. }
  165. }
  166. }, [dom])
  167. return [rect]
  168. }
  169. const useTableBodyRect = d => {
  170. const headD = useMemo(() => {
  171. if (d) {
  172. return d.querySelector('.ant-table-header') ?? null
  173. }
  174. return null
  175. }, [d])
  176. const [headerRect] = useDomRect(headD)
  177. const deltaHeight = useMemo(() => {
  178. return headerRect?.height ?? 0
  179. }, [headerRect])
  180. const [wrapperRect] = useDomRect(d)
  181. const pRect = useMemo(() => {
  182. if (wrapperRect && typeof deltaHeight === 'number' && !isNaN(deltaHeight)) {
  183. let nHeight = (wrapperRect?.height ?? 0) - deltaHeight
  184. if (nHeight < 0) {
  185. nHeight = 0
  186. }
  187. return {
  188. ...wrapperRect,
  189. height: nHeight
  190. }
  191. }
  192. return null
  193. }, [deltaHeight, wrapperRect])
  194. return [pRect]
  195. }
  196. const handleNan = (val, config, defaultRet = '-') => {
  197. if (typeof val === 'number') {
  198. const precision = config?.basic_settings?.precision ?? 1
  199. return isNaN(val) ? defaultRet : val.toLocaleString('zh-cn', { 'minimumFractionDigits': precision, 'maximumFractionDigits': precision })
  200. }
  201. return defaultRet
  202. }
  203. const fetchVariablesFromStr = str => {
  204. let ret = []
  205. if (typeof str === 'string' && str !== '') {
  206. const reg_g = /\$\{(.+?)\}/g;
  207. let result = null
  208. do {
  209. result = reg_g.exec(str);
  210. // console.log("result=", result);
  211. result && ret.push(result[1]);
  212. } while (result)
  213. }
  214. return ret
  215. }
  216. const fetchVariablesFromInfo = info => {
  217. let ret = []
  218. if ([1, 2, 3, 4, 5, 7].indexOf(info?.chart_type) !== -1) {
  219. const pointIds = (info?.config?.points ?? []).map(({ point_id }) => point_id)
  220. for (let id of pointIds) {
  221. ret.push(...fetchVariablesFromStr(id))
  222. }
  223. } else { }
  224. ret = [...new Set(ret)].filter(str => typeof str === 'string' && str !== '')
  225. return ret
  226. }
  227. function postDownloadFile(url, params, method='post') {
  228. let form = document.createElement('form');
  229. form.setAttribute('style', 'display:none;');
  230. form.setAttribute('method', method);
  231. form.setAttribute('action', process.env.REACT_APP_BASE_URL + url);
  232. Object.keys(params).forEach((key) => {
  233. if (Array.isArray(params[key])) {
  234. // value_list = []
  235. params[key].forEach((value) => {
  236. let input_item = document.createElement('input');
  237. input_item.name = key;
  238. input_item.value = value;
  239. form.appendChild(input_item);
  240. });
  241. } else {
  242. let input_item = document.createElement('input');
  243. input_item.name = key;
  244. input_item.value = params[key];
  245. form.appendChild(input_item);
  246. }
  247. });
  248. document.body.appendChild(form);
  249. // let windowName = 'Download(' + (new Date().getTime()) + ')';
  250. // let w = window.open('', windowName);
  251. // '_blank'; 打开新页面,注释则不打开新页面
  252. form.target = '_blank';
  253. form.submit();
  254. form.remove();
  255. // w.close();
  256. }
  257. const useUrlTs = () => {
  258. const [searchParams] = useSearchParams()
  259. const ts = useMemo(() => {
  260. const t = searchParams.get('ts')
  261. let ret = null
  262. try {
  263. const mT = dayjs.unix(parseInt(t))
  264. if (mT.isValid()) {
  265. ret = mT.unix()
  266. }
  267. } catch (e) { }
  268. return ret
  269. }, [searchParams])
  270. return [ts]
  271. }
  272. const tickMap = {
  273. 'minute': {
  274. formatStr: 'YYYY-MM-DD HH:mm:00',
  275. waitFunc: mTime => {
  276. const mS = cCurr.millisecond()
  277. if (mS === 0) {
  278. }
  279. }
  280. }
  281. }
  282. const useSystemTick = unit => {
  283. // 跟随系统时间tick,暂时做一版 分钟tick
  284. // "YYYY-MM-DD HH:mm:00"
  285. const [tickInfo, setTickInfo] = useState(null)
  286. useEffect(() => {
  287. let myTimeout = null
  288. let prev = ''
  289. const curr = dayjs()
  290. if (tickInfo) {
  291. prev = tickInfo?.timeStr ?? ''
  292. } else {
  293. // 无参考时间以当前为准
  294. prev = curr.format("YYYY-MM-DD HH:mm:00")
  295. }
  296. const currTimeStr = curr.format("YYYY-MM-DD HH:mm:00")
  297. if (currTimeStr !== prev) {
  298. setTickInfo({
  299. type: 'SYSTEM_MINUTE_CHANGE',
  300. timeStr: currTimeStr
  301. })
  302. } else {
  303. let cCurr = curr
  304. const func = () => {
  305. const cCurrStr = cCurr.format("YYYY-MM-DD HH:mm:00")
  306. // console.log(cCurrStr)
  307. if (cCurrStr !== prev) {
  308. setTickInfo({
  309. type: 'SYSTEM_MINUTE_CHANGE',
  310. timeStr: cCurrStr
  311. })
  312. } else {
  313. myTimeout = setTimeout(func, 1000 - cCurr.millisecond())
  314. cCurr = dayjs()
  315. }
  316. }
  317. func()
  318. }
  319. return () => {
  320. if (myTimeout) {
  321. clearTimeout(myTimeout)
  322. }
  323. }
  324. }, [tickInfo])
  325. return [tickInfo]
  326. }
  327. function setComma(num){
  328. if(num===undefined||num===null){
  329. return ''
  330. }
  331. let str = String(num)
  332. let str2 = []
  333. let suffix = ''
  334. let isNegative = false
  335. if(str.indexOf('-')===0){
  336. isNegative = true
  337. }
  338. if(str.indexOf('.')!==-1){
  339. str2 = str.split('.')
  340. suffix = '.'+str2[1]
  341. }else if(str.indexOf(' ')!==-1){
  342. str2 = str.split(' ')
  343. suffix = ' '+str2[1]
  344. }else{
  345. str2 = [str,'']
  346. }
  347. let str3 = str2[0]
  348. if(isNegative){
  349. str3 = str2[0].substring(1)
  350. }
  351. if(str3.length>3){
  352. str3 = str3.split('').reverse()
  353. let newNum = 0
  354. for(var i = 0;i < str3.length;i++){
  355. if(newNum!==0&&newNum%3===0){
  356. str3.splice(i,0,',')
  357. i++
  358. }
  359. newNum++
  360. }
  361. str3 = str3.reverse().join('')
  362. }
  363. if(isNegative){
  364. return '-'+str3+suffix
  365. }
  366. return str3+suffix
  367. }
  368. function RGBToHex(rgba) {
  369. const [r, g, b, o] = rgba
  370. const hex = `#${(
  371. ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) +
  372. (o < 1 && o >= 0
  373. ? ((1 << 8) + Math.floor(o * 255)).toString(16).slice(1)
  374. : '')
  375. ).toUpperCase()}`
  376. return hex
  377. }
  378. function hexToRgb(hexValue) {
  379. let a = 1
  380. if (hexValue.length > 7) {
  381. a = Math.ceil(10000 * parseInt(hexValue.slice(7), 16) / 255) / 10000;
  382. }
  383. return { r: parseInt(hexValue.slice(1, 3), 16), g: parseInt(hexValue.slice(3, 5), 16), b: parseInt(hexValue.slice(5, 7), 16), a };
  384. }
  385. export {
  386. useSystemTick,
  387. useUrlTs,
  388. rangeOptions,
  389. rangeAndIntervalOptions,
  390. useLATEST_OPTIONS,
  391. pageTypes,
  392. cardTypes,
  393. chartTypes,
  394. MULTI_COLORS_OPTIONS,
  395. MULTI_SINGLE_COLORS_OPTIONS,
  396. useResizeEchart,
  397. useObjVals,
  398. downloadDomImg,
  399. usePopupContainer,
  400. useDomRect,
  401. useTableBodyRect,
  402. handleNan,
  403. fetchVariablesFromInfo,
  404. fetchVariablesFromStr,
  405. postDownloadFile,
  406. setComma,
  407. RGBToHex,
  408. hexToRgb,
  409. childValue,
  410. hasValue
  411. }