import { rangeOptions, pageTypes, cardTypes, chartTypes, MULTI_COLORS_OPTIONS, MULTI_SINGLE_COLORS_OPTIONS, rangeAndIntervalOptions, useLATEST_OPTIONS } from './constant' import * as echarts from 'echarts' import { useEffect, useMemo, useState, useCallback } from 'react' import html2canvas from 'html2canvas' import { useSearchParams } from 'react-router-dom' import dayjs from 'dayjs' function childValue(arr) { return (arr ?? []).reduce((acc, cur) => { const childValues = (cur?.['child'] ?? []).map((item) => item?.value ) return acc.concat(childValues) }, []) } function hasValue(val) { return val !== null && val != void 0 } function debounce(fn, delay = 1000) { // 实现防抖函数的核心是使用setTimeout // time变量用于保存setTimeout返回的Id let time = null // 将回调接收的参数保存到args数组中 function _debounce(...args) { // 如果time不为0,也就是说有定时器存在,将该定时器清除 if (time !== null) { clearTimeout(time) } time = setTimeout(() => { // 使用apply改变fn的this,同时将参数传递给fn fn.apply(this, args) }, delay) } // 防抖函数会返回另一个函数,该函数才是真正被调用的函数 return _debounce } function throttle(fn, interval = 100) { //该变量用于记录上一次函数的执行事件 let lastTime = 0 const _throttle = function (...args) { // 获取当前时间 const nowTime = new Date().getTime() // cd剩余时间 const remainTime = nowTime - lastTime // 如果剩余时间大于间隔时间,也就是说可以再次执行函数 if (remainTime - interval >= 0) { fn.apply(this, args) // 将上一次函数执行的时间设置为nowTime,这样下次才能重新进入cd lastTime = nowTime } } // 返回_throttle函数 return _throttle } const useResizeEchart = dom => { useEffect(() => { if (dom) { const resizeObserver = new ResizeObserver(() => { let instance = echarts.getInstanceByDom(dom) if (instance) { instance.resize() } }) resizeObserver.observe(dom) return () => { resizeObserver.unobserve(dom) } } }, [dom]) return [] } const useObjVals = (vals, onChange, minObjectNum = 0) => { const arr = useMemo(() => { return vals .map((val, index) => { return { value: val, onChange: nVal => onChange(Object.assign(vals.concat(), { [index]: nVal })) } }) .concat( vals.length < minObjectNum ? new Array(minObjectNum - vals.length).fill(null).map(() => { return { value: null, onChange: () => { } } }) : [] ) }, [vals, onChange, minObjectNum]) return arr } const downloadCanvas = title => { return canvas => { if (canvas) { let dataURL = canvas.toDataURL('image/png') // dataURL = dataURL.replace('data:image/png;base64,', '') const a = document.createElement('a') document.body.appendChild(a) a.href = dataURL a.download = title a.click() document.body.removeChild(a) } } } const parseDom = targetDom => { return html2canvas(targetDom, { width: targetDom.offsetWidth, //画布的宽 height: targetDom.offsetHeight, //画布的高 scale: 1, //处理模糊问题 useCORS: true //开启跨域,这个是必须的 //scrollX:0,//图片x轴的位置 //scrollY:0,//图片Y轴的位置 //x:0,//x轴的偏移量 //Y:0//Y轴的便宜量 }) } function downloadDomImg(targetDom, title) { if (targetDom) { parseDom(targetDom).then(downloadCanvas(title)) } } const usePopupContainer = () => { const [props, setProps] = useState(null) const refFunc = useCallback(d => { setProps( d ? { getPopupContainer: () => d } : null ) }, []) return [refFunc, props] } const useDomRect = dom => { const [rect, setRect] = useState(null) useEffect(() => { if (dom) { const resizeObserver = new ResizeObserver( throttle(entries => { entries.forEach(entry => { // console.log('大小位置', entry.contentRect); setRect({ height: entry?.contentRect?.height ?? 0, width: entry?.contentRect?.width ?? 0 }) }) }) ) resizeObserver.observe(dom) return () => { resizeObserver.unobserve(dom) } } }, [dom]) return [rect] } const useTableBodyRect = d => { const headD = useMemo(() => { if (d) { return d.querySelector('.ant-table-header') ?? null } return null }, [d]) const [headerRect] = useDomRect(headD) const deltaHeight = useMemo(() => { return headerRect?.height ?? 0 }, [headerRect]) const [wrapperRect] = useDomRect(d) const pRect = useMemo(() => { if (wrapperRect && typeof deltaHeight === 'number' && !isNaN(deltaHeight)) { let nHeight = (wrapperRect?.height ?? 0) - deltaHeight if (nHeight < 0) { nHeight = 0 } return { ...wrapperRect, height: nHeight } } return null }, [deltaHeight, wrapperRect]) return [pRect] } const handleNan = (val, config, defaultRet = '-') => { if (typeof val === 'number') { const precision = config?.basic_settings?.precision ?? 1 return isNaN(val) ? defaultRet : val.toLocaleString('zh-cn', { 'minimumFractionDigits': precision, 'maximumFractionDigits': precision }) } return defaultRet } const fetchVariablesFromStr = str => { let ret = [] if (typeof str === 'string' && str !== '') { const reg_g = /\$\{(.+?)\}/g; let result = null do { result = reg_g.exec(str); // console.log("result=", result); result && ret.push(result[1]); } while (result) } return ret } const fetchVariablesFromInfo = info => { let ret = [] if ([1, 2, 3, 4, 5, 7].indexOf(info?.chart_type) !== -1) { const pointIds = (info?.config?.points ?? []).map(({ point_id }) => point_id) for (let id of pointIds) { ret.push(...fetchVariablesFromStr(id)) } } else { } ret = [...new Set(ret)].filter(str => typeof str === 'string' && str !== '') return ret } function postDownloadFile(url, params, method='post') { let form = document.createElement('form'); form.setAttribute('style', 'display:none;'); form.setAttribute('method', method); form.setAttribute('action', process.env.REACT_APP_BASE_URL + url); Object.keys(params).forEach((key) => { if (Array.isArray(params[key])) { // value_list = [] params[key].forEach((value) => { let input_item = document.createElement('input'); input_item.name = key; input_item.value = value; form.appendChild(input_item); }); } else { let input_item = document.createElement('input'); input_item.name = key; input_item.value = params[key]; form.appendChild(input_item); } }); document.body.appendChild(form); // let windowName = 'Download(' + (new Date().getTime()) + ')'; // let w = window.open('', windowName); // '_blank'; 打开新页面,注释则不打开新页面 form.target = '_blank'; form.submit(); form.remove(); // w.close(); } const useUrlTs = () => { const [searchParams] = useSearchParams() const ts = useMemo(() => { const t = searchParams.get('ts') let ret = null try { const mT = dayjs.unix(parseInt(t)) if (mT.isValid()) { ret = mT.unix() } } catch (e) { } return ret }, [searchParams]) return [ts] } const tickMap = { 'minute': { formatStr: 'YYYY-MM-DD HH:mm:00', waitFunc: mTime => { const mS = cCurr.millisecond() if (mS === 0) { } } } } const useSystemTick = unit => { // 跟随系统时间tick,暂时做一版 分钟tick // "YYYY-MM-DD HH:mm:00" const [tickInfo, setTickInfo] = useState(null) useEffect(() => { let myTimeout = null let prev = '' const curr = dayjs() if (tickInfo) { prev = tickInfo?.timeStr ?? '' } else { // 无参考时间以当前为准 prev = curr.format("YYYY-MM-DD HH:mm:00") } const currTimeStr = curr.format("YYYY-MM-DD HH:mm:00") if (currTimeStr !== prev) { setTickInfo({ type: 'SYSTEM_MINUTE_CHANGE', timeStr: currTimeStr }) } else { let cCurr = curr const func = () => { const cCurrStr = cCurr.format("YYYY-MM-DD HH:mm:00") // console.log(cCurrStr) if (cCurrStr !== prev) { setTickInfo({ type: 'SYSTEM_MINUTE_CHANGE', timeStr: cCurrStr }) } else { myTimeout = setTimeout(func, 1000 - cCurr.millisecond()) cCurr = dayjs() } } func() } return () => { if (myTimeout) { clearTimeout(myTimeout) } } }, [tickInfo]) return [tickInfo] } function setComma(num){ if(num===undefined||num===null){ return '' } let str = String(num) let str2 = [] let suffix = '' let isNegative = false if(str.indexOf('-')===0){ isNegative = true } if(str.indexOf('.')!==-1){ str2 = str.split('.') suffix = '.'+str2[1] }else if(str.indexOf(' ')!==-1){ str2 = str.split(' ') suffix = ' '+str2[1] }else{ str2 = [str,''] } let str3 = str2[0] if(isNegative){ str3 = str2[0].substring(1) } if(str3.length>3){ str3 = str3.split('').reverse() let newNum = 0 for(var i = 0;i < str3.length;i++){ if(newNum!==0&&newNum%3===0){ str3.splice(i,0,',') i++ } newNum++ } str3 = str3.reverse().join('') } if(isNegative){ return '-'+str3+suffix } return str3+suffix } function RGBToHex(rgba) { const [r, g, b, o] = rgba const hex = `#${( ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) + (o < 1 && o >= 0 ? ((1 << 8) + Math.floor(o * 255)).toString(16).slice(1) : '') ).toUpperCase()}` return hex } function hexToRgb(hexValue) { let a = 1 if (hexValue.length > 7) { a = Math.ceil(10000 * parseInt(hexValue.slice(7), 16) / 255) / 10000; } return { r: parseInt(hexValue.slice(1, 3), 16), g: parseInt(hexValue.slice(3, 5), 16), b: parseInt(hexValue.slice(5, 7), 16), a }; } export { useSystemTick, useUrlTs, rangeOptions, rangeAndIntervalOptions, useLATEST_OPTIONS, pageTypes, cardTypes, chartTypes, MULTI_COLORS_OPTIONS, MULTI_SINGLE_COLORS_OPTIONS, useResizeEchart, useObjVals, downloadDomImg, usePopupContainer, useDomRect, useTableBodyRect, handleNan, fetchVariablesFromInfo, fetchVariablesFromStr, postDownloadFile, setComma, RGBToHex, hexToRgb, childValue, hasValue }