utils.js 50.2 KB
Newer Older
1
import moment from 'moment';
2
import _, {isArray} from 'lodash';
陈龙's avatar
陈龙 committed
3 4
import maxIcon from './assets/最大实心.svg';
import minIcon from './assets/最小实心.svg';
5
import minIconDownArrow from './assets/最小实心箭头朝下.svg';
陈龙's avatar
陈龙 committed
6
import lineChart from '@wisdom-components/basicchart/es/LineChart';
7
import * as echarts from 'echarts';
8

9
/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
陈龙's avatar
陈龙 committed
10
const COLOR = {
11 12 13 14 15 16 17 18
    NORMAL: '#1685FF',
    UPER: '#fa8c16',
    UPUPER: '#FF0000',
    // LOWER: '#13c2c2',
    // LOWLOWER: '#2f54eb',
    LOWER: '#fa8c16',
    LOWLOWER: '#FF0000',
    AVG: '#00B8B1',
陈龙's avatar
陈龙 committed
19
};
陈龙's avatar
陈龙 committed
20

陈龙's avatar
陈龙 committed
21
const isMobile = () => {
22 23 24 25 26
    const userAgent = navigator.userAgent.toLowerCase();
    if (/ipad|iphone|midp|rv:1.2.3.4|ucweb|android|windows ce|windows mobile/.test(userAgent)) {
        return true;
    }
    return false;
陈龙's avatar
陈龙 committed
27 28 29
};
const returnRem = (num, base = 375) => num * (base / 375);
export const handlePx = (num, unit = '') => {
30 31 32 33
    const _isMobile = isMobile();
    const _base = document.body.clientWidth;
    let _num = _isMobile ? `${returnRem(num, _base)}` : `${num}`;
    return unit ? `${_num}${unit}` : Number(_num);
陈龙's avatar
陈龙 committed
34 35
};
const PC_OPTION = {
36 37 38 39 40 41 42 43 44
    // markPoint: {
    //     padding: [2, 12],
    //     lineHeight: 22,
    //     backgroundColor:
    //         window.globalConfig && window.globalConfig && window.globalConfig.variableTheme?.primaryColor
    //             ? window.globalConfig.variableTheme.primaryColor
    //             : '#0087F7',
    //     borderWidth: 1,
    // },
45 46 47
    fontSize: 16,
    fontColor: '#ffffff',
    dataZoomHeight: 28,
陈龙's avatar
陈龙 committed
48
};
陈龙's avatar
陈龙 committed
49
const MOBILE_OPTION = {
50 51 52 53 54 55
    // markPoint: {
    //     padding: [2, 6],
    //     lineHeight: 18,
    //     backgroundColor: 'rgba(255,255,255,0.6)',
    //     borderWidth: 0,
    // },
56 57 58
    fontSize: handlePx(12),
    fontColor: '#0087F7',
    dataZoomHeight: 20,
陈龙's avatar
陈龙 committed
59
};
陈龙's avatar
陈龙 committed
60
const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
61 62
/**
 * 图表系列名称格式化
63 64
 *
 * @param {any} data
65
 * @param {boolean} contrast 是否为同期对比
66
 * @param {any} contrastOption 同期对比周期配置, day|month
67 68
 * @param nameWithSensor
 * @param {boolean} isSingle 是否是单设备,单设备的名称不带设备名
69
 * @returns
70
 */
71
const nameFormatter = (data, contrast, contrastOption, nameWithSensor, isSingle) => {
72
    const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data;
73
    let name = nameWithSensor ? (isSingle ? `${sensorName}` : `${equipmentName}-${sensorName}`) : equipmentName;
74 75 76 77 78
    if (contrast) {
        const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, '');
        name = `${name}-${time}`;
    }
    return name;
79 80 81 82
};

/**
 * 图表系列数据格式化
83 84
 *
 * @param {any} data
85
 * @param {boolean} contrast 是否为同期对比
86
 * @param {any} contrastOption 同期对比周期配置, day|month
87 88 89
 * @returns 图表系列数据, [[DateTime, value]]
 */
const dataAccessor = (data, contrast, contrastOption) => {
90 91 92 93
    const dataModel = data?.dataModel ?? [];
    const formatStr = contrastOption === 'day' ? '2020-01-01 HH:mm:ss' : '2020-01-DD HH:mm:ss';
    return dataModel
        .filter((item) => item.sensorName !== '是否在线')
94
        .map((item, index) => {
95
            const time = contrast ? moment(item.pt).format(formatStr) : item.pt;
96 97 98 99 100 101
            let _v = item.pv;
            // if (index === 0) _v = _v * 10000 * 10000 * 10000;
            // if (index === dataModel.lastIndex) _v = _v / (10000 * 10000 * 10000);
            // if (index === 0) _v = _v * 2;
            // if (index === dataModel.lastIndex) _v = _v / 2;
            return [moment(time).valueOf(), _v];
102
        });
103 104 105 106
};

/**
 * 面积图配置(目前默认曲线图)
107 108 109
 *
 * @param {any} data 数据项
 * @returns Null/areaStyle, 为null显示曲线图, 为areaStyle对象显示为面积图.
110 111
 */
const areaStyleFormatter = (data) => {
112 113
    const {sensorName} = data;
    return sensorName && sensorName.indexOf('流量') > -1 ? {} : null;
114 115
};

116 117 118 119 120 121 122
/**
 * 数据项中指标值最小最大值
 *
 * @param {any} data 数据项
 * @returns
 */
const minMax = (data) => {
123 124 125 126 127 128 129 130
    const {dataModel} = data;
    let min = Number.MAX_SAFE_INTEGER;
    let max = Number.MIN_SAFE_INTEGER;
    dataModel.forEach((item) => {
        min = Math.min(min, item.pv ?? 0);
        max = Math.max(max, item.pv ?? 0);
    });
    return [min, max];
131
};
132

133
const markLineItem = (name, value, color) => {
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    return {
        name: name,
        yAxis: value,
        value: value,
        lineStyle: {
            color: color || '#000',
        },
        label: {
            position: 'insideEndTop',
            color: color || '#000',
            formatter: function () {
                return `${name}:${value}`;
            },
        },
    };
149 150 151
};

export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    // 只有一个数据曲线时显示markline
    if (!dataItem || !schemes || dataSource.length !== 1) return {};
    const {deviceType, stationCode, sensorName, decimalPoint} = dataItem;
    const curSchemes = schemes.filter(
        (item) =>
            item.deviceCode === stationCode &&
            item.sensorName === sensorName &&
            item.valueType === '直接取值',
    );
    const data = [];
    curSchemes.forEach((scheme) => {
        const {hLimit, hhLimit, lLimit, llLimit} = scheme;
        lLimit !== null && lLimit !== void 0 && data.push(markLineItem('低限', lLimit, '#fa8c16'));
        hLimit !== null && hLimit !== void 0 && data.push(markLineItem('高限', hLimit, '#fa8c16'));
        llLimit !== null && llLimit !== void 0 && data.push(markLineItem('低低限', llLimit, '#FF0000'));
        hhLimit !== null && hhLimit !== void 0 && data.push(markLineItem('高高限', hhLimit, '#FF0000'));
    });
    data.push({
        name: '平均线',
        type: 'average',
        lineStyle: {
            color: '#00b8b1',
            type: 'solid',
        },
        label: {
            position: 'insideEndTop',
            color: '#00b8b1',
            formatter: function (param) {
                return `平均值:${param.value}`;
            },
        },
    });
    return {
        symbol: ['none', 'none'],
        data,
    };
188
};
189

190 191 192 193
/**
 * 坐标轴添加网格线配置
 *
 * @param {any} axis
194 195
 * @param showGrid
 * @param theme
196
 */
197
export const decorateAxisGridLine = (axis, showGrid, theme) => {
198 199 200
    if (!axis) return;
    axis.minorTick = {
        ...(axis.minorTick || {}),
201 202 203 204 205
        ...(theme === 'BI' ? {
            lineStyle: {
                ...(theme === 'BI' ? {color: 'rgba(56,67,90,0.81)'} : {})
            },
        } : {}),
206 207 208 209 210
        show: showGrid,
        splitNumber: 2,
    };
    axis.minorSplitLine = {
        ...(axis.minorSplitLine || {}),
211 212 213 214 215 216
        ...(theme === 'BI' ? {
            lineStyle: {
                ...(theme === 'BI' ? {color: 'rgba(56,67,90,0.81)'} : {})
            },
        } : {}),
        show: false,
217 218 219 220
    };
    axis.splitLine = {
        ...(axis.splitLine || {}),
        show: showGrid,
221 222 223
        lineStyle: {
            ...(theme === 'BI' ? {color: 'rgba(56,67,90,0.81)'} : {})
        },
224
    };
225 226
};

陈龙's avatar
陈龙 committed
227
// tooltip 模板
228
const headTemplate = (param, opt) => {
229 230 231 232 233 234 235 236 237 238 239 240 241 242
    if (!param) return '';
    const {name, axisValueLabel, axisType, axisValue} = param;
    const timeFormat =
        opt && opt.contrast
            ? opt.contrastOption === 'day'
                ? 'HH:mm:ss'
                : 'DD日HH时'
            : '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:${handlePx(
        5,
        'px',
    )}; padding-bottom: ${handlePx(5, 'px')};">${text}</div>`;
陈龙's avatar
陈龙 committed
243 244
};
const seriesTemplate = (param, unit) => {
245 246 247 248 249 250 251
    if (!param || param.seriesName === '自定义') return '';
    const {value, encode} = param;
    // const val = value[encode.y[0]];
    const _unit = unit || '';
    const color = '#008CFF';
    if (!isArray(value))
        return ` <div style="display: flex; align-items: center;">
252
            <span style="${
253
            isMobile()
254
                ? 'width: ' +
255 256
                handlePx(90, 'px') +
                ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
257
                : ''
258
        }">${param.seriesName}</span>
陈龙's avatar
陈龙 committed
259
            <span style="display:inline-block;">:</span>
260
            <span style="color:${color};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
261 262
            value?.toFixed(3) ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
263
            <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
264
        </div>`;
265 266
    return param.componentSubType !== 'candlestick'
        ? `<div style="display: flex; align-items: center;">
267
              <span style="${
268 269 270 271 272 273
            isMobile()
                ? 'width: ' +
                handlePx(90, 'px') +
                ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
                : ''
        }">${param.seriesName}</span><span style="display:inline-block;">:</span>
274
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
275 276
            value[1] ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
277
              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
程恺文's avatar
程恺文 committed
278
            </div>`
279
        : `
陈龙's avatar
陈龙 committed
280 281
            <div style="display: flex; align-items: center;">
              <span>首值</span><span style="display:inline-block;">:</span>
282
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
283 284
            value[1] ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
285
              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
286 287 288
            </div>
            <div style="display: flex; align-items: center;">
              <span>尾值</span><span style="display:inline-block;">:</span>
289
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
290 291
            value[2] ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
292
              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
293 294
            </div>
            <div style="display: flex; align-items: center;">
295
              <span>周期最小值</span><span style="display:inline-block;">:</span>
296
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
297 298
            value[3] ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
299
              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
300 301
            </div>
            <div style="display: flex; align-items: center;">
302
              <span>周期最大值</span><span style="display:inline-block;">:</span>
303
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
304 305
            value[4] ?? '-'
        }</span>
陈龙's avatar
陈龙 committed
306
              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
307 308
            </div>
          `;
陈龙's avatar
陈龙 committed
309
};
310
const tooltipAccessor = (unit, contrastOption) => {
311 312 313 314 315 316 317 318 319 320 321 322 323 324
    return {
        formatter: function (params, ticket, callback) {
            let tooltipHeader = '';
            let tooltipContent = '';
            if (isArray(params)) {
                tooltipHeader = headTemplate(params[0], contrastOption);
                params.forEach((param) => {
                    tooltipContent += seriesTemplate(param, unit?.[param?.seriesIndex]);
                });
            } else {
                tooltipHeader = headTemplate(params, contrastOption);
                tooltipContent += seriesTemplate(params, unit?.[params?.seriesIndex]);
            }
            return `
陈龙's avatar
陈龙 committed
325 326 327 328 329
      <div>
        ${tooltipHeader}
        <div>${tooltipContent}</div>
      </div>
    `;
330 331
        },
    };
陈龙's avatar
陈龙 committed
332
};
陈龙's avatar
陈龙 committed
333 334
// 处理特殊业务:1. 频率;2. 是否在线(暂时不上)
const handleSpecial1 = (special, dataSource) => {
335 336 337 338 339 340
    // 频率部分的业务
    const _colorMap = {
        变频: '#1685ff',
        工频: '#00d0c7',
        运行: '#1685ff',
        故障: '#ff6b37',
341
        停止: '#aeaeae',
342 343 344 345 346 347 348 349 350 351 352
    };
    let _special1 = special?.special1 ?? {};
    let _valDesc = _special1?.valDesc ?? {};
    let _data = dataSource.find((item) => item.sensorName === _special1.name);
    let _markAreaData = [];
    let _pieces =
        _data?.dataModel
            ?.reduce((final, cur, index) => {
                let _pt = moment(cur.pt).valueOf();
                let _length = final.length;
                if (_colorMap[_valDesc[cur.pv]]) {
353
                    if (index === 0) {
354 355 356 357 358 359 360 361 362 363 364 365 366
                        final.push({
                            lte: _pt,
                            gte: 0,
                            color: _colorMap[_valDesc[cur.pv]],
                            value: cur.pv,
                            text: _valDesc[cur.pv],
                        });
                    } else {
                        if (cur.pv === final[_length - 1].value) {
                            final[_length - 1].lte = _pt;
                        } else {
                            final.push({
                                lte: _pt,
367
                                gte: final[_length - 1].lte,
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
                                color: _colorMap[_valDesc[cur.pv]],
                                value: cur.pv,
                                text: _valDesc[cur.pv],
                            });
                        }
                    }
                }
                return final;
            }, [])
            .map((item) => {
                _markAreaData.push([
                    {
                        xAxis: item.gte,
                        itemStyle: {
                            // color: item.color
                            color: {
                                type: 'linear',
                                x: 0,
                                y: 0,
                                x2: 0,
                                y2: 1,
                                colorStops: [
                                    {
                                        offset: 0,
                                        color: item.color, // 0% 处的颜色
                                    },
                                    {
                                        offset: 1,
                                        color: item.color, // 100% 处的颜色
                                    },
                                ],
                                global: false, // 缺省为 false
                            },
                        },
                        name: item.text,
                        label: {show: true},
                    },
                    {
                        xAxis: item.lte,
                        itemStyle: {
                            color: item.color,
                        },
                    },
                ]);
                delete item.value;
                return item;
            }) ?? [];
    let _final = {};
    if (_markAreaData.length) {
        _final.visualMap = {
            type: 'piecewise',
            show: false,
            dimension: 0,
            seriesIndex: 0,
            pieces: _pieces,
        };
    }
    if (_pieces.length) {
        _final.markArea = {
            silent: true,
428
            itemStyle: {
429
                opacity: 0.1,
430
            },
431 432 433 434
            data: _markAreaData,
        };
    }
    return _final;
陈龙's avatar
陈龙 committed
435 436
};
// 生成x坐标
陈龙's avatar
陈龙 committed
437
const returnXAxis = ({
438 439 440 441 442 443 444 445 446 447 448
                         dataSource,
                         contrast,
                         contrastOption,
                         nameWithSensor,
                         showMarkLine,
                         deviceAlarmSchemes,
                         showPoint,
                         restOption,
                         smooth,
                         special,
                         yAxis,
449 450
                         predicateData,
                         chartType
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
                     }) => {
    // 根据"指标名称"分类yAxis
    const yAxisInterator = (() => {
        const map = new Map();
        let current = -1;
        const get = (name) => (map.has(name) ? map.get(name) : map.set(name, ++current).get(name));
        return {get};
    })();
    let _offlineData = [];
    // 生成visualMap、markArea
    let {visualMap, markArea} = handleSpecial1(special, dataSource);
    let _filterArr = ['是否在线'];
    if (special?.special1?.name) {
        _filterArr.push(special.special1.name);
    }
    // 生成series
467 468 469 470 471
    // 单设备情况下,不显示设备名称
    let deviceNumber = dataSource?.reduce((final, cur) => {
        if (!final.includes(cur.stationCode)) final.push(cur.stationCode);
        return final
    }, [])?.length;
472
    // 线图 且 有预测数据情况下,才合并预测数据
473
    let series = (predicateData && predicateData.length && chartType === 'lineChart' ? dataSource.concat(predicateData) : dataSource)
474 475 476 477 478 479 480 481
        .filter((item) => {
            if (item.sensorName === '是否在线') {
                _offlineData.push(item);
            }
            return !_filterArr.includes(item.sensorName);
        })
        .map((item, index) => {
            const {sensorName, unit} = item;
482
            const name = nameFormatter(item, contrast, contrastOption, nameWithSensor, deviceNumber === 1);
483 484 485 486 487
            const data = dataAccessor(item, contrast, contrastOption);
            const type = 'line';
            const areaStyle = areaStyleFormatter(item);
            const _index = yAxis.findIndex((item) => item.name === unit);
            const yAxisIndex = _index > -1 ? _index : 0;
488
            let markLine = showMarkLine
489 490
                ? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes)
                : {};
491 492
            // let markPoint = showPoint ? minMaxMarkPoint(item, index, dataSource) : {};
            let markPoint = {};
493
            let _lineStyle = {};
494 495 496 497 498 499 500 501 502 503 504 505
            /*            if (item.deviceType === '预测' && chartType === 'lineChart') {
                            markPoint = null;
                            markLine = null;
                            _lineStyle = {
                                itemStyle: {
                                    color: '#07a49a'
                                },
                                lineStyle: {
                                    type: 'dashed',
                                }
                            }
                        }*/
506 507 508 509 510 511 512 513 514 515 516 517
            return {
                notMerge: true,
                name,
                type,
                data,
                areaStyle,
                yAxisIndex,
                smooth,
                unit,
                markLine,
                markPoint,
                markArea,
518
                ..._lineStyle,
519 520 521
            };
        });
    // 由于series更新后,没有的数据曲线仍然停留在图表区上,导致图表可视区范围有问题
陈龙's avatar
陈龙 committed
522 523 524 525
    let minArr = series.map((item) => item.data?.[0]?.[0]).filter((item) => item !== undefined);
    let maxArr = series.map((item) => item.data?.[item.data.length - 1]?.[0]).filter((item) => item !== undefined);
    let minObj = minArr.length ? minArr : [moment(dataSource?.[0]?.dateFrom).valueOf()];
    let maxObj = maxArr.length ? maxArr : [moment(dataSource?.[0]?.dateTo).valueOf()];
526
    const min = Math.min(
陈龙's avatar
陈龙 committed
527 528
        ...minObj
    )
529
    const max = Math.max(
陈龙's avatar
陈龙 committed
530 531
        ...maxObj
    )
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    return {
        xAxis: {
            type: 'time',
            min,
            max,
            axisLabel: {
                formatter: contrast
                    ? contrastOption === 'month'
                        ? '{dd}日'
                        : '{HH}:{mm}'
                    : {
                        year: '{yyyy}',
                        month: '{MMM}',
                        day: '{d}日',
                        hour: '{HH}:{mm}',
                        minute: '{HH}:{mm}',
                        second: '{HH}:{mm}:{ss}',
                        none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss}',
                    },
551
            },
552 553 554 555
        },
        series,
        visualMap,
    };
陈龙's avatar
陈龙 committed
556
};
陈龙's avatar
陈龙 committed
557
const handleDefault = (config, cusOption) => {
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    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 showBoxOption = _.get(config, 'showBoxOption', false);
    // 自定义属性
    const restOption = _.pick(cusOption, ['title', 'legend']);
    const special = _.get(config, 'special', {});
    return {
        needUnit,
        curveCenter,
        nameWithSensor,
        showGridLine,
        showMarkLine,
        showPoint,
        deviceAlarmSchemes,
        chartType,
        showBoxOption,
        restOption,
        special,
    };
陈龙's avatar
陈龙 committed
583 584
};
const handleMaxValue = (value) => {
585
    if (value <= 1) return Number(value.toFixed(3));
586 587
    if (value >= 100000) return `${(value / 1000).toFixed(2)}k`;
    return value.toFixed(2);
588
};
589
const reduceYAxis = (arr, dataSource) => {
590 591 592 593 594 595 596
    let _offsetValue = [];
    // 1. 合并相同单位的坐标轴
    let _arr = arr.reduce((final, cur) => {
        let _key = cur.name === null ? 'null' : cur.name;
        if (!final[_key]) {
            final[_key] = cur;
        }
597
        return final;
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
    }, {});
    // 2. 合并相同单位的数据,找出最大值
    let _maxValueArr = Object.values(
        dataSource.reduce((final, cur) => {
            let _key = cur.sensorName === null ? 'null' : cur.sensorName;
            let _maxValue = cur.dataModel.reduce((final, cur) => {
                // eslint-disable-next-line no-param-reassign
                if (cur.pv > final) final = cur.pv;
                return final;
            }, 0);
            if (final[_key] === undefined) {
                final[_key] = _maxValue;
            } else {
                final[_key] = Math.max([final[_key], _maxValue]);
            }
            return final;
        }, {}),
    );
    // 3. 合并,生成Y轴配置
    return Object.values(_arr).map((item, index) => {
        let _key = item.name === null ? 'null' : item.name;
        let _lastAxisNumber = _maxValueArr[index - 2];
        let _baseOffset = _offsetValue[index - 2] ?? 0;
        let _finalOffset =
            (_lastAxisNumber !== undefined // 没有相邻的轴
                ? (_lastAxisNumber === 0 ? 20 : _lastAxisNumber.toFixed(2).replaceAll('.', '').length) * 12
                : 0) + _baseOffset;
        _offsetValue.push(_finalOffset);
        return {
            ...item,
            offset: _finalOffset,
            position: index % 2 === 0 ? 'left' : 'right',
            nameTextStyle: {
                align: index % 2 === 0 ? 'right' : 'left',
            },
        };
    });
635
};
636 637

/**
638
 * 1. 生成常规的yAxis配置; 2. 处理sensorType为状态值的指标,生成yAxis配置
639
 *
640 641 642 643
 * @param {array} dataSource 数据源
 * @param {boolean} needUnit 是否显示单位。
 * @param {boolean} curveCenter 曲线是否居中。
 * @param {boolean} showGridLine 是否显示网格线。
644
 * @param theme
645 646
 * @returns {object} 返回左右轴的margin、yAxis的配置。
 */
647
const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine, theme}) => {
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
    const yAxisMap = new Map();
    // 1. 找出最大值; 2. 计算出y轴最大宽度动态计算偏移距离;
    dataSource.forEach((item, index) => {
        const {sensorName, unit} = item;
        const key = sensorName;
        if (!yAxisMap.has(key)) {
            const axis = {
                type: 'value',
                name: needUnit ? unit : null,
                axisLabel: {
                    formatter: (value) => {
                        return handleMaxValue(value);
                    },
                },
                axisLine: {
                    show: true,
                },
                minorTick: {
                    lineStyle: {
陈龙's avatar
陈龙 committed
667
                        // color: '#e2e2e2',
668 669 670 671
                    },
                },
                minorSplitLine: {
                    lineStyle: {
陈龙's avatar
陈龙 committed
672 673
                        // color: '#e2e2e2',
                        // type: 'dashed',
674 675 676 677 678 679 680 681 682 683 684 685 686 687
                    },
                },
            };
            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);
688
        decorateAxisGridLine(axis, showGridLine, theme);
689 690 691 692 693 694
    });
    const yAxis =
        yAxisMap.size > 0 ? reduceYAxis([...yAxisMap.values()], dataSource) : {type: 'value'};
    const leftNum = Math.ceil(yAxisMap.size / 2);
    const rightNum = Math.floor(yAxisMap.size / 2);
    return {leftNum, rightNum, yAxis};
695
};
696
/**
697
 * 1. 最后的配置处理、合并
698 699 700
 *     dataZoom 缩放
 *     xAxis.minInterval X轴的最小间隔
 *     legend legend配置
701 702 703 704 705 706 707 708 709 710
 *
 * @param {object} restOption 额外配置
 * @param {object} xAxis X轴的配置
 * @param {array} legendData Legend数组
 * @param {string} chartType 线型 lineChart|boxChart
 * @param {boolean} contrast 是否为同期对比
 * @param {string} contrastOption 同期对比周期配置, day|month
 * @param {object} config 其他的配置
 */
const assignOptions = (
711 712 713 714 715 716 717
    restOption,
    xAxis,
    legendData,
    chartType,
    contrast,
    contrastOption,
    config,
718
) => {
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
    restOption.dataZoom = [
        {
            show: true,
            bottom: 20,
            start: 0,
            end: 100,
            height: config.isMultiple ? 20 : 28,
            type: 'inside',
            zoomOnMouseWheel: true,
            filterMode: chartType === 'lineChart' ? 'none' : 'weakFilter',
        },
        {
            show: true,
            bottom: 20,
            start: 0,
            end: 100,
            height: config.isMultiple ? 20 : 28,
            type: 'slider',
            zoomOnMouseWheel: true,
            labelFormatter: function (e) {
                let _formatterStr = 'YYYY-MM-DD HH:mm:ss';
                if (contrast) {
                    if (contrastOption === 'day') _formatterStr = 'HH:mm';
                    if (contrastOption === 'month') _formatterStr = 'MM月DD日 HH时';
                }
                return moment(e).format(_formatterStr);
            },
        },
    ];
    xAxis.minInterval = 3600 * (1 * 1000);
    if (legendData) {
        restOption.legend = {
            ...{
                show: true,
                right: 10,
754
                top: 10,
755 756 757 758 759 760 761 762 763
                icon: 'rect',
                itemWidth: 14,
                itemHeight: 8,
                itemGap: 20,
            },
            ...restOption.legend,
            ...{data: legendData},
        };
    }
陈龙's avatar
陈龙 committed
764
};
765 766

const returnMaxOrMinNumber = (dataSource, type) => {
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
    let _obj = null;
    if (type === 'period' && dataSource?.[0]?.dataModel?.length) {
        let _length = dataSource?.[0]?.dataModel?.length;
        let _first = dataSource?.[0]?.dataModel[0]?.pt;
        let _last = dataSource?.[0]?.dataModel[_length - 1]?.pt;
        return ['展示时段: ', _first, _last, type];
    }
    dataSource?.[0]?.dataModel
        .filter((item) => item.pv !== null)
        .forEach((item) => {
            if (!_obj) {
                _obj = item;
            } else {
                if (type === 'min') {
                    if (item.pv < _obj.pv) {
                        _obj = item;
                    }
                }
                if (type === 'max') {
                    if (item.pv > _obj.pv) {
                        _obj = item;
                    }
                }
            }
        });
    let _value = [];
    if (_obj?.pt) {
        _value = [moment(_obj.pt).valueOf(), _obj.pv];
        let _img = type === 'max' ? maxIcon : minIconDownArrow;
        _value.push(_img);
        _value.push(type);
        // 把最大值最小值都push进去,方便计算
    }
    return _value;
801 802
};

803
const handleGrid = (dataSource, needUnit, theme) => {
804
    // 如果是单曲线,_grid的top需要一行的高度,用来放置最值最小值
805
    let _base = 40
806 807 808 809 810 811 812 813
    let _topForUnit = needUnit ? 20 : 0;
    return {
        top: _base + _topForUnit,
        left: 30,
        right: 10,
        bottom: 60,
        containLabel: true,
    };
814 815
};
const renderItem = (params, api) => {
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
    let _base = params.dataIndex;
    let _baseChartWidth = 10;
    let _numberStringWidth =
        _base === 1 && api.value(3) === 'max'
            ? String(api.value(4) || 0).length * _baseChartWidth
            : String(api.value(1)).length * _baseChartWidth;
    let _left = 30;
    let _baseWidth = 190 + _numberStringWidth;
    let _imgWidth = 45;
    let _dateWidth = 128;
    let _imageX = _left + _base * _baseWidth;
    let _timeX = _left + _base * _baseWidth + _imgWidth;
    let _valueX = _left + _base * _baseWidth + _imgWidth + _dateWidth;
    let _color = api.value(3) === 'min' ? '#05C2BC' : '#1685FF';
    let topValue = 25;
    let _trimmer = -2;
    return {
        type: 'group',
        children: [
            {
                type: 'image',
                style: {
                    image: api.value(2),
                    textVerticalAlign: 'middle',
                    y: -6,
                },
                position: [_imageX, topValue],
            },
            {
                type: 'text',
                style: {
                    text: moment(api.value(0)).format('YYYY-MM-DD HH:mm:ss') + ': ',
                    textVerticalAlign: 'top',
                },
                position: [_timeX, topValue + _trimmer],
            },
            {
                type: 'text',
                style: {
                    text: api.value(1),
                    textVerticalAlign: 'middle',
                    fill: _color,
                    font: 'bolder 16px cursive',
                    lineWidth: 10,
                    y: 3,
                },
                position: [_valueX, topValue + _trimmer],
            },
        ],
    };
866 867
};
const returnCustomSeries = (dataSource) => {
868 869
    let _maxNumber = returnMaxOrMinNumber(dataSource, 'max');
    let _minNumber = returnMaxOrMinNumber(dataSource, 'min');
870
    if (!_maxNumber.length && !_minNumber.length) return false;
871 872 873 874 875 876 877 878 879 880 881 882 883 884
    // let _period = returnMaxOrMinNumber(dataSource, 'period');
    // 需要将最大值最小分别传入,后续计算图例位置需要,先min后max
    let _max = _maxNumber[1];
    let _min = _minNumber[1];
    [_maxNumber, _minNumber].forEach((item) => {
        item.push(_min);
        item.push(_max);
    });
    return {
        name: '自定义',
        type: 'custom',
        renderItem: renderItem,
        data: [_minNumber, _maxNumber],
    };
885
};
886 887

const renderStatusItem = (params, api) => {
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
    var categoryIndex = api.value(0);
    var start = api.coord([api.value(1), categoryIndex]);
    var end = api.coord([api.value(2), categoryIndex]);
    var height = api.size([0, 1])[1] * 0.4;
    var rectShape = echarts.graphic.clipRectByRect(
        {
            x: start[0],
            y: start[1] - height / 2,
            width: end[0] - start[0],
            height: height,
        },
        {
            x: params.coordSys.x,
            y: params.coordSys.y,
            width: params.coordSys.width,
            height: params.coordSys.height,
        },
    );
    return (
        rectShape && {
            type: 'rect',
            transition: ['shape'],
            shape: rectShape,
            style: api.style(),
        }
    );
914
};
915

陈龙's avatar
陈龙 committed
916 917 918 919 920 921 922 923 924
/**
 * 图表配置项生成
 *
 * @param {any} dataSource 数据源
 * @param {any} cusOption 自定义属性
 * @param {any} contrast 是否为同期对比
 * @param {any} contrastOption 同期对比周期配置, day|month
 * @param {any} smooth Ture/false, 曲线/折线
 * @param {any} config 额外配置信息
925 926
 * @param lineDataType
 * @param predicateData
927
 * @param theme
陈龙's avatar
陈龙 committed
928
 */
929

陈龙's avatar
陈龙 committed
930 931
const optionGenerator = (
    dataSource,
932
    cusOption,
陈龙's avatar
陈龙 committed
933 934 935
    contrast,
    contrastOption,
    smooth,
936 937
    config,
    lineDataType = '',
938 939
    predicateData,
    theme = 'Normal'
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
) => {
    // 1. 处理配置,配置分配默认值;
    const {
        needUnit,
        curveCenter,
        nameWithSensor,
        showGridLine,
        showMarkLine,
        showPoint,
        deviceAlarmSchemes,
        chartType,
        showBoxOption,
        restOption,
        special,
    } = handleDefault(config, cusOption);
    const {leftNum, rightNum, yAxis} = handleYAxis({
        dataSource,
        needUnit,
        curveCenter,
        showGridLine,
960
        theme
961 962 963 964 965 966 967 968 969 970 971 972 973
    });
    let {xAxis, series, visualMap} = returnXAxis({
        dataSource,
        contrast,
        contrastOption,
        nameWithSensor,
        showMarkLine,
        deviceAlarmSchemes,
        showPoint,
        smooth,
        restOption,
        special,
        yAxis,
974 975
        predicateData,
        chartType
976 977
    });
    // 3. 判断是否开启网格;
978 979
    const grid = handleGrid(dataSource, needUnit, theme);
    decorateAxisGridLine(xAxis, showGridLine, theme);
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
    const tooltipTimeFormat = !contrast
        ? 'YYYY-MM-DD HH:mm:ss'
        : contrastOption === 'day'
            ? 'HH:mm'
            : 'DD HH:mm';
    let tooltip = {};
    // 增加箱线图的逻辑,单曲线才存在该逻辑
    if (chartType && showBoxOption && !special?.special1?.name) {
        if (chartType === 'boxChart' && lineDataType === '特征曲线') {
            const otherData =
                dataSource?.[0]?.dataModel.map((item) => {
                    const {firstPV, lastPV, maxPV, minPV, pt} = item;
                    return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV];
                }) || []; //当存在othersData的时候,只是单曲线
            xAxis = {type: 'time'};
995
            decorateAxisGridLine(xAxis, showGridLine, theme);
996 997 998 999 1000 1001 1002 1003 1004 1005 1006
            let unit = [];
            series = series.map((item) => {
                if (item.unit) unit.push(item.unit);
                item.areaStyle = null;
                return {
                    ...item,
                    z: 9,
                    showSymbol: false,
                    lineStyle: {
                        ...(item.lineStyle ?? {}),
                        width: 1,
1007
                        opacity: 0.4
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
                    }
                };
            });
            // 箱线图去除曲线 2023年10月17日
            series.push({
                type: 'candlestick',
                name: '箱线图',
                symbol: 'none',
                data: otherData,
                z: 10,
                itemStyle: {
                    color: '#FFA200',
                    color0: '#44CD00',
                    borderColor: '#FFA200',
                    borderColor0: '#44CD00',
1023
                },
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
            });
            tooltip = tooltipAccessor(unit);
        }
        if (chartType === 'lineChart' || lineDataType === '原始曲线') {
            let _maxData = [];
            let _minData = [];
            let _currentYear = moment().format('YYYY');
            const formatStr =
                contrastOption === 'day'
                    ? `${_currentYear}-01-01 HH:mm:00`
                    : `${_currentYear}-01-DD HH:mm:00`; // 用来做同期对比,把日期拉到同一区间
            let _maxValues = [];
            /** 生成泳道图,分两种情况 1. 当最大值最小值都是正数时; 2. 当最大值小于零时(此时,最小值一定小于零); */
            dataSource?.[0]?.dataModel.forEach((item) => {
                const {firstPV, lastPV, maxPV, minPV, pt} = item;
                _maxValues.push(maxPV);
                let time = contrast ? moment(pt).format(formatStr) : pt;
                _maxData.push([
                    moment(time).valueOf(),
                    (maxPV > 0 ? maxPV - minPV : minPV - maxPV).toFixed(2),
                ]);
                _minData.push([moment(time).valueOf(), maxPV > 0 ? minPV : maxPV]);
            }); //当存在othersData的时候,只是单曲线
            // xAxis = {type: 'category', data: series[0].data.map(item => moment(item[0]).format('YYYY-MM-DD HH:mm:ss'))};
            xAxis = {type: 'time'};
1049
            decorateAxisGridLine(xAxis, showGridLine, theme);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
            let _unit = '';
            series = series.map((item) => {
                _unit = item.unit ?? '';
                item.areaStyle = null;
                return {...item, showSymbol: false};
            });
            [[..._minData], [..._maxData]].forEach((item, index) => {
                series.push({
                    name: index === 0 ? '周期最小值' : '周期最大值',
                    type: 'line',
                    data: item,
                    lineStyle: {
                        opacity: 0,
                    },
                    ...(index !== 0
                        ? {
                            areaStyle: {
                                color: series?.[0]?.itemStyle?.color ?? '#65a0d1',
                                opacity: 0.2,
                            },
                        }
                        : {}),
                    stack: 'confidence-band',
                    symbol: 'none',
                });
            });
1076
            // 加入预测逻辑
1077 1078 1079 1080
            tooltip = {
                trigger: 'axis',
                formatter: (e) => {
                    return `<div>
陈龙's avatar
陈龙 committed
1081 1082 1083
                    ${headTemplate(e[0])}
                    <div>
                        <div style="display: flex; align-items: center;">
1084
                              <span style="${
1085 1086 1087 1088 1089 1090 1091 1092
                        isMobile()
                            ? 'width: ' +
                            handlePx(90, 'px') +
                            ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
                            : ''
                    }">${
                        e[0].seriesName
                    }</span><span style="display:inline-block;">:</span>
陈龙's avatar
陈龙 committed
1093
                              <span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
1094 1095 1096
                        5,
                        'px',
                    )} 0 auto;">${e[0]?.value?.[1] ?? '-'}</span>
陈龙's avatar
陈龙 committed
1097
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
1098
                            </div>
1099
                            <div style="display: ${
1100 1101
                        lineDataType === '特征曲线' ? 'flex' : 'none'
                    }; align-items: center;">
1102
                              <span>周期最小值</span><span style="display:inline-block;">:</span>
陈龙's avatar
陈龙 committed
1103
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
1104 1105 1106
                        5,
                        'px',
                    )} 0 auto;">${e?.[1]?.value?.[1] ?? '-'}</span>
陈龙's avatar
陈龙 committed
1107
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
1108
                            </div>
1109
                            <div style="display: ${
1110 1111
                        lineDataType === '特征曲线' ? 'flex' : 'none'
                    }; align-items: center;">
1112
                              <span>周期最大值</span><span style="display:inline-block;">:</span>
陈龙's avatar
陈龙 committed
1113
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
1114 1115 1116
                        5,
                        'px',
                    )} 0 auto;">${_maxValues[e?.[2]?.dataIndex] ?? '-'}</span>
陈龙's avatar
陈龙 committed
1117
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
陈龙's avatar
陈龙 committed
1118 1119
                            </div>
                        </div>
程恺文's avatar
程恺文 committed
1120
                    </div>`;
1121 1122
                },
            };
1123
            if (predicateData && predicateData.length) {
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
                tooltip = {
                    trigger: 'axis',
                    formatter: (e) => {
                        return `<div>
                    ${headTemplate(e[0])}
                    <div>
                        <div style="display: flex; align-items: center;">
                              <span style="${
                            isMobile()
                                ? 'width: ' +
                                handlePx(90, 'px') +
                                ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
                                : ''
                        }">${
                            e[0].seriesName
                        }</span><span style="display:inline-block;">:</span>
                              <span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
                            5,
                            'px',
                        )} 0 auto;">${e[0]?.value?.[1] ?? '-'}</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
                            </div>
                            <div style="display: ${predicateData && chartType === 'lineChart' && e.length >= 2 ? 'flex' : 'none'}">
                            <span>${e?.[1]?.seriesName}</span>
                                <span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
                            5,
                            'px',
                        )} 0 auto;">${e[1]?.value?.[1] ?? '-'}</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>     
                            </div>
                            <div style="display: ${
                            lineDataType === '特征曲线' ? 'flex' : 'none'
                        }; align-items: center;">
                              <span>周期最小值</span><span style="display:inline-block;">:</span>
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
                            5,
                            'px',
                        )} 0 auto;">${e?.[2]?.value?.[1] ?? '-'}</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
                            </div>
                            <div style="display: ${
                            lineDataType === '特征曲线' ? 'flex' : 'none'
                        }; align-items: center;">
                              <span>周期最大值</span><span style="display:inline-block;">:</span>
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
                            5,
                            'px',
                        )} 0 auto;">${_maxValues[e?.[3]?.dataIndex] ?? '-'}</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
                            </div>
                        </div>
                    </div>`;
                    },
                };
            }
1179 1180
        }
        // 单曲线需要标记最大值、最小值的情况下,需要增加自定义的series,将最大最小值显示在图表上
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
//        2024年3月11日 废弃在图表左上角标记最大值最小值的做法,采用在图表中标记并显示出值的做法。注释暂时保留 edit by chenlong
        /*        if (dataSource?.[0]?.dataModel?.length && chartType === 'lineChart') {
                    let _dataSource = dataSource?.filter((item) => item?.dataModel?.length);
                    if (_dataSource?.length) {
                        let _customSeries = returnCustomSeries(dataSource);
                        if (_customSeries) {
                            series.push(_customSeries);
                        }
                    }
                }*/
1191 1192 1193 1194 1195
    } else {
        tooltip = tooltipAccessor(
            series.map((item) => item.unit),
            {contrastOption, contrast},
        );
1196
    }
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
    tooltip.timeFormat = tooltipTimeFormat;
    let _legendData = series
        .filter((item) => !['周期最大值', '周期最小值', '自定义'].includes(item.name)) // legend中,过滤掉辅助业务的legend
        .map((item) => item.name);
    assignOptions(restOption, xAxis, _legendData, chartType, contrast, contrastOption, config);
    let _options = {
        yAxis,
        grid,
        xAxis,
        series,
        tooltip,
        visualMap,
1209
        ...restOption
1210 1211
    };
    return _options;
1212 1213 1214
};

export default optionGenerator;
1215

1216
const handleDataSource = (dataSource) => {
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
    let _temp = null;
    let _data = [];
    let _dataLength = dataSource[0].dataModel.length;
    dataSource[0].dataModel.forEach((item, index) => {
        if (index === 0) {
            _data.push(item);
        } else if (index === _dataLength - 1) {
            _data.push(item);
        } else {
            if (_temp.pv !== item.pv) {
                _data.push(item);
            }
        }
        _temp = item;
    });
    return _data;
1233
};
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
const handleSpecial2 = (special, sensorName, sensorType, data1, data2) => {
    let color = '';
    let name = '';
    let value1 = '';
    let value2 = '';
    // 1. valDesc
    if (sensorType === '状态值') {
        const _colorMap = {
            变频: '#1685ff',
            工频: '#00d0c7',
            运行: '#1685ff',
            故障: '#ff6b37',
1246
            停止: '#aeaeae',
1247 1248 1249 1250 1251 1252 1253 1254 1255
        };
        let _valDescMap = special.allValDesc[sensorName]?.split(';').reduce((final, cur) => {
            let _arr = cur.split(':');
            final[_arr[0]] = _arr[1];
            return final;
        }, {});
        name = _valDescMap[data1.pv || 0];
        color = _colorMap[name];
    }
1256

1257 1258 1259
    //2. 开关量
    if (sensorType === '开关值') {
        const _switchColorMap = {
1260
            0: '#aeaeae',
1261 1262 1263 1264 1265 1266 1267 1268 1269
            1: '#1685ff',
        };
        const _switchNameMap = {
            0: '关',
            1: '开',
        };
        name = _switchNameMap[data1.pv || 0];
        color = _switchColorMap[data1.pv || 0];
    }
1270

1271 1272 1273
    value1 = moment(data1.pt).valueOf();
    value2 = moment(data2.pt).valueOf();
    return {color, value1, value2, name};
1274 1275
};
const handleDataToSeries = (special, sensorName, sensorType, data) => {
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
    let _data = [];
    let _legend = [];
    data.forEach((item, index) => {
        if (index === data.length - 1) return;
        let {color, value1, value2, name} = handleSpecial2(
            special,
            sensorName,
            sensorType,
            item,
            data[index + 1],
        );
        if (!_legend.includes(name)) _legend.push(name);
        _data.push({
            itemStyle: {normal: {color}},
            name: name,
            value: [0, value1, value2, `${item.pt}-${data[index + 1].pt}`],
        });
1293
    });
1294
    return {data: _data, legend: _legend};
1295 1296
};

1297
const specialTypeChartOptionGenerator = ({
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
                                             dataSource,
                                             cusOption,
                                             contrast,
                                             contrastOption,
                                             smooth,
                                             config,
                                         }) => {
    const {special, sensorType} = config;
    const {allSensorType, allPointAddress} = special;
    // 处理原始数据,处理数据为后series数据
    const sensorName = dataSource[0].sensorName;
    let _data = handleDataSource(dataSource);
    let {data, legend} = handleDataToSeries(special, sensorName, sensorType, _data);
    // 1. x/y轴
    let xAxis = {
        type: 'time',
        axisLabel: {
            formatter: contrast
                ? contrastOption === 'month'
                    ? '{dd}日'
                    : '{HH}:{mm}'
                : {
                    year: '{yyyy}',
                    month: '{MMM}',
                    day: '{MMM}{d}日',
                    hour: '{HH}:{mm}',
                    minute: '{HH}:{mm}',
                    second: '{HH}:{mm}:{ss}',
                    none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss}',
                },
        },
        minorTick: {
            lineStyle: {
陈龙's avatar
陈龙 committed
1331
                // color: '#e2e2e2',
1332 1333 1334 1335 1336 1337
            },
            show: true,
            splitNumber: 2,
        },
        minorSplitLine: {
            lineStyle: {
陈龙's avatar
陈龙 committed
1338 1339
                // color: '#e2e2e2',
                // type: 'dashed',
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
            },
            show: true,
        },
        splitLine: {
            show: true,
        },
        minInterval: 3600 * 1000,
    };
    let yAxis = {
        data: [dataSource[0].sensorName],
        axisLine: {
            show: true,
        },
        minorTick: {
            lineStyle: {
陈龙's avatar
陈龙 committed
1355
                // color: '#e2e2e2',
1356 1357 1358 1359 1360 1361
            },
            show: true,
            splitNumber: 2,
        },
        minorSplitLine: {
            lineStyle: {
陈龙's avatar
陈龙 committed
1362 1363
                // color: '#e2e2e2',
                // type: 'dashed',
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
            },
            show: true,
        },
        splitLine: {
            show: true,
        },
    };
    //2. series
    let series = [
        {
            type: 'custom',
            renderItem: renderStatusItem,
            itemStyle: {
                opacity: 0.8,
            },
            encode: {
                x: [1, 2],
                y: 0,
            },
            data,
        },
        ...legend.map((item) => {
            let _map = {
                变频: '#1685ff',
                工频: '#00d0c7',
                运行: '#1685ff',
                故障: '#ff6b37',
1391 1392
                停止: '#898989',
                : '#cacaca',
1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
                : '#1685ff',
            };
            return {
                type: 'custom',
                name: item,
                color: _map[item],
                renderItem: () => {
                },
            };
        }),
    ];
    let grid = {
        top: 80,
        left: 30,
        right: 10,
        bottom: 60,
        containLabel: true,
    };
    let legendConfig = {
        show: true,
        data: legend,
        selectedMode: false,
        right: 10,
        top: 30,
        icon: 'rect',
        itemWidth: 14,
        itemHeight: 8,
        itemGap: 20,
    };
    let _option = {
        xAxis,
        yAxis,
        grid,
        series,
        legend: legendConfig,
        tooltip: {
            trigger: 'item',
            formatter: function (params) {
                return params.marker + params.name + ': ' + params?.value?.[3];
            },
        },
        dataZoom: [
            {
                type: 'slider',
                filterMode: 'weakFilter',
                showDataShadow: false,
                labelFormatter: '',
            },
            {
                type: 'inside',
                filterMode: 'weakFilter',
            },
        ],
    };
    return _option;
1448
};
1449
export {specialTypeChartOptionGenerator};