Commit 3f662052 authored by 陈龙's avatar 陈龙

feat: 完善移动端适配

parent 6a5da773
...@@ -146,6 +146,7 @@ ...@@ -146,6 +146,7 @@
}, },
"dependencies": { "dependencies": {
"@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-methods": "^7.18.6",
"@wisdom-components/VmsVideo": "1.1.13",
"@wisdom-components/basictable": "^1.5.14", "@wisdom-components/basictable": "^1.5.14",
"@wisdom-components/ec_historyview": "^1.4.3", "@wisdom-components/ec_historyview": "^1.4.3",
"@wisdom-components/empty": "^1.3.9", "@wisdom-components/empty": "^1.3.9",
...@@ -153,8 +154,8 @@ ...@@ -153,8 +154,8 @@
"@wisdom-components/loadbox": "1.1.4", "@wisdom-components/loadbox": "1.1.4",
"@wisdom-components/timerangepicker": "^1.3.4", "@wisdom-components/timerangepicker": "^1.3.4",
"@wisdom-components/videoslidermodal": "1.1.2", "@wisdom-components/videoslidermodal": "1.1.2",
"@wisdom-components/VmsVideo": "1.1.13",
"@wisdom-utils/utils": "0.0.46", "@wisdom-utils/utils": "0.0.46",
"antd-mobile": "5.10.4",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
"echarts": "^5.4.0", "echarts": "^5.4.0",
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
"test": "echo \"Error: run tests from root\" && exit 1" "test": "echo \"Error: run tests from root\" && exit 1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.17.9" "@babel/runtime": "^7.17.9",
"antd-mobile": "5.10.4"
} }
} }
...@@ -24,9 +24,13 @@ path: / ...@@ -24,9 +24,13 @@ path: /
<code src="./demos/index.js"> <code src="./demos/index.js">
## 移动端
<code src="./demos/mobile.js">
## 多图表 ## 多图表
<code src="./demos/GridDemo.js"> [//]: # (<code src="./demos/GridDemo.js">)
## API ## API
......
...@@ -32,7 +32,7 @@ const SimgleChart = memo((props) => { ...@@ -32,7 +32,7 @@ const SimgleChart = memo((props) => {
showBoxOption, showBoxOption,
}; };
return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config); return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config);
}, [dataSource, smooth, curveCenter]); }, [dataSource, smooth, curveCenter,chartType]);
useEffect(() => { useEffect(() => {
chartRef.current?.resize?.(); chartRef.current?.resize?.();
......
...@@ -13,6 +13,7 @@ const deviceParams = [ ...@@ -13,6 +13,7 @@ const deviceParams = [
// deviceCode: 'EGBF00000002', // deviceCode: 'EGBF00000002',
// deviceCode: 'EGBF00000018', // deviceCode: 'EGBF00000018',
deviceCode: 'XMYL00000345', deviceCode: 'XMYL00000345',
// deviceCode: 'XMYL00000000',
// deviceCode: 'EGBF00000014', // deviceCode: 'EGBF00000014',
// sensors: '今日供水量,今日用电量,1#水箱液位,是否在线', // sensors: '今日供水量,今日用电量,1#水箱液位,是否在线',
sensors: '进水压力', sensors: '进水压力',
...@@ -32,9 +33,6 @@ const Demo = () => { ...@@ -32,9 +33,6 @@ const Demo = () => {
<div style={{height: 700}}> <div style={{height: 700}}>
<HistoryView deviceParams={deviceParams} defaultModel="curve"/> <HistoryView deviceParams={deviceParams} defaultModel="curve"/>
</div> </div>
<div style={{height: 300}}>
<MobileHistoryChart deviceParams={deviceParams} chartType={'boxChart'}/>
</div>
</div> </div>
); );
......
import React from 'react';
import {MobileHistoryChart} from "../mobile";
const deviceParams = [
/* {
deviceCode: 'EGBF00000141',
sensors: '今日供水量,今日用电量',
deviceType: '二供泵房',
pointAddressID: 208,
}, */
{
deviceCode: 'EGBF00000141',
sensors: '今日供水量',
deviceType: '二供泵房',
pointAddressID: 208,
},
];
const Demo = () => {
return (
<div>
<div style={{height: 400}}>
<MobileHistoryChart deviceParams={deviceParams}/>
</div>
</div>
);
};
export default Demo;
...@@ -199,3 +199,9 @@ ...@@ -199,3 +199,9 @@
opacity: 1; opacity: 1;
} }
} }
.@{history-view}-item {
display: flex;
align-items: center;
margin-bottom: 10px;
}
import React, {useEffect, useMemo, useState, useContext} from 'react'; import React, {useEffect, useMemo, useState, useContext} from 'react';
import GridChart from "./GridChart";
import {ConfigProvider} from "antd"; import {ConfigProvider} from "antd";
import {Selector, Checkbox, Space} from 'antd-mobile/es';
import moment from "moment"; import moment from "moment";
import {getDeviceAlarmScheme, getHistoryInfo} from "./apis"; import {getDeviceAlarmScheme, getHistoryInfo} from "./apis";
import SimgleChart from "./SingleChart"; import SimgleChart from "./SingleChart";
import {handlePx} from "./utils";
import styles from './index.less';
// deviceAlarmSchemes 用来获取对应的 方案的最大值/最小值 标记状态 const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
// dataSource 获取的报警信息
// deviceParams,
// defaultDate,
// {
// deviceCode: 'XMYL00000345',
// sensors: '进水压力',
// deviceType: '熊猫压力表',
// pointAddressID: 4,
// },
//{
// "isDilute": true, // 抽稀
// "zoom": "30",// 抽稀
// "unit": "min",// 抽稀
// "ignoreOutliers": false, // 滤波
// "dateFrom": "2023-07-09 16:38:32",
// "dateTo": "2023-07-10 16:38:32",
// "acrossTables": [
// {
// "deviceCode": "XMYL00000297",
// "sensors": "进水压力,是否在线",
// "deviceType": "熊猫压力表"
// }
// ],
// "isBoxPlots": true // 箱线图
// }
const DATE_FORMAT = 'YYYY-MM-DD';
const MobileHistoryChart = ( const MobileHistoryChart = (
{ {
date = { date = {
dateFrom: moment().format(`${DATE_FORMAT} 00:00:00`), dateFrom: moment().subtract(1, 'days').format(`${DATE_FORMAT}`),
dateTo: moment().format(`${DATE_FORMAT} 23:59:59`) dateTo: moment().format(`${DATE_FORMAT}`)
},// 默认当天 },// 默认当天
deviceParams = [], // 设备参数,必传 deviceParams = [], // 设备参数,必传
chartType='lineChart', // lineChart boxChart chartType = 'lineChart', // lineChart boxChart
ignoreOutliers = true, // 滤波 // ignoreOutliers = true, // 滤波
isDilute = true, // 抽稀去重 isDilute = true, // 抽稀去重
needMarkLine = true, needMarkLine = true,
showBoxOption = true, // 开启箱线图配置,默认开启 showBoxOption = true, // 开启箱线图配置,默认开启
chartGrid = true, // 开启网格 chartGrid = true, // 开启网格
...rest
} }
) => { ) => {
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState(null); const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState(null);
const [chartDataSource, setChartDataSource] = useState(null); const [chartDataSource, setChartDataSource] = useState(null);
const [options, setOptions] = useState({
curveCenter: true,
ignoreOutliers: true
});
const checkBoxOptions = useMemo(() => {
return Object.keys(options).reduce((final, cur) => {
if (options[cur]) final.push(cur);
return final
}, []);
}, [options]);
const [lineType, setLineType] = useState([chartType]);
const {getPrefixCls} = useContext(ConfigProvider.ConfigContext); const {getPrefixCls} = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('history-view'); const prefixCls = getPrefixCls('history-view');
const isBoxPlots = const isBoxPlots =
...@@ -87,7 +72,7 @@ const MobileHistoryChart = ( ...@@ -87,7 +72,7 @@ const MobileHistoryChart = (
let _params = { let _params = {
...date, ...date,
isBoxPlots, isBoxPlots,
ignoreOutliers, ignoreOutliers: options.ignoreOutliers,
isDilute, isDilute,
...thinKey, ...thinKey,
acrossTables acrossTables
...@@ -139,20 +124,57 @@ const MobileHistoryChart = ( ...@@ -139,20 +124,57 @@ const MobileHistoryChart = (
}); });
} }
useEffect(() => { useEffect(() => {
getDataSource();
getScheme(); getScheme();
}, []) }, [])
return deviceAlarmSchemes && chartDataSource ? <SimgleChart useEffect(() => {
showBoxOption={showBoxOption} getDataSource();
curveCenter={true} }, [options.ignoreOutliers])
chartGrid={chartGrid} return deviceAlarmSchemes && chartDataSource ? <div style={{}}>
prefixCls={prefixCls} {
dataSource={chartDataSource} chartDataSource.length === 1 ? <div className={styles[`${prefixCls}-item`]}>
chartType={isBoxPlots ? chartType : null} <span style={{fontSize: handlePx(16)}}>曲线形态:</span>
deviceAlarmSchemes={deviceAlarmSchemes} <Selector
/> : null options={[
{label: '线形图', value: 'lineChart'},
{label: '箱线图', value: 'boxChart'}
]}
defaultValue={lineType}
onChange={(arr, extend) => setLineType(arr)}
/>
</div> : ''
}
<div className={styles[`${prefixCls}-item`]}>
<span style={{fontSize: handlePx(16)}}>曲线设置:</span>
<Checkbox.Group
value={checkBoxOptions}
onChange={val => {
let _arr = Object.keys(options);
let _options = _arr.reduce((final, cur) => {
final[cur] = !!val.includes(cur);
return final;
}, {})
setOptions(_options);
}}
>
<Space direction='horizontal'>
<Checkbox value='curveCenter'>曲线居中</Checkbox>
<Checkbox value='ignoreOutliers'>滤波</Checkbox>
</Space>
</Checkbox.Group>
</div>
<div style={{height: rest.height || '10rem', width: rest.width || '100%'}}>
<SimgleChart
showBoxOption={showBoxOption}
curveCenter={options.curveCenter}
showGridLine={chartGrid}
prefixCls={prefixCls}
dataSource={chartDataSource}
chartType={isBoxPlots ? lineType[0] : null}
deviceAlarmSchemes={deviceAlarmSchemes}
/>
</div>
</div> : null
} }
export { export {
MobileHistoryChart MobileHistoryChart
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ import moment from 'moment'; ...@@ -2,7 +2,7 @@ import moment from 'moment';
import _, {isArray} from 'lodash'; import _, {isArray} from 'lodash';
/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */ /** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
const axisWidth = 40; const AXIS_WIDTH = 40;
const COLOR = { const COLOR = {
NORMAL: '#1685FF', NORMAL: '#1685FF',
UPER: '#fa8c16', UPER: '#fa8c16',
...@@ -13,6 +13,7 @@ const COLOR = { ...@@ -13,6 +13,7 @@ const COLOR = {
LOWLOWER: '#FF0000', LOWLOWER: '#FF0000',
AVG: '#00B8B1', AVG: '#00B8B1',
}; };
const isMobile = () => { const isMobile = () => {
const userAgent = navigator.userAgent.toLowerCase(); const userAgent = navigator.userAgent.toLowerCase();
if ( if (
...@@ -23,7 +24,42 @@ const isMobile = () => { ...@@ -23,7 +24,42 @@ const isMobile = () => {
return true; return true;
} }
return false; return false;
};
const returnRem = (num, base = 375) => num * (base / 375);
export const handlePx = (num, unit = '') => {
const _isMobile = isMobile();
const _base = document.body.clientWidth;
let _num = _isMobile ? `${returnRem(num, _base)}` : `${num}`;
return unit ? `${_num}${unit}` : Number(_num);
};
const PC_OPTION = {
markPoint: {
padding: [2, 12],
lineHeight: 22,
backgroundColor:
window.globalConfig &&
window.globalConfig &&
window.globalConfig.variableTheme?.primaryColor
? window.globalConfig.variableTheme.primaryColor
: '#0087F7',
borderWidth: 1
},
fontSize: 16,
fontColor: "#ffffff",
dataZoomHeight: 28
}
const MOBILE_OPTION = {
markPoint: {
padding: [2, 6],
lineHeight: 18,
backgroundColor: 'rgba(255,255,255,0.6)',
borderWidth: 0
},
fontSize: handlePx(12),
fontColor: "#0087F7",
dataZoomHeight: 20
} }
const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
/** /**
* 图表系列名称格式化 * 图表系列名称格式化
* *
...@@ -149,6 +185,7 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => { ...@@ -149,6 +185,7 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
}; };
export const minMaxMarkPoint = (dataItem, index, dataSource) => { export const minMaxMarkPoint = (dataItem, index, dataSource) => {
const _isMobile = isMobile();
// 只有一个数据曲线时显示markline // 只有一个数据曲线时显示markline
if (!dataItem || dataSource.length !== 1) return {}; if (!dataItem || dataSource.length !== 1) return {};
const data = []; const data = [];
...@@ -159,26 +196,18 @@ export const minMaxMarkPoint = (dataItem, index, dataSource) => { ...@@ -159,26 +196,18 @@ export const minMaxMarkPoint = (dataItem, index, dataSource) => {
symbolOffset: [0, '50%'], symbolOffset: [0, '50%'],
label: { label: {
formatter: '{b|{b} }{c|{c}}', formatter: '{b|{b} }{c|{c}}',
backgroundColor: ...currentOption["markPoint"],
window.globalConfig &&
window.globalConfig &&
window.globalConfig.variableTheme?.primaryColor
? window.globalConfig.variableTheme.primaryColor
: '#0087F7',
borderColor: '#ccc', borderColor: '#ccc',
borderWidth: 1,
borderRadius: 4, borderRadius: 4,
padding: [2, 10],
lineHeight: 22,
position: 'top', position: 'top',
distance: 10, distance: 10,
rich: { rich: {
b: { b: {
color: '#fff', color: currentOption['fontColor'],
}, },
c: { c: {
color: '#fff', color: currentOption['fontColor'],
fontSize: 16, fontSize: currentOption["fontSize"],
fontWeight: 700, fontWeight: 700,
}, },
}, },
...@@ -252,162 +281,90 @@ export const offlineArea = (dataItem) => { ...@@ -252,162 +281,90 @@ export const offlineArea = (dataItem) => {
data: datas, data: datas,
}; };
}; };
const headTemplate = (param) => {
/** if (!param) return '';
* 图表配置项生成 const {name, axisValueLabel, axisType, axisValue} = param;
* const timeFormat = 'YYYY-MM-DD HH:mm:ss';
* @param {any} dataSource 数据源 const text =
* @param {any} cusOption 自定义属性 axisType === 'xAxis.time' ? moment(axisValue).format(timeFormat) : name || axisValueLabel;
* @param {any} contrast 是否为同期对比 return `<div style="border-bottom: 1px solid #F0F0F0; color: #808080; margin-bottom:${handlePx(5, 'px')}; padding-bottom: ${handlePx(5, 'px')};">${text}</div>`;
* @param {any} contrastOption 同期对比周期配置, day|month };
* @param {any} smooth Ture/false, 曲线/折线 const seriesTemplate = (param, unit) => {
* @param {any} config 额外配置信息 if (!param) return '';
*/ const {value, encode} = param;
const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth, config) => { // const val = value[encode.y[0]];
const needUnit = _.get(config, 'needUnit', false); const _unit = unit || 'Mpa';
const curveCenter = _.get(config, 'curveCenter', false); const color = '#008CFF';
const nameWithSensor = _.get(config, 'nameWithSensor', true); if (!isArray(value))
const showGridLine = _.get(config, 'showGridLine', true); return ` <div style="display: flex; align-items: center;">
const showMarkLine = _.get(config, 'showMarkLine', false); <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${param.seriesName}</span>
const showPoint = _.get(config, 'showPoint', false); <span style="display:inline-block;">:</span>
const deviceAlarmSchemes = _.get(config, 'deviceAlarmSchemes', []); <span style="color:${color};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value?.toFixed(3) ?? '-'}</span>
const chartType = _.get(config, 'chartType', null); <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
// const justLine = _.get(config, 'justLine', false);
const showBoxOption = _.get(config, 'showBoxOption', false);
// 自定义属性
const restOption = _.pick(cusOption, ['title', 'legend']);
// 一种指标一个y轴
const yAxisMap = new Map();
dataSource.forEach((item, index) => {
const {sensorName, unit} = item;
const key = sensorName;
if (!yAxisMap.has(key)) {
const i = yAxisMap.size;
const axis = {
type: 'value',
name: needUnit ? unit : null,
position: i % 2 === 0 ? 'left' : 'right',
offset: Math.floor(i / 2) * axisWidth,
axisLabel: {
formatter: (value) => (value > 100000 ? `${value / 1000}k` : value),
},
axisLine: {
show: true,
},
nameTextStyle: {
align: i % 2 === 0 ? 'right' : 'left',
},
minorTick: {
lineStyle: {
color: '#e2e2e2',
},
},
minorSplitLine: {
lineStyle: {
color: '#e2e2e2',
type: 'dashed',
},
},
};
yAxisMap.set(key, axis);
}
// 曲线居中
if (curveCenter && item.dataModel && item.dataModel.length > 0) {
const [min, max] = minMax(item);
const axis = yAxisMap.get(key);
axis.min = axis.min === void 0 ? min : Math.min(min, axis.min);
axis.max = axis.max === void 0 ? max : Math.max(max, axis.max);
}
// 网格显示
const axis = yAxisMap.get(key);
decorateAxisGridLine(axis, showGridLine);
});
const yAxis = yAxisMap.size > 0 ? [...yAxisMap.values()] : {type: 'value'};
// 根据y轴个数调整边距
const leftNum = Math.ceil(yAxisMap.size / 2);
const rightNum = Math.floor(yAxisMap.size / 2);
const grid = {
top: needUnit ? 80 : 60,
left: 10 + leftNum * axisWidth,
right: rightNum === 0 ? 20 : rightNum * axisWidth,
bottom: 60,
};
const headTemplate = (param) => {
if (!param) return '';
const {name, axisValueLabel, axisType, axisValue} = param;
const timeFormat = 'YYYY-MM-DD HH:mm:ss';
const text =
axisType === 'xAxis.time' ? moment(axisValue).format(timeFormat) : name || axisValueLabel;
return `<div style="border-bottom: 1px solid #F0F0F0; color: #808080; margin-bottom: 5px; padding-bottom: 5px;">${text}</div>`;
};
const seriesTemplate = (param, unit) => {
if (!param) return '';
const {value, encode} = param;
// const val = value[encode.y[0]];
const _unit = unit || 'Mpa';
const color = '#008CFF';
if (!isArray(value))
return ` <div style="display: flex; align-items: center;">
<span>${isMobile() ? '实际值' : param.seriesName}</span><span style="display:inline-block;">:</span>
<span style="color:${color};margin: 0 5px 0 auto;">${value?.toFixed(3) ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span>
</div>`; </div>`;
return param.componentSubType !== 'candlestick' return param.componentSubType !== 'candlestick'
? `<div style="display: flex; align-items: center;"> ? `<div style="display: flex; align-items: center;">
<span>${isMobile() ? '实际值' : param.seriesName}</span><span style="display:inline-block;">:</span> <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${param.seriesName}</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${value[1] ?? '-'}</span> <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div>` </div>`
: ` : `
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>首值</span><span style="display:inline-block;">:</span> <span>首值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${value[1] ?? '-'}</span> <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>尾值</span><span style="display:inline-block;">:</span> <span>尾值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${value[2] ?? '-'}</span> <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[2] ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>最小值</span><span style="display:inline-block;">:</span> <span>最小值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${value[3] ?? '-'}</span> <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[3] ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>最大值</span><span style="display:inline-block;">:</span> <span>最大值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${value[4] ?? '-'}</span> <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[4] ?? '-'}</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
`; `;
}; };
const tooltipAccessor = (unit) => { const tooltipAccessor = (unit) => {
return { return {
formatter: function (params, ticket, callback) { formatter: function (params, ticket, callback) {
let tooltipHeader = ''; let tooltipHeader = '';
let tooltipContent = ''; let tooltipContent = '';
if (isArray(params)) { if (isArray(params)) {
tooltipHeader = headTemplate(params[0]); tooltipHeader = headTemplate(params[0]);
params.forEach((param) => { params.forEach((param) => {
tooltipContent += seriesTemplate(param, unit); tooltipContent += seriesTemplate(param, unit);
}); });
} else { } else {
tooltipHeader = headTemplate(params); tooltipHeader = headTemplate(params);
tooltipContent += seriesTemplate(params, unit); tooltipContent += seriesTemplate(params, unit);
} }
return ` return `
<div> <div>
${tooltipHeader} ${tooltipHeader}
<div>${tooltipContent}</div> <div>${tooltipContent}</div>
</div> </div>
`; `;
}, },
};
}; };
};
const returnXAxis = ({
dataSource,
contrast,
contrastOption,
nameWithSensor,
showMarkLine,
deviceAlarmSchemes,
showPoint,
restOption,
smooth
}) => {
// 根据"指标名称"分类yAxis // 根据"指标名称"分类yAxis
const yAxisInterator = (() => { const yAxisInterator = (() => {
const map = new Map(); const map = new Map();
...@@ -475,21 +432,177 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -475,21 +432,177 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
.map((item) => item.data?.[item.data.length - 1]?.[0]) .map((item) => item.data?.[item.data.length - 1]?.[0])
.filter((item) => item !== undefined), .filter((item) => item !== undefined),
); );
let xAxis = {type: 'time', min, max}; return {xAxis: {type: 'time', min, max}, series};
}
const handleDefault = (config, cusOption) => {
const needUnit = _.get(config, 'needUnit', false);
const curveCenter = _.get(config, 'curveCenter', false);
const nameWithSensor = _.get(config, 'nameWithSensor', true);
const showGridLine = _.get(config, 'showGridLine', true);
const showMarkLine = _.get(config, 'showMarkLine', false);
const showPoint = _.get(config, 'showPoint', false);
const deviceAlarmSchemes = _.get(config, 'deviceAlarmSchemes', []);
const chartType = _.get(config, 'chartType', null);
// const justLine = _.get(config, 'justLine', false);
const showBoxOption = _.get(config, 'showBoxOption', false);
// 自定义属性
const restOption = _.pick(cusOption, ['title', 'legend']);
return {
needUnit,
curveCenter,
nameWithSensor,
showGridLine,
showMarkLine,
showPoint,
deviceAlarmSchemes,
chartType,
showBoxOption,
restOption
}
}
const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
// 一种指标一个y轴
const yAxisMap = new Map();
dataSource.forEach((item, index) => {
const {sensorName, unit} = item;
const key = sensorName;
if (!yAxisMap.has(key)) {
const i = yAxisMap.size;
const axis = {
type: 'value',
name: needUnit ? unit : null,
position: i % 2 === 0 ? 'left' : 'right',
offset: Math.floor(i / 2) * AXIS_WIDTH,
axisLabel: {
formatter: (value) => (value > 100000 ? `${value / 1000}k` : value),
},
axisLine: {
show: true,
},
nameTextStyle: {
align: i % 2 === 0 ? 'right' : 'left',
},
minorTick: {
lineStyle: {
color: '#e2e2e2',
},
},
minorSplitLine: {
lineStyle: {
color: '#e2e2e2',
type: 'dashed',
},
},
};
yAxisMap.set(key, axis);
}
// 曲线居中
if (curveCenter && item.dataModel && item.dataModel.length > 0) {
const [min, max] = minMax(item);
const axis = yAxisMap.get(key);
axis.min = axis.min === void 0 ? min : Math.min(min, axis.min);
axis.max = axis.max === void 0 ? max : Math.max(max, axis.max);
}
// 网格显示
const axis = yAxisMap.get(key);
decorateAxisGridLine(axis, showGridLine);
});
const yAxis = yAxisMap.size > 0 ? [...yAxisMap.values()] : {type: 'value'};
// 根据y轴个数调整边距
const leftNum = Math.ceil(yAxisMap.size / 2);
const rightNum = Math.floor(yAxisMap.size / 2);
return {leftNum, rightNum, yAxis};
}
const assignOptions = (restOption, xAxis, legendData) => {
restOption.dataZoom = [
{
show: true,
bottom: 10,
start: 0,
end: 100,
height: currentOption['dataZoomHeight'],
type: 'inside',
zoomOnMouseWheel: true,
},
{
show: true,
bottom: 10,
start: 0,
end: 100,
height: currentOption['dataZoomHeight'],
type: 'slider',
zoomOnMouseWheel: true,
},
];
xAxis.minInterval = 3600 * (1 * 1000);
if (legendData) {
restOption.legend = {
// orient: 'vertical',
data: legendData,
itemGap: 10,
padding: [0, 0, 0, 200],
textStyle: {
width: 120,
overflow: 'truncate',
},
}
}
};
/**
* 图表配置项生成
*
* @param {any} dataSource 数据源
* @param {any} cusOption 自定义属性
* @param {any} contrast 是否为同期对比
* @param {any} contrastOption 同期对比周期配置, day|month
* @param {any} smooth Ture/false, 曲线/折线
* @param {any} config 额外配置信息
*/
const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth, config) => {
const {
needUnit,
curveCenter,
nameWithSensor,
showGridLine,
showMarkLine,
showPoint,
deviceAlarmSchemes,
chartType,
showBoxOption,
restOption
} = handleDefault(config, cusOption);
const {leftNum, rightNum, yAxis} = handleYAxis({dataSource, needUnit, curveCenter, showGridLine});
const grid = {
top: needUnit ? 80 : 60,
// top: 200,
left: 10 + leftNum * AXIS_WIDTH,
right: rightNum === 0 ? 20 : rightNum * AXIS_WIDTH,
bottom: 60,
};
let {xAxis, series} = returnXAxis({
dataSource,
contrast,
contrastOption,
nameWithSensor,
showMarkLine,
deviceAlarmSchemes,
showPoint,
smooth,
restOption
});
decorateAxisGridLine(xAxis, showGridLine); decorateAxisGridLine(xAxis, showGridLine);
const tooltipTimeFormat = !contrast const tooltipTimeFormat = !contrast
? 'YYYY-MM-DD HH:mm:ss' ? 'YYYY-MM-DD HH:mm:ss'
: contrastOption === 'day' : contrastOption === 'day'
? 'HH:mm' ? 'HH:mm'
: 'DD HH:mm'; : 'DD HH:mm';
let tooltip = { let tooltip = {};
timeFormat: tooltipTimeFormat, // 增加箱线图的逻辑,单曲线才存在该逻辑
// trigger: 'axis',
// axisPointer: {
// type: 'cross'
// }
};
// 增加箱线图的逻辑,单曲线才存在
if (chartType && showBoxOption) { if (chartType && showBoxOption) {
if (chartType === 'boxChart') { if (chartType === 'boxChart') {
const otherData = const otherData =
...@@ -497,17 +610,12 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -497,17 +610,12 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
const {firstPV, lastPV, maxPV, minPV, pt} = item; const {firstPV, lastPV, maxPV, minPV, pt} = item;
return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV]; return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV];
}) || []; //当存在othersData的时候,只是单曲线 }) || []; //当存在othersData的时候,只是单曲线
// xAxis = {type: 'category', data: series[0].data.map(item => moment(item[0]).format('YYYY-MM-DD HH:mm:ss'))};
xAxis = {type: 'time'}; xAxis = {type: 'time'};
decorateAxisGridLine(xAxis, showGridLine); decorateAxisGridLine(xAxis, showGridLine);
let unit = ''; let unit = '';
series = series.map((item) => { series = series.map((item) => {
if (item.unit) unit = item.unit; if (item.unit) unit = item.unit;
let _item = {...item, symbol: 'none'}; return {...item, symbol: 'none'};
/* _item.data = _item?.data?.map(d => {
return d[1] || null
}) || [];*/
return _item;
}); });
series.push({ series.push({
type: 'candlestick', type: 'candlestick',
...@@ -522,7 +630,8 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -522,7 +630,8 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
}, },
}); });
tooltip = tooltipAccessor(unit); tooltip = tooltipAccessor(unit);
} else { }
if (chartType === 'lineChart') {
let _maxData = []; let _maxData = [];
let _minData = []; let _minData = [];
let _currentYear = moment().format('YYYY'); let _currentYear = moment().format('YYYY');
...@@ -572,55 +681,40 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -572,55 +681,40 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
${headTemplate(e[0])} ${headTemplate(e[0])}
<div> <div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>${ <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${
isMobile() ? '当前值' : e[0].seriesName e[0].seriesName
}</span><span style="display:inline-block;">:</span> }</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.NORMAL};margin: 0 5px 0 auto;">${ <span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
e[0]?.value?.[1] ?? '-' e[0]?.value?.[1] ?? '-'
}</span> }</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>最小值</span><span style="display:inline-block;">:</span> <span>最小值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${ <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
e[1]?.value?.[1] ?? '-' e[1]?.value?.[1] ?? '-'
}</span> }</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<span>最大值</span><span style="display:inline-block;">:</span> <span>最大值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 5px 0 auto;">${ <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
_maxValues[e[2].dataIndex] ?? '-' _maxValues[e[2].dataIndex] ?? '-'
}</span> }</span>
<span style="font-size: 12px;">${_unit}</span> <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
</div> </div>
</div> </div>
</div>`; </div>`;
}, },
}; };
} }
} else {
tooltip = tooltipAccessor();
} }
restOption.dataZoom = [ tooltip.timeFormat = tooltipTimeFormat;
{ let _legendData = series.filter(item => !['最大值', '最小值'].includes(item.name)).map(item => item.name);
show: true, assignOptions(restOption, xAxis, _legendData);
bottom: 10, debugger
start: 0,
end: 100,
height: 28,
type: 'inside',
zoomOnMouseWheel: true,
},
{
show: true,
bottom: 10,
start: 0,
end: 100,
height: 28,
type: 'slider',
zoomOnMouseWheel: true,
},
];
xAxis.minInterval = 3600 * 1 * 1000;
return { return {
yAxis, yAxis,
grid, grid,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment