Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wisdom-components
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ReactWeb5
wisdom-components
Commits
d35c48a5
Commit
d35c48a5
authored
Apr 17, 2023
by
陈龙
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 新增箱线图、置信区间功能
parent
c180e1d5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1270 additions
and
1161 deletions
+1270
-1161
SingleChart.js
packages/extend-components/EC_HistoryView/src/SingleChart.js
+116
-111
GridDemo.js
...es/extend-components/EC_HistoryView/src/demos/GridDemo.js
+2
-2
index.js
packages/extend-components/EC_HistoryView/src/demos/index.js
+9
-6
index.js
packages/extend-components/EC_HistoryView/src/index.js
+796
-753
utils.js
packages/extend-components/EC_HistoryView/src/utils.js
+347
-289
No files found.
packages/extend-components/EC_HistoryView/src/SingleChart.js
View file @
d35c48a5
import
React
,
{
memo
,
useEffect
,
useMemo
,
useRef
}
from
'react'
;
import
{
BasicChart
}
from
'@wisdom-components/basicchart'
;
import
React
,
{
memo
,
useEffect
,
useMemo
,
useRef
}
from
'react'
;
import
{
BasicChart
}
from
'@wisdom-components/basicchart'
;
import
PandaEmpty
from
'@wisdom-components/empty'
;
import
optionGenerator
,
{
alarmMarkLine
,
minMaxMarkPoint
,
offlineArea
}
from
'./utils'
;
import
{
isArray
,
cloneDeep
}
from
'lodash'
;
import
optionGenerator
,
{
alarmMarkLine
,
minMaxMarkPoint
,
offlineArea
}
from
'./utils'
;
import
{
isArray
,
cloneDeep
}
from
'lodash'
;
const
SimgleChart
=
memo
((
props
)
=>
{
const
{
dataSource
,
contrast
=
false
,
contrastOption
=
'day'
,
smooth
=
true
,
curveCenter
,
showGridLine
=
false
,
deviceAlarmSchemes
,
}
=
props
;
const
chartRef
=
useRef
();
const
{
dataSource
,
contrast
=
false
,
contrastOption
=
'day'
,
smooth
=
true
,
curveCenter
,
showGridLine
=
false
,
deviceAlarmSchemes
,
chartType
,
justLine
}
=
props
;
const
chartRef
=
useRef
();
const
option
=
useMemo
(()
=>
{
const
config
=
{
needUnit
:
true
,
curveCenter
,
showGridLine
,
deviceAlarmSchemes
,
showMarkLine
:
true
,
showPoint
:
true
,
};
return
optionGenerator
(
dataSource
,
null
,
contrast
,
contrastOption
,
smooth
,
config
);
},
[
dataSource
,
smooth
,
curveCenter
]);
const
option
=
useMemo
(()
=>
{
const
config
=
{
needUnit
:
true
,
curveCenter
,
showGridLine
,
deviceAlarmSchemes
,
showMarkLine
:
true
,
showPoint
:
true
,
chartType
,
justLine
};
return
optionGenerator
(
dataSource
,
null
,
contrast
,
contrastOption
,
smooth
,
config
);
},
[
dataSource
,
smooth
,
curveCenter
]);
useEffect
(()
=>
{
chartRef
.
current
?.
resize
?.();
const
chart
=
chartRef
.
current
?.
getEchartsInstance
?.();
useEffect
(()
=>
{
chartRef
.
current
?.
resize
?.();
const
chart
=
chartRef
.
current
?.
getEchartsInstance
?.();
function
hander
(
params
)
{
const
{
selected
}
=
params
;
const
count
=
Object
.
values
(
selected
||
{}).
filter
((
item
)
=>
item
).
length
;
const
option
=
cloneDeep
(
chart
.
getOption
());
const
needMarkLine
=
count
===
1
;
option
.
series
.
forEach
((
item
,
index
)
=>
{
let
offlineAreas
=
offlineArea
(
dataSource
[
index
]);
if
(
offlineAreas
.
data
?.
length
)
{
option
.
markArea
=
null
;
item
.
markArea
=
offlineAreas
;
}
if
(
needMarkLine
&&
selected
[
item
.
name
])
{
item
.
markLine
=
alarmMarkLine
(
dataSource
[
index
],
index
,
[
dataSource
[
index
]],
deviceAlarmSchemes
,
);
item
.
markPoint
=
minMaxMarkPoint
(
dataSource
[
index
],
index
,
[
dataSource
[
index
]]);
}
else
{
item
.
markLine
=
{};
item
.
markPoint
=
{};
function
hander
(
params
)
{
const
{
selected
}
=
params
;
const
count
=
Object
.
values
(
selected
||
{}).
filter
((
item
)
=>
item
).
length
;
const
option
=
cloneDeep
(
chart
.
getOption
());
const
needMarkLine
=
count
===
1
;
option
.
series
.
forEach
((
item
,
index
)
=>
{
let
offlineAreas
=
offlineArea
(
dataSource
[
index
]);
if
(
offlineAreas
.
data
?.
length
)
{
option
.
markArea
=
null
;
item
.
markArea
=
offlineAreas
;
}
if
(
needMarkLine
&&
selected
[
item
.
name
])
{
item
.
markLine
=
alarmMarkLine
(
dataSource
[
index
],
index
,
[
dataSource
[
index
]],
deviceAlarmSchemes
,
);
item
.
markPoint
=
minMaxMarkPoint
(
dataSource
[
index
],
index
,
[
dataSource
[
index
]]);
}
else
{
item
.
markLine
=
{};
item
.
markPoint
=
{};
}
});
chart
.
setOption
(
option
,
true
);
}
});
chart
.
setOption
(
option
,
true
);
}
if
(
!
chart
)
return
;
chart
.
on
(
'legendselectchanged'
,
hander
);
return
()
=>
{
chart
.
off
(
'legendselectchanged'
,
hander
);
};
},
[
dataSource
,
deviceAlarmSchemes
]);
// 网格开关,不更新整个图表
useEffect
(()
=>
{
const
chart
=
chartRef
.
current
?.
getEchartsInstance
?.();
if
(
!
chart
)
return
;
const
option
=
chart
.
getOption
();
// 交互指针
const
tooltip
=
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
showGridLine
?
'cross'
:
'line'
,
},
};
// 网格线
const
axisConfig
=
{
minorTick
:
{
show
:
showGridLine
,
splitNumber
:
2
,
},
minorSplitLine
:
{
show
:
showGridLine
,
lineStyle
:
{
type
:
'dashed'
,
},
},
splitLine
:
{
show
:
showGridLine
,
},
};
let
yAxis
=
axisConfig
;
if
(
isArray
(
option
.
yAxis
))
{
yAxis
=
option
.
yAxis
.
map
((
item
)
=>
({
...
axisConfig
}));
}
let
xAxis
=
axisConfig
;
chart
.
setOption
({
xAxis
,
yAxis
,
tooltip
,
});
},
[
showGridLine
]);
if
(
!
chart
)
return
;
chart
.
on
(
'legendselectchanged'
,
hander
);
return
()
=>
{
chart
.
off
(
'legendselectchanged'
,
hander
);
};
},
[
dataSource
,
deviceAlarmSchemes
]);
// 网格开关,不更新整个图表
useEffect
(()
=>
{
const
chart
=
chartRef
.
current
?.
getEchartsInstance
?.();
if
(
!
chart
)
return
;
const
option
=
chart
.
getOption
();
// 交互指针
const
tooltip
=
{
trigger
:
'axis'
,
axisPointer
:
{
type
:
showGridLine
?
'cross'
:
'line'
,
},
};
// 网格线
const
axisConfig
=
{
minorTick
:
{
show
:
showGridLine
,
splitNumber
:
2
,
},
minorSplitLine
:
{
show
:
showGridLine
,
lineStyle
:
{
type
:
'dashed'
,
},
},
splitLine
:
{
show
:
showGridLine
,
},
};
let
yAxis
=
axisConfig
;
if
(
isArray
(
option
.
yAxis
))
{
yAxis
=
option
.
yAxis
.
map
((
item
)
=>
({...
axisConfig
}));
}
let
xAxis
=
axisConfig
;
chart
.
setOption
({
xAxis
,
yAxis
,
tooltip
,
});
},
[
showGridLine
]);
// 数据都为空显示缺省页
const
isEmpty
=
useMemo
(
()
=>
!
dataSource
||
!
dataSource
.
length
||
!
dataSource
.
find
((
e
)
=>
e
.
dataModel
&&
e
.
dataModel
.
length
>
0
),
[
dataSource
],
);
// 数据都为空显示缺省页
const
isEmpty
=
useMemo
(
()
=>
!
dataSource
||
!
dataSource
.
length
||
!
dataSource
.
find
((
e
)
=>
e
.
dataModel
&&
e
.
dataModel
.
length
>
0
),
[
dataSource
],
);
return
isEmpty
?
(
<
PandaEmpty
/>
)
:
(
<
BasicChart
ref
=
{
chartRef
}
option
=
{
option
}
notMerge
style
=
{{
width
:
'100%'
,
height
:
'100%'
}}
/
>
);
return
isEmpty
?
(
<
PandaEmpty
/>
)
:
(
<
BasicChart
ref
=
{
chartRef
}
option
=
{
option
}
notMerge
style
=
{{
width
:
'100%'
,
height
:
'100%'
}}
/
>
);
});
export
default
SimgleChart
;
packages/extend-components/EC_HistoryView/src/demos/GridDemo.js
View file @
d35c48a5
...
...
@@ -2,7 +2,7 @@ import React from 'react';
import
HistoryView
from
'../index'
;
const
deviceParams
=
[
{
/*
{
deviceCode: 'EGBF00000146',
sensors: '进水压力,出水瞬时流量,出水累计流量',
deviceType: '二供泵房',
...
...
@@ -13,7 +13,7 @@ const deviceParams = [
sensors: '进水压力,出水瞬时流量,出水累计流量',
deviceType: '二供泵房',
pointAddressID: 4,
},
},
*/
{
deviceCode
:
'EGBF00000002'
,
sensors
:
'进水压力,出水瞬时流量,出水累计流量'
,
...
...
packages/extend-components/EC_HistoryView/src/demos/index.js
View file @
d35c48a5
...
...
@@ -3,17 +3,20 @@ import HistoryView from '../index';
const
deviceParams
=
[
{
deviceCode
:
'EGBF00000146'
,
sensors
:
'进水压力,出水瞬时流量,出水累计流量'
,
deviceCode
:
'EGBF00000082'
,
sensors
:
'进水压力'
,
// sensors: '出水瞬时流量',
// sensors: '出水累计流量',
// sensors: '进水压力,出水瞬时流量,出水累计流量',
deviceType
:
'二供泵房'
,
pointAddressID
:
4
,
},
{
deviceCode
:
'EG
JZ0000111
3'
,
/*
{
deviceCode: 'EG
BF0000008
3',
sensors: '出水瞬时流量,出水压力,泵1状态',
deviceType: '二供机组',
pointAddressID: 4,
},
},
*/
// {
// deviceCode: 'EGJZ00001113',
// sensors: '出水压力',
...
...
@@ -24,7 +27,7 @@ const deviceParams = [
const
Demo
=
()
=>
{
return
(
<
div
style
=
{{
height
:
700
}}
>
<
HistoryView
deviceParams
=
{
deviceParams
}
defaultModel
=
"
tabl
e"
/>
<
HistoryView
deviceParams
=
{
deviceParams
}
defaultModel
=
"
curv
e"
/>
<
/div
>
);
};
...
...
packages/extend-components/EC_HistoryView/src/index.js
View file @
d35c48a5
import
React
,
{
useContext
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
React
,
{
useContext
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
PropTypes
from
'prop-types'
;
import
classNames
from
'classnames'
;
import
{
Checkbox
,
ConfigProvider
,
DatePicker
,
Radio
,
Select
,
Spin
,
Tabs
,
Tooltip
,
Button
,
Checkbox
,
ConfigProvider
,
DatePicker
,
Radio
,
Select
,
Spin
,
Tabs
,
Tooltip
,
Button
,
}
from
'antd'
;
import
{
CloseCircleFilled
,
PlusCircleOutlined
,
QuestionCircleFilled
,
DownloadOutlined
,
CloseCircleFilled
,
PlusCircleOutlined
,
QuestionCircleFilled
,
DownloadOutlined
,
}
from
'@ant-design/icons'
;
import
moment
from
'moment'
;
import
_
from
'lodash'
;
import
TimeRangePicker
from
'@wisdom-components/timerangepicker'
;
import
PandaEmpty
from
'@wisdom-components/empty'
;
import
BasicTable
from
'@wisdom-components/basictable'
;
import
{
getHistoryInfo
,
getDeviceAlarmScheme
,
getExportDeviceHistoryUrl
}
from
'./apis'
;
import
{
getHistoryInfo
,
getDeviceAlarmScheme
,
getExportDeviceHistoryUrl
}
from
'./apis'
;
import
SimgleChart
from
'./SingleChart'
;
import
GridChart
from
'./GridChart'
;
import
'./index.less'
;
import
{
globalConfig
}
from
'antd/lib/config-provider'
;
import
{
globalConfig
}
from
'antd/lib/config-provider'
;
const
{
RangePicker
}
=
DatePicker
;
const
{
Option
}
=
Select
;
const
{
RangePicker
}
=
DatePicker
;
const
{
Option
}
=
Select
;
const
startFormat
=
'YYYY-MM-DD 00:00:00'
;
const
endFormat
=
'YYYY-MM-DD 23:59:59'
;
...
...
@@ -38,794 +38,837 @@ const timeFormat = 'YYYY-MM-DD HH:mm:ss';
const
dateFormat
=
'YYYYMMDD'
;
const
timeList
=
[
{
key
:
'twelveHours'
,
name
:
'近12小时'
,
},
{
key
:
'roundClock'
,
name
:
'近24小时'
,
},
{
key
:
'oneWeek'
,
name
:
'近1周'
,
},
{
key
:
'oneMonth'
,
name
:
'近1月'
,
},
{
key
:
'twelveHours'
,
name
:
'近12小时'
,
},
{
key
:
'roundClock'
,
name
:
'近24小时'
,
},
{
key
:
'oneWeek'
,
name
:
'近1周'
,
},
{
key
:
'oneMonth'
,
name
:
'近1月'
,
},
];
const
CheckboxData
=
[
{
key
:
'curveCenter'
,
label
:
'曲线居中'
,
checked
:
false
,
showInCurve
:
true
,
showInTable
:
false
,
},
{
key
:
'chartGrid'
,
label
:
'图表网格'
,
checked
:
true
,
showInCurve
:
true
,
showInTable
:
false
,
},
{
key
:
'ignoreOutliers'
,
label
:
'数据滤波'
,
type
:
'updateIgnoreOutliers'
,
checked
:
false
,
showInCurve
:
true
,
showInTable
:
true
,
tooltip
:
'数据滤波针对与样本平均值相差2个标准差以上的值进行过滤。'
,
},
{
key
:
'dataThin'
,
label
:
'数据抽稀'
,
type
:
'updateDataThin'
,
checked
:
true
,
showInCurve
:
false
,
showInTable
:
true
,
},
{
key
:
'curveCenter'
,
label
:
'曲线居中'
,
checked
:
false
,
showInCurve
:
true
,
showInTable
:
false
,
},
{
key
:
'chartGrid'
,
label
:
'图表网格'
,
checked
:
true
,
showInCurve
:
true
,
showInTable
:
false
,
},
{
key
:
'ignoreOutliers'
,
label
:
'数据滤波'
,
type
:
'updateIgnoreOutliers'
,
checked
:
false
,
showInCurve
:
true
,
showInTable
:
true
,
tooltip
:
'数据滤波针对与样本平均值相差2个标准差以上的值进行过滤。'
,
},
{
key
:
'justLine'
,
label
:
'仅查看曲线'
,
type
:
''
,
checked
:
false
,
showInCurve
:
false
,
showInTable
:
false
,
},
{
key
:
'dataThin'
,
label
:
'数据抽稀'
,
type
:
'updateDataThin'
,
checked
:
true
,
showInCurve
:
false
,
showInTable
:
true
,
},
];
const
timeIntervalList
=
[
{
key
:
'5'
,
zoom
:
'5'
,
unit
:
'min'
,
name
:
'5分钟'
,
},
{
key
:
'10'
,
zoom
:
'10'
,
unit
:
'min'
,
name
:
'10分钟'
,
},
{
key
:
'30'
,
zoom
:
'30'
,
unit
:
'min'
,
name
:
'30分钟'
,
},
{
key
:
'1'
,
zoom
:
'1'
,
unit
:
'h'
,
name
:
'1小时'
,
},
{
key
:
'2'
,
zoom
:
'2'
,
unit
:
'h'
,
name
:
'2小时'
,
},
{
key
:
'4'
,
zoom
:
'4'
,
unit
:
'h'
,
name
:
'4小时'
,
},
{
key
:
'6'
,
zoom
:
'6'
,
unit
:
'h'
,
name
:
'6小时'
,
},
{
key
:
'12'
,
zoom
:
'12'
,
unit
:
'h'
,
name
:
'12小时'
,
},
{
key
:
'5'
,
zoom
:
'5'
,
unit
:
'min'
,
name
:
'5分钟'
,
},
{
key
:
'10'
,
zoom
:
'10'
,
unit
:
'min'
,
name
:
'10分钟'
,
},
{
key
:
'30'
,
zoom
:
'30'
,
unit
:
'min'
,
name
:
'30分钟'
,
},
{
key
:
'1'
,
zoom
:
'1'
,
unit
:
'h'
,
name
:
'1小时'
,
},
{
key
:
'2'
,
zoom
:
'2'
,
unit
:
'h'
,
name
:
'2小时'
,
},
{
key
:
'4'
,
zoom
:
'4'
,
unit
:
'h'
,
name
:
'4小时'
,
},
{
key
:
'6'
,
zoom
:
'6'
,
unit
:
'h'
,
name
:
'6小时'
,
},
{
key
:
'12'
,
zoom
:
'12'
,
unit
:
'h'
,
name
:
'12小时'
,
},
];
const
updateTime
=
(
key
)
=>
{
let
start
=
''
;
let
end
=
''
;
if
(
Array
.
isArray
(
key
))
{
start
=
moment
(
key
[
0
]).
format
(
timeFormat
);
end
=
moment
(
key
[
1
]).
format
(
timeFormat
);
}
else
{
switch
(
key
)
{
case
'twelveHours'
:
start
=
moment
().
subtract
(
12
,
'hour'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'roundClock'
:
start
=
moment
().
subtract
(
24
,
'hour'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'oneWeek'
:
start
=
moment
().
subtract
(
7
,
'day'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'oneMonth'
:
start
=
moment
().
subtract
(
30
,
'day'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
let
start
=
''
;
let
end
=
''
;
if
(
Array
.
isArray
(
key
))
{
start
=
moment
(
key
[
0
]).
format
(
timeFormat
);
end
=
moment
(
key
[
1
]).
format
(
timeFormat
);
}
else
{
switch
(
key
)
{
case
'twelveHours'
:
start
=
moment
().
subtract
(
12
,
'hour'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'roundClock'
:
start
=
moment
().
subtract
(
24
,
'hour'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'oneWeek'
:
start
=
moment
().
subtract
(
7
,
'day'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
case
'oneMonth'
:
start
=
moment
().
subtract
(
30
,
'day'
).
format
(
timeFormat
);
end
=
moment
().
format
(
timeFormat
);
break
;
}
}
}
return
[
{
dateFrom
:
start
,
dateTo
:
end
,
},
];
return
[
{
dateFrom
:
start
,
dateTo
:
end
,
},
];
};
const
DefaultDatePicker
=
(
value
)
=>
[
{
key
:
1
,
value
:
moment
(),
},
{
key
:
2
,
value
:
moment
().
subtract
(
1
,
value
),
},
{
key
:
1
,
value
:
moment
(),
},
{
key
:
2
,
value
:
moment
().
subtract
(
1
,
value
),
},
];
const
handleBatchTime
=
(
arr
,
cOption
)
=>
{
let
newArr
=
[];
arr
.
forEach
((
child
)
=>
{
if
(
child
.
value
)
{
newArr
.
push
({
dateFrom
:
moment
(
child
.
value
).
startOf
(
cOption
).
format
(
startFormat
),
dateTo
:
moment
(
child
.
value
).
endOf
(
cOption
).
format
(
endFormat
),
});
}
});
newArr
=
_
.
uniqWith
(
newArr
,
_
.
isEqual
);
// 去掉重复日期时间
return
newArr
;
let
newArr
=
[];
arr
.
forEach
((
child
)
=>
{
if
(
child
.
value
)
{
newArr
.
push
({
dateFrom
:
moment
(
child
.
value
).
startOf
(
cOption
).
format
(
startFormat
),
dateTo
:
moment
(
child
.
value
).
endOf
(
cOption
).
format
(
endFormat
),
});
}
});
newArr
=
_
.
uniqWith
(
newArr
,
_
.
isEqual
);
// 去掉重复日期时间
return
newArr
;
};
const
timeColumn
=
{
title
:
'采集时间'
,
dataIndex
:
'time'
,
key
:
'time'
,
width
:
170
,
fixed
:
'left'
,
ellipsis
:
true
,
align
:
'center'
,
title
:
'采集时间'
,
dataIndex
:
'time'
,
key
:
'time'
,
width
:
170
,
fixed
:
'left'
,
ellipsis
:
true
,
align
:
'center'
,
};
const
HistoryView
=
(
props
)
=>
{
const
{
getPrefixCls
}
=
useContext
(
ConfigProvider
.
ConfigContext
);
const
prefixCls
=
getPrefixCls
(
'history-view'
);
const
{
title
,
grid
,
defaultChecked
,
tableProps
,
deviceParams
,
defaultModel
,
showModels
,
needMarkLine
,
}
=
props
;
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
activeTabKey
,
setActiveTabKey
]
=
useState
(
defaultModel
);
// 时间模式: 自定义模式/同期对比模式
const
[
timeValue
,
setTimeValue
]
=
useState
(
'customer'
);
// 自定义模式
const
[
customerChecked
,
setCustomerChecked
]
=
useState
(
defaultChecked
);
// 时间快速选择类型值
const
[
customerTime
,
setCustomerTime
]
=
useState
();
// 自定义时间选择值
// 同期对比模式
const
[
contrastOption
,
setContrastOption
]
=
useState
(
'day'
);
// 对比时间类型: 日/月
const
[
datePickerArr
,
setDatePickerArr
]
=
useState
(
DefaultDatePicker
(
'day'
));
// 对比时间段配置值
const
[
checkboxData
,
setCheckboxData
]
=
useState
(()
=>
[...
CheckboxData
]);
// 曲线设置项
const
[
dataThinKey
,
setDataThinKey
]
=
useState
(
timeIntervalList
[
0
].
key
);
// 曲线抽稀时间设置
const
[
columns
,
setColumns
]
=
useState
([]);
const
[
tableData
,
setTableData
]
=
useState
([]);
const
[
chartDataSource
,
setChartDataSource
]
=
useState
([]);
// 选择的时间范围值
const
dateRange
=
useMemo
(()
=>
{
if
(
timeValue
===
'customer'
)
{
return
updateTime
(
customerChecked
||
customerTime
);
}
else
{
return
handleBatchTime
(
datePickerArr
,
contrastOption
);
}
},
[
contrastOption
,
customerChecked
,
customerTime
,
datePickerArr
,
timeValue
]);
const
configDependence
=
checkboxData
.
filter
((
item
)
=>
[
'curveCenter'
,
'chartGrid'
].
indexOf
(
item
.
key
)
===
-
1
)
.
map
((
item
)
=>
item
.
checked
)
.
join
(
','
);
// 数据配置
const
dataConfig
=
useMemo
(()
=>
{
const
initial
=
{
ignoreOutliers
:
false
,
dataThin
:
false
,
zoom
:
''
,
// 数据抽稀时间
unit
:
''
,
// 数据抽稀时间单位
const
{
getPrefixCls
}
=
useContext
(
ConfigProvider
.
ConfigContext
);
const
prefixCls
=
getPrefixCls
(
'history-view'
);
const
{
title
,
grid
,
defaultChecked
,
tableProps
,
deviceParams
,
defaultModel
,
showModels
,
needMarkLine
,
}
=
props
;
const
isBoxPlots
=
deviceParams
?.
length
===
1
&&
deviceParams
[
0
]?.
sensors
?.
split
(
','
).
length
===
1
;
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
activeTabKey
,
setActiveTabKey
]
=
useState
(
defaultModel
);
// 时间模式: 自定义模式/同期对比模式
const
[
timeValue
,
setTimeValue
]
=
useState
(
'customer'
);
// 自定义模式
const
[
customerChecked
,
setCustomerChecked
]
=
useState
(
defaultChecked
);
// 时间快速选择类型值
const
[
customerTime
,
setCustomerTime
]
=
useState
();
// 自定义时间选择值
// 同期对比模式
const
[
contrastOption
,
setContrastOption
]
=
useState
(
'day'
);
// 对比时间类型: 日/月
const
[
datePickerArr
,
setDatePickerArr
]
=
useState
(
DefaultDatePicker
(
'day'
));
// 对比时间段配置值
const
[
checkboxData
,
setCheckboxData
]
=
useState
(()
=>
[...
CheckboxData
]);
// 曲线设置项
const
[
dataThinKey
,
setDataThinKey
]
=
useState
(
timeIntervalList
[
0
].
key
);
// 曲线抽稀时间设置
const
[
columns
,
setColumns
]
=
useState
([]);
const
[
tableData
,
setTableData
]
=
useState
([]);
const
[
chartDataSource
,
setChartDataSource
]
=
useState
([]);
const
[
chartType
,
setChartType
]
=
useState
(
'lineChart'
);
// 选择的时间范围值
const
dateRange
=
useMemo
(()
=>
{
if
(
timeValue
===
'customer'
)
{
return
updateTime
(
customerChecked
||
customerTime
);
}
else
{
return
handleBatchTime
(
datePickerArr
,
contrastOption
);
}
},
[
contrastOption
,
customerChecked
,
customerTime
,
datePickerArr
,
timeValue
]);
const
configDependence
=
checkboxData
.
filter
((
item
)
=>
[
'curveCenter'
,
'chartGrid'
].
indexOf
(
item
.
key
)
===
-
1
)
.
map
((
item
)
=>
item
.
checked
)
.
join
(
','
);
// 数据配置
const
dataConfig
=
useMemo
(()
=>
{
const
initial
=
{
ignoreOutliers
:
false
,
dataThin
:
false
,
zoom
:
''
,
// 数据抽稀时间
unit
:
''
,
// 数据抽稀时间单位
};
// 曲线居中,过滤异常值,数据抽稀
const
config
=
checkboxData
.
reduce
(
(
pre
,
item
)
=>
(
item
.
key
!==
'curveCenter'
&&
(
pre
[
item
.
key
]
=
item
.
checked
),
pre
),
initial
,
);
// 数据抽稀时间单位
const
dataThin
=
timeIntervalList
.
find
((
item
)
=>
item
.
key
===
dataThinKey
);
config
.
zoom
=
activeTabKey
===
'curve'
?
''
:
dataThin
?.
zoom
??
''
;
config
.
unit
=
activeTabKey
===
'curve'
?
''
:
dataThin
?.
unit
??
''
;
config
.
dataThin
=
activeTabKey
===
'curve'
?
true
:
config
.
dataThin
;
// 曲线强制抽稀
return
config
;
},
[
configDependence
,
dataThinKey
,
activeTabKey
]);
// 图表居中
const
[
curveCenter
,
chartGrid
]
=
useMemo
(()
=>
{
const
curveCenter
=
checkboxData
.
find
((
item
)
=>
item
.
key
===
'curveCenter'
)?.
checked
;
const
chartGrid
=
checkboxData
.
find
((
item
)
=>
item
.
key
===
'chartGrid'
)?.
checked
;
return
[
curveCenter
,
chartGrid
];
},
[
checkboxData
]);
// 自定义模式: 快速选择
const
onCustomerTimeChange
=
(
key
)
=>
{
setCustomerChecked
(
key
);
!!
customerTime
&&
setCustomerTime
(
null
);
};
// 曲线居中,过滤异常值,数据抽稀
const
config
=
checkboxData
.
reduce
(
(
pre
,
item
)
=>
(
item
.
key
!==
'curveCenter'
&&
(
pre
[
item
.
key
]
=
item
.
checked
),
pre
),
initial
,
);
// 数据抽稀时间单位
const
dataThin
=
timeIntervalList
.
find
((
item
)
=>
item
.
key
===
dataThinKey
);
config
.
zoom
=
activeTabKey
===
'curve'
?
''
:
dataThin
?.
zoom
??
''
;
config
.
unit
=
activeTabKey
===
'curve'
?
''
:
dataThin
?.
unit
??
''
;
config
.
dataThin
=
activeTabKey
===
'curve'
?
true
:
config
.
dataThin
;
// 曲线强制抽稀
return
config
;
},
[
configDependence
,
dataThinKey
,
activeTabKey
]);
// 图表居中
const
[
curveCenter
,
chartGrid
]
=
useMemo
(()
=>
{
const
curveCenter
=
checkboxData
.
find
((
item
)
=>
item
.
key
===
'curveCenter'
)?.
checked
;
const
chartGrid
=
checkboxData
.
find
((
item
)
=>
item
.
key
===
'chartGrid'
)?.
checked
;
return
[
curveCenter
,
chartGrid
];
},
[
checkboxData
]);
// 自定义模式: 快速选择
const
onCustomerTimeChange
=
(
key
)
=>
{
setCustomerChecked
(
key
);
!!
customerTime
&&
setCustomerTime
(
null
);
};
// 自定义模式: 自定义时间选择
const
onCustomerRangeChange
=
(
value
)
=>
{
if
(
!
value
)
{
// 时间清空,回到默认时间选择
setCustomerChecked
(
defaultChecked
);
setCustomerTime
(
value
);
}
else
{
setCustomerChecked
(
null
);
setCustomerTime
(
value
);
}
};
// 同期对比模式: 选择(日/月)
const
onContrastChange
=
(
value
)
=>
{
setContrastOption
(
value
);
setDatePickerArr
([...
DefaultDatePicker
(
value
)]);
};
// 自定义模式: 自定义时间选择
const
onCustomerRangeChange
=
(
value
)
=>
{
if
(
!
value
)
{
// 时间清空,回到默认时间选择
setCustomerChecked
(
defaultChecked
);
setCustomerTime
(
value
);
}
else
{
setCustomerChecked
(
null
);
setCustomerTime
(
value
);
}
};
// 同期对比模式: 时间段选择
const
onContrastPickerChange
=
(
date
,
dateString
,
item
)
=>
{
const
arr
=
[...
datePickerArr
];
arr
.
forEach
((
child
)
=>
{
if
(
child
.
key
===
item
.
key
)
{
child
.
value
=
date
;
}
});
setDatePickerArr
(
arr
);
};
// 同期对比模式: 新增日期选择组件
const
handleAddDatePicker
=
()
=>
{
setDatePickerArr
([
...
datePickerArr
,
{
key
:
datePickerArr
[
datePickerArr
.
length
-
1
].
key
+
1
,
value
:
''
,
},
]);
};
// 同期对比模式: 删除日期选择组件
const
handleDeleteDatePicker
=
(
index
)
=>
{
const
arr
=
[...
datePickerArr
];
arr
.
splice
(
index
,
1
);
setDatePickerArr
(
arr
);
};
// 时间设置切换(自定义/同期对比)
const
onTimeSetChange
=
(
e
)
=>
{
setTimeValue
(
e
.
target
.
value
);
if
(
e
.
target
.
value
===
'contrast'
)
{
// 同期对比
onContrastChange
(
contrastOption
);
}
else
{
// 自定义
// 不需要处理
}
};
// 同期对比模式: 选择(日/月)
const
onContrastChange
=
(
value
)
=>
{
setContrastOption
(
value
);
setDatePickerArr
([...
DefaultDatePicker
(
value
)]);
};
const
renderTimeOption
=
()
=>
{
return
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-date`
)}
>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
时间选择
<
/div
>
<
Radio
.
Group
value
=
{
timeValue
}
onChange
=
{
onTimeSetChange
}
>
<
Radio
.
Button
value
=
"customer"
>
自定义
<
/Radio.Button
>
<
Radio
.
Button
value
=
"contrast"
>
同期对比
<
/Radio.Button
>
<
/Radio.Group
>
{
timeValue
===
'customer'
&&
(
// 自定义
<>
<
TimeRangePicker
onChange
=
{
onCustomerTimeChange
}
value
=
{
customerChecked
}
dataSource
=
{
timeList
}
/
>
<
RangePicker
className
=
{
classNames
(
`
${
prefixCls
}
-custime-customer`
)}
onChange
=
{
onCustomerRangeChange
}
value
=
{
customerTime
}
showTime
/>
<
/
>
)}
{
timeValue
===
'contrast'
&&
(
// 同期对比
<>
<
Select
value
=
{
contrastOption
}
style
=
{{
width
:
60
}}
onChange
=
{
onContrastChange
}
>
<
Option
value
=
"day"
>
日
<
/Option
>
<
Option
value
=
"month"
>
月
<
/Option
>
<
/Select
>
{
datePickerArr
.
map
((
child
,
index
)
=>
(
<
div
key
=
{
child
.
key
}
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-list`
)}
>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-wrap`
)}
>
<
DatePicker
picker
=
{
contrastOption
}
value
=
{
child
.
value
}
onChange
=
{(
date
,
dateString
)
=>
onContrastPickerChange
(
date
,
dateString
,
child
)}
/
>
{
datePickerArr
.
length
>
2
&&
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-delete`
)}
onClick
=
{()
=>
handleDeleteDatePicker
(
index
)}
>
<
CloseCircleFilled
/>
<
/div
>
)}
<
/div
>
{
index
<
datePickerArr
.
length
-
1
&&
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-connect`
)}
>
与
<
/div
>
)}
<
/div
>
))}
{
datePickerArr
.
length
<
5
&&
<
PlusCircleOutlined
onClick
=
{
handleAddDatePicker
}
/>
}
<
/
>
)}
<
/div
>
);
};
// 曲线设置项选择/取消
const
onCheckboxChange
=
(
e
,
key
)
=>
{
let
data
=
[...
checkboxData
];
data
.
forEach
((
item
)
=>
{
if
(
item
.
key
===
key
)
{
item
.
checked
=
e
.
target
.
checked
;
}
});
setCheckboxData
(
data
);
};
// 同期对比模式: 时间段选择
const
onContrastPickerChange
=
(
date
,
dateString
,
item
)
=>
{
const
arr
=
[...
datePickerArr
];
arr
.
forEach
((
child
)
=>
{
if
(
child
.
key
===
item
.
key
)
{
child
.
value
=
date
;
}
});
setDatePickerArr
(
arr
);
};
// 数据抽稀时间间隔
const
onTimeIntervalChange
=
(
value
)
=>
{
setDataThinKey
(
value
);
};
// 同期对比模式: 新增日期选择组件
const
handleAddDatePicker
=
()
=>
{
setDatePickerArr
([
...
datePickerArr
,
{
key
:
datePickerArr
[
datePickerArr
.
length
-
1
].
key
+
1
,
value
:
''
,
},
]);
};
const
renderCheckbox
=
(
child
)
=>
{
const
curveAccess
=
activeTabKey
===
'curve'
&&
child
.
showInCurve
;
const
tableAccess
=
activeTabKey
===
'table'
&&
child
.
showInTable
;
const
gridOptions
=
[
'curveCenter'
];
// 同期对比模式: 删除日期选择组件
const
handleDeleteDatePicker
=
(
index
)
=>
{
const
arr
=
[...
datePickerArr
];
arr
.
splice
(
index
,
1
);
setDatePickerArr
(
arr
);
};
if
(
grid
&&
curveAccess
&&
gridOptions
.
indexOf
(
child
.
key
)
===
-
1
)
return
null
;
return
(
(
curveAccess
||
tableAccess
)
&&
(
<>
<
Checkbox
checked
=
{
child
.
checked
}
onChange
=
{(
e
)
=>
onCheckboxChange
(
e
,
child
.
key
)}
>
{
child
.
label
}
<
/Checkbox
>
{
child
.
tooltip
&&
(
<
Tooltip
title
=
{
child
.
tooltip
}
>
<
QuestionCircleFilled
className
=
{
`
${
prefixCls
}
-question`
}
/
>
<
/Tooltip
>
)}
<
/
>
)
);
};
// 时间设置切换(自定义/同期对比)
const
onTimeSetChange
=
(
e
)
=>
{
setTimeValue
(
e
.
target
.
value
);
if
(
e
.
target
.
value
===
'contrast'
)
{
// 同期对比
onContrastChange
(
contrastOption
);
}
else
{
// 自定义
// 不需要处理
}
};
const
renderCurveOption
=
()
=>
{
return
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-cover`
)}
>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
曲线设置
<
/div
>
{
checkboxData
.
map
((
child
)
=>
{
const
box
=
renderCheckbox
(
child
);
if
(
!
box
)
return
null
;
return
(
<
div
key
=
{
child
.
key
}
className
=
{
`
${
prefixCls
}
-cover-item`
}
>
{
box
}
const
renderTimeOption
=
()
=>
{
return
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-date`
)}
>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
时间选择
<
/div
>
<
Radio
.
Group
value
=
{
timeValue
}
onChange
=
{
onTimeSetChange
}
>
<
Radio
.
Button
value
=
"customer"
>
自定义
<
/Radio.Button
>
<
Radio
.
Button
value
=
"contrast"
>
同期对比
<
/Radio.Button
>
<
/Radio.Group
>
{
timeValue
===
'customer'
&&
(
// 自定义
<>
<
TimeRangePicker
onChange
=
{
onCustomerTimeChange
}
value
=
{
customerChecked
}
dataSource
=
{
timeList
}
/
>
<
RangePicker
className
=
{
classNames
(
`
${
prefixCls
}
-custime-customer`
)}
onChange
=
{
onCustomerRangeChange
}
value
=
{
customerTime
}
showTime
/>
<
/
>
)}
{
timeValue
===
'contrast'
&&
(
// 同期对比
<>
<
Select
value
=
{
contrastOption
}
style
=
{{
width
:
60
}}
onChange
=
{
onContrastChange
}
>
<
Option
value
=
"day"
>
日
<
/Option
>
<
Option
value
=
"month"
>
月
<
/Option
>
<
/Select
>
{
datePickerArr
.
map
((
child
,
index
)
=>
(
<
div
key
=
{
child
.
key
}
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-list`
)}
>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-wrap`
)}
>
<
DatePicker
picker
=
{
contrastOption
}
value
=
{
child
.
value
}
onChange
=
{(
date
,
dateString
)
=>
onContrastPickerChange
(
date
,
dateString
,
child
)}
/
>
{
datePickerArr
.
length
>
2
&&
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-delete`
)}
onClick
=
{()
=>
handleDeleteDatePicker
(
index
)}
>
<
CloseCircleFilled
/>
<
/div
>
)}
<
/div
>
{
index
<
datePickerArr
.
length
-
1
&&
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-contrast-connect`
)}
>
与
<
/div
>
)}
<
/div
>
))}
{
datePickerArr
.
length
<
5
&&
<
PlusCircleOutlined
onClick
=
{
handleAddDatePicker
}
/>
}
<
/
>
)}
<
/div
>
);
})}
{
activeTabKey
===
'table'
&&
(
<
Select
value
=
{
dataThinKey
}
style
=
{{
width
:
90
}}
onChange
=
{
onTimeIntervalChange
}
disabled
=
{
!
dataConfig
.
dataThin
}
>
{
timeIntervalList
.
map
((
child
)
=>
(
<
Option
key
=
{
child
.
key
}
unit
=
{
child
.
unit
}
value
=
{
child
.
key
}
>
{
child
.
name
}
<
/Option
>
))}
<
/Select
>
)}
<
/div
>
);
};
const
exportExcelBtn
=
()
=>
{
deviceParams
.
forEach
((
i
,
r
)
=>
{
let
timeFrom
=
dateRange
[
r
]?.
dateFrom
||
moment
().
format
(
startFormat
);
let
timeTo
=
dateRange
[
r
]?.
dateTo
||
moment
().
format
(
timeFormat
);
let
fileName
=
`数据报表-
${
i
.
deviceType
}
-
${
i
.
deviceCode
}
-
${
moment
(
timeFrom
).
format
(
dateFormat
,
)}
至
${
moment
(
timeTo
).
format
(
dateFormat
)}
`
;
getExportDeviceHistoryUrl
({
deviceType
:
i
.
deviceType
,
deviceCode
:
i
.
deviceCode
,
quotas
:
i
.
sensors
,
startTime
:
timeFrom
,
endTime
:
timeTo
,
fileName
:
fileName
,
})
.
then
((
res
)
=>
{
if
(
res
&&
res
.
code
===
-
1
)
return
message
.
error
(
res
.
msg
);
const
url
=
`
${
window
.
location
.
origin
}
/PandaCore/GCK/FileHandleContoller/Download/name?name=
${
res
.
data
}
&_site=
${
globalConfig
?.
userInfo
?.
site
}
`;
const aDom = document.createElement('a');
aDom.href = url;
aDom.click();
aDom.remove();
})
.catch((err) => {});
});
};
);
};
const handleTableData = (data) => {
const ignoreOutliers = checkboxData.find((item) => item.key === 'ignoreOutliers').checked;
const dataIndexAccess = (dataItem, index) => {
const { stationCode, sensorName } = dataItem;
return `
$
{
stationCode
}
-
$
{
sensorName
}
-
$
{
index
}
`;
// 曲线设置项选择/取消
const
onCheckboxChange
=
(
e
,
key
)
=>
{
let
data
=
[...
checkboxData
];
let
_index
=
data
.
findIndex
(
item
=>
item
.
key
===
'justLine'
);
// 仅查看曲线会在勾选了数据滤波后展示
let
_index1
=
data
.
findIndex
(
item
=>
item
.
key
===
'ignoreOutliers'
);
// 仅查看曲线会在勾选了数据滤波后展示
data
.
forEach
((
item
)
=>
{
if
(
item
.
key
===
key
)
{
item
.
checked
=
e
.
target
.
checked
;
}
});
if
(
key
===
'ignoreOutliers'
)
{
data
[
_index
].
showInCurve
=
e
.
target
.
checked
;
data
[
_index
].
checked
=
e
.
target
.
checked
;
}
if
(
key
===
'chartType'
)
{
data
[
_index1
].
showInCurve
=
e
.
target
.
value
;
data
[
_index1
].
checked
=
false
;
}
debugger
setCheckboxData
(
data
);
};
let format = timeFormat;
if (timeValue === 'contrast')
{
format = contrastOption === 'day' ? '2020-01-01 HH:mm:00' : '2020-01-DD HH:mm:00'
;
}
// 数据抽稀时间间隔
const
onTimeIntervalChange
=
(
value
)
=>
{
setDataThinKey
(
value
)
;
}
;
// 处理表头数据
const columnsData = data.map((item, index) => {
const { stationCode, equipmentName, sensorName, unit, dataModel } = item;
const dataIndex = dataIndexAccess(item, index);
let col = {
title: `
$
{
equipmentName
}
-
$
{
sensorName
}
$
{
unit
?
`(
${
unit
}
)`
:
''
}
`,
dataIndex: dataIndex,
key: dataIndex,
ellipsis: true,
align: 'center',
};
// 同期对比
if (timeValue === 'contrast' && dataModel[0]) {
const time = item.dataModel[0].pt
.slice(0, contrastOption === 'day' ? 10 : 7)
.replace(/-/g, '');
col.title = `
$
{
equipmentName
}
-
$
{
sensorName
}
-
$
{
time
}
`;
}
return col;
});
const
renderCheckbox
=
(
child
)
=>
{
const
curveAccess
=
activeTabKey
===
'curve'
&&
child
.
showInCurve
;
const
tableAccess
=
activeTabKey
===
'table'
&&
child
.
showInTable
;
const
gridOptions
=
[
'curveCenter'
];
if
(
grid
&&
curveAccess
&&
gridOptions
.
indexOf
(
child
.
key
)
===
-
1
)
return
null
;
return
(
(
curveAccess
||
tableAccess
)
&&
(
<>
<
Checkbox
checked
=
{
child
.
checked
}
onChange
=
{(
e
)
=>
onCheckboxChange
(
e
,
child
.
key
)}
>
{
child
.
label
}
<
/Checkbox
>
{
child
.
tooltip
&&
(
<
Tooltip
title
=
{
child
.
tooltip
}
>
<
QuestionCircleFilled
className
=
{
`
${
prefixCls
}
-question`
}
/
>
<
/Tooltip
>
)}
<
/
>
)
);
};
// 格式化时间对齐数据, 生成行数
const timeData = {};
const
renderCurveOption
=
(
isChart
,
isSingle
)
=>
{
return
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-cover`
)}
>
{
isChart
&&
isSingle
?
<>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
曲线形态
<
/div
>
<
Radio
.
Group
value
=
{
chartType
}
onChange
=
{(
e
)
=>
{
let
_value
=
e
.
target
.
value
;
setChartType
(
_value
);
onCheckboxChange
({
target
:
{
value
:
_value
!==
'boxChart'
}},
'chartType'
)
}}
>
<
Radio
.
Button
value
=
{
'lineChart'
}
>
线形图
<
/Radio.Button
>
<
Radio
.
Button
value
=
{
'boxChart'
}
>
箱线图
<
/Radio.Button
>
<
/Radio.Group></
>
:
''
}
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
曲线设置
<
/div
>
{
checkboxData
.
map
((
child
)
=>
{
const
box
=
renderCheckbox
(
child
);
if
(
!
box
)
return
null
;
return
(
<
div
key
=
{
child
.
key
}
className
=
{
`
${
prefixCls
}
-cover-item`
}
>
{
box
}
<
/div
>
);
})}
{
activeTabKey
===
'table'
&&
(
<
Select
value
=
{
dataThinKey
}
style
=
{{
width
:
90
}}
onChange
=
{
onTimeIntervalChange
}
disabled
=
{
!
dataConfig
.
dataThin
}
>
{
timeIntervalList
.
map
((
child
)
=>
(
<
Option
key
=
{
child
.
key
}
unit
=
{
child
.
unit
}
value
=
{
child
.
key
}
>
{
child
.
name
}
<
/Option
>
))}
<
/Select
>
)}
<
/div
>
);
};
const buildDefaultData = (time) => {
const obj = { key: time, time: time };
data.forEach((item, index) => {
const dataIndex = dataIndexAccess(item, index);
obj[dataIndex] = '';
});
return obj;
const
exportExcelBtn
=
()
=>
{
deviceParams
.
forEach
((
i
,
r
)
=>
{
let
timeFrom
=
dateRange
[
r
]?.
dateFrom
||
moment
().
format
(
startFormat
);
let
timeTo
=
dateRange
[
r
]?.
dateTo
||
moment
().
format
(
timeFormat
);
let
fileName
=
`数据报表-
${
i
.
deviceType
}
-
${
i
.
deviceCode
}
-
${
moment
(
timeFrom
).
format
(
dateFormat
,
)}
至
${
moment
(
timeTo
).
format
(
dateFormat
)}
`
;
getExportDeviceHistoryUrl
({
deviceType
:
i
.
deviceType
,
deviceCode
:
i
.
deviceCode
,
quotas
:
i
.
sensors
,
startTime
:
timeFrom
,
endTime
:
timeTo
,
fileName
:
fileName
,
})
.
then
((
res
)
=>
{
if
(
res
&&
res
.
code
===
-
1
)
return
message
.
error
(
res
.
msg
);
const
url
=
`
${
window
.
location
.
origin
}
/PandaCore/GCK/FileHandleContoller/Download/name?name=
${
res
.
data
}
&_site=
${
globalConfig
?.
userInfo
?.
site
}
`;
const aDom = document.createElement('a');
aDom.href = url;
aDom.click();
aDom.remove();
})
.catch((err) => {
});
});
};
data.forEach((item, index) => {
const { stationCode, sensorName, dataModel } = item;
dataModel.forEach((data) => {
const formatTime = moment(data.pt).format(format);
let time = formatTime;
const handleTableData = (data) => {
const ignoreOutliers = checkboxData.find((item) => item.key === 'ignoreOutliers').checked;
const dataIndexAccess = (dataItem, index) => {
const {stationCode, sensorName} = dataItem;
return `
$
{
stationCode
}
-
$
{
sensorName
}
-
$
{
index
}
`;
};
let format = timeFormat;
if (timeValue === 'contrast') {
time = time.slice(contrastOption === 'day' ? 11 : 8, 16)
;
format = contrastOption === 'day' ? '2020-01-01 HH:mm:00' : '2020-01-DD HH:mm:00'
;
}
timeData[formatTime] = timeData[formatTime] || buildDefaultData(time);
});
});
// 处理表头数据
const columnsData = data.map((item, index) => {
const {stationCode, equipmentName, sensorName, unit, dataModel} = item;
const dataIndex = dataIndexAccess(item, index);
let col = {
title: `
$
{
equipmentName
}
-
$
{
sensorName
}
$
{
unit
?
`(
${
unit
}
)`
:
''
}
`,
dataIndex: dataIndex,
key: dataIndex,
ellipsis: true,
align: 'center',
};
// 同期对比
if (timeValue === 'contrast' && dataModel[0]) {
const time = item.dataModel[0].pt
.slice(0, contrastOption === 'day' ? 10 : 7)
.replace(/-/g, '');
col.title = `
$
{
equipmentName
}
-
$
{
sensorName
}
-
$
{
time
}
`;
}
return col;
});
// 处理表格数据
data.forEach((child, index) => {
const { dataModel } = child;
const dataIndex = dataIndexAccess(child, index);
dataModel.forEach((value, j) => {
const formatTime = moment(value.pt).format(format);
const dataRow = timeData[formatTime];
if (dataRow) {
dataRow[dataIndex] = value.pv === null || value.pv === undefined ? '' : value.pv;
}
});
});
const timeSort = (a, b) => {
let aa = a,
bb = b;
if (timeValue === 'contrast') {
aa = a.slice(contrastOption === 'day' ? 11 : 8, 16);
bb = b.slice(contrastOption === 'day' ? 11 : 8, 16);
}
return aa.localeCompare(bb);
// 格式化时间对齐数据, 生成行数
const timeData = {};
const buildDefaultData = (time) => {
const obj = {key: time, time: time};
data.forEach((item, index) => {
const dataIndex = dataIndexAccess(item, index);
obj[dataIndex] = '';
});
return obj;
};
data.forEach((item, index) => {
const {stationCode, sensorName, dataModel} = item;
dataModel.forEach((data) => {
const formatTime = moment(data.pt).format(format);
let time = formatTime;
if (timeValue === 'contrast') {
time = time.slice(contrastOption === 'day' ? 11 : 8, 16);
}
timeData[formatTime] = timeData[formatTime] || buildDefaultData(time);
});
});
// 处理表格数据
data.forEach((child, index) => {
const {dataModel} = child;
const dataIndex = dataIndexAccess(child, index);
dataModel.forEach((value, j) => {
const formatTime = moment(value.pt).format(format);
const dataRow = timeData[formatTime];
if (dataRow) {
dataRow[dataIndex] = value.pv === null || value.pv === undefined ? '' : value.pv;
}
});
});
const timeSort = (a, b) => {
let aa = a,
bb = b;
if (timeValue === 'contrast') {
aa = a.slice(contrastOption === 'day' ? 11 : 8, 16);
bb = b.slice(contrastOption === 'day' ? 11 : 8, 16);
}
return aa.localeCompare(bb);
};
const times = Object.keys(timeData).sort(timeSort);
const tableData = times.map((time) => timeData[time]);
setColumns([timeColumn, ...columnsData]);
setTableData(tableData);
};
const times = Object.keys(timeData).sort(timeSort);
const tableData = times.map((time) => timeData[time]);
setColumns([timeColumn, ...columnsData]);
setTableData(tableData);
};
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState([]);
const beforChangeParams = (value = {}) => {
if (!needMarkLine) return Promise.resolve();
return getDeviceAlarmScheme({
data: deviceParams.map((item) => ({
deviceType: item.deviceType,
deviceCode: item.deviceCode,
pointAddressID: item.pointAddressID,
sensorName: item.sensors,
})),
})
.then((res) => {
if (res.code === 0) setDeviceAlarmSchemes(res.data || []);
else setDeviceAlarmSchemes([]);
return Promise.resolve();
})
.catch((err) => {
setDeviceAlarmSchemes([]);
return Promise.resolve();
});
};
const handleDataThinKey = (diffDays) => {
// edit by zy 根据选择的时长控制抽稀频度
if (diffDays >= 7 && diffDays < 15) {
return { unit: 'h', zoom: '2' };
} else if (diffDays >= 15 && diffDays < 30) {
return { unit: 'h', zoom: '4' };
} else if (diffDays >= 30) {
return { unit: 'h', zoom: '6' };
} else {
return {};
}
};
// 处理接口服务参数的变化
const onChangeParams = (value = {}) => {
const { dateRange, isDilute, ignoreOutliers, zoom, unit } = value;
const requestArr = [];
const acrossTables = [];
deviceParams.forEach((i) => {
if (i.sensors && i.deviceCode && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID']));
});
if (!acrossTables?.length) {
handleTableData([]);
setChartDataSource([]);
return;
}
dateRange.forEach((item) => {
const param = {
isDilute,
zoom,
unit,
ignoreOutliers,
isVertical: false, // 是否查询竖表
dateFrom: item.dateFrom,
dateTo: item.dateTo,
acrossTables,
};
let diffDays = moment(item.dateTo).diff(moment(item.dateFrom), 'days');
let zoomParam = activeTabKey === 'curve' ? handleDataThinKey(diffDays) : {};
requestArr.push(getHistoryInfo({ ...param, ...zoomParam }));
});
setLoading(true);
Promise.all(requestArr).then((results) => {
if (results.length) {
let data = [];
results.forEach((res, index) => {
const { dateFrom, dateTo } = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) {
res.data.forEach((d) => {
d.dateFrom = dateFrom;
d.dateTo = dateTo;
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState([]);
const beforChangeParams = (value = {}) => {
if (!needMarkLine) return Promise.resolve();
return getDeviceAlarmScheme({
data: deviceParams.map((item) => ({
deviceType: item.deviceType,
deviceCode: item.deviceCode,
pointAddressID: item.pointAddressID,
sensorName: item.sensors,
})),
})
.then((res) => {
if (res.code === 0) setDeviceAlarmSchemes(res.data || []);
else setDeviceAlarmSchemes([]);
return Promise.resolve();
})
.catch((err) => {
setDeviceAlarmSchemes([]);
return Promise.resolve();
});
deviceParams.forEach((p) => {
// 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? [];
const list = sensors.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
);
dataItem.dateFrom = dateFrom;
dataItem.dateTo = dateTo;
return dataItem;
});
data = data.concat(list);
};
const handleDataThinKey = (diffDays) => {
// edit by zy 根据选择的时长控制抽稀频度
if (diffDays >= 7 && diffDays < 15) {
return {unit: 'h', zoom: '2'};
} else if (diffDays >= 15 && diffDays < 30) {
return {unit: 'h', zoom: '4'};
} else if (diffDays >= 30) {
return {unit: 'h', zoom: '6'};
} else if (diffDays < 7 && diffDays >= 2) {
return {unit: 'min', zoom: '40'};
} else if (diffDays < 2 && diffDays >= 1) {
return {unit: 'min', zoom: '30'};
} else {
return {unit: 'min', zoom: '10'};
}
};
// 处理接口服务参数的变化
const onChangeParams = (value = {}) => {
const {dateRange, isDilute, ignoreOutliers, zoom, unit} = value;
const requestArr = [];
const acrossTables = [];
deviceParams.forEach((i) => {
if (i.sensors && i.deviceCode && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID']));
});
if (!acrossTables?.length) {
handleTableData([]);
setChartDataSource([]);
return;
}
dateRange.forEach((item) => {
let _showLine = checkboxData.find(item => item.key === 'justLine');
const param = {
isDilute,
zoom,
unit,
ignoreOutliers,
isVertical: false, // 是否查询竖表
dateFrom: item.dateFrom,
dateTo: item.dateTo,
acrossTables,
isBoxPlots: isBoxPlots
};
let diffDays = moment(item.dateTo).diff(moment(item.dateFrom), 'days');
let zoomParam = activeTabKey === 'curve' ? handleDataThinKey(diffDays) : {};
requestArr.push(getHistoryInfo({...param, ...zoomParam}));
});
setLoading(true);
Promise.all(requestArr).then((results) => {
if (results.length) {
let data = [];
results.forEach((res, index) => {
const {dateFrom, dateTo} = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) {
res.data.forEach((d) => {
d.dateFrom = dateFrom;
d.dateTo = dateTo;
});
deviceParams.forEach((p) => {
// 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? [];
const list = sensors.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
);
dataItem.dateFrom = dateFrom;
dataItem.dateTo = dateTo;
return dataItem;
});
data = data.concat(list);
});
}
});
setLoading(false);
handleTableData(data);
setChartDataSource(data);
}
});
};
useEffect(() => {
const {dataThin, ignoreOutliers, zoom, unit} = dataConfig;
beforChangeParams().finally(() => {
onChangeParams({
isDilute: dataThin,
ignoreOutliers,
zoom,
unit,
dateRange,
isBoxPlots: isBoxPlots
});
}
});
setLoading(false);
handleTableData(data);
setChartDataSource(data);
}
});
};
useEffect(() => {
const { dataThin, ignoreOutliers, zoom, unit } = dataConfig;
beforChangeParams().finally(() => {
onChangeParams({
isDilute: dataThin,
ignoreOutliers,
zoom,
unit,
dateRange,
});
});
}, [dateRange, dataConfig, deviceParams]);
const renderPanel = (model) => {
if (model === 'curve') {
return (
<>
<div className={`
$
{
prefixCls
}
-
options
`}>
{renderTimeOption()}
{renderCurveOption()}
</div>
<div className={`
$
{
prefixCls
}
-
content
`}>
{!chartDataSource.length ? (
<PandaEmpty />
) : grid === true ? (
<GridChart
curveCenter={curveCenter}
prefixCls={prefixCls}
dataSource={chartDataSource}
contrast={timeValue === 'contrast'}
contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes}
/>
) : (
<SimgleChart
curveCenter={curveCenter}
showGridLine={chartGrid}
prefixCls={prefixCls}
dataSource={chartDataSource}
contrast={timeValue === 'contrast'}
contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes}
/>
)}
</div>
</>
);
}
if (model === 'table') {
return (
<>
<div className={`
$
{
prefixCls
}
-
options
`}>
{renderTimeOption()}
{renderCurveOption()}
</div>
<div className={`
$
{
prefixCls
}
-
content
`}>
{chartDataSource.length > 0 ? (
<BasicTable
dataSource={tableData}
columns={columns}
{...tableProps}
pagination={false}
onChange={() => {}}
/>
) : (
<PandaEmpty />
)}
</div>
</>
);
}
};
return (
<div className={classNames(prefixCls)}>
<Spin spinning={loading} wrapperClassName={classNames(`
$
{
prefixCls
}
-
spin
`)}>
{showModels.length === 1 && (
<div className={`
$
{
prefixCls
}
-
single
-
panel
`}>{renderPanel(showModels[0])}</div>
)}
{showModels.length > 1 && (
<Tabs
activeKey={activeTabKey}
onChange={(key) => setActiveTabKey(key)}
centered
tabBarExtraContent={{
left: <h3>{title}</h3>,
right: (
<div className={`
$
{
prefixCls
}
-
extra
-
right
`}>
{activeTabKey === 'table' && (
<Button type="link" onClick={exportExcelBtn}>
<DownloadOutlined />
下载
</Button>
)}
</div>
),
}}
>
<Tabs.TabPane key="curve" tab="曲线">
{renderPanel('curve')}
</Tabs.TabPane>
<Tabs.TabPane key="table" tab="表格">
{renderPanel('table')}
</Tabs.TabPane>
</Tabs>
)}
</Spin>
</div>
);
}, [dateRange, dataConfig, deviceParams, chartType]);
const renderPanel = (model) => {
if (model === 'curve') {
return (
<>
<div className={`
$
{
prefixCls
}
-
options
`}>
{renderTimeOption()}
{renderCurveOption(true, (deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1))}
</div>
<div className={`
$
{
prefixCls
}
-
content
`}>
{!chartDataSource.length ? (
<PandaEmpty/>
) : grid === true ? (
<GridChart
curveCenter={curveCenter}
prefixCls={prefixCls}
dataSource={chartDataSource}
contrast={timeValue === 'contrast'}
contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes}
/>
) : (
<SimgleChart
curveCenter={curveCenter}
showGridLine={chartGrid}
prefixCls={prefixCls}
dataSource={chartDataSource}
justLine={!!checkboxData.find(item => item.key === 'justLine' && item.checked)}
chartType={isBoxPlots ? chartType : null}
contrast={timeValue === 'contrast'}
contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes}
/>
)}
</div>
</>
);
}
if (model === 'table') {
return (
<>
<div className={`
$
{
prefixCls
}
-
options
`}>
{renderTimeOption()}
{renderCurveOption()}
</div>
<div className={`
$
{
prefixCls
}
-
content
`}>
{chartDataSource.length > 0 ? (
<BasicTable
dataSource={tableData}
columns={columns}
{...tableProps}
pagination={false}
onChange={() => {
}}
/>
) : (
<PandaEmpty/>
)}
</div>
</>
);
}
};
return (
<div className={classNames(prefixCls)}>
<Spin spinning={loading} wrapperClassName={classNames(`
$
{
prefixCls
}
-
spin
`)}>
{showModels.length === 1 && (
<div className={`
$
{
prefixCls
}
-
single
-
panel
`}>{renderPanel(showModels[0])}</div>
)}
{showModels.length > 1 && (
<Tabs
activeKey={activeTabKey}
onChange={(key) => setActiveTabKey(key)}
centered
tabBarExtraContent={{
left: <h3>{title}</h3>,
right: (
<div className={`
$
{
prefixCls
}
-
extra
-
right
`}>
{activeTabKey === 'table' && (
<Button type="link" onClick={exportExcelBtn}>
<DownloadOutlined/>
下载
</Button>
)}
</div>
),
}}
>
<Tabs.TabPane key="curve" tab="曲线">
{renderPanel('curve')}
</Tabs.TabPane>
<Tabs.TabPane key="table" tab="表格">
{renderPanel('table')}
</Tabs.TabPane>
</Tabs>
)}
</Spin>
</div>
);
};
HistoryView.propTypes = {
grid: PropTypes.bool,
title: PropTypes.string,
defaultChecked: PropTypes.oneOf(['twelveHours', 'roundClock', 'oneWeek', 'oneMonth']),
tableProps: PropTypes.object,
deviceParams: PropTypes.arrayOf(
PropTypes.objectOf({
deviceCode: PropTypes.string,
sensors: PropTypes.string,
deviceType: PropTypes.string,
pointAddressID: PropTypes.number, // 可选,配置了将会查询相关报警方案配置
}),
),
defaultModel: PropTypes.oneOf(['curve', 'table']),
showModels: PropTypes.arrayOf(PropTypes.oneOf(['curve', 'table'])),
grid: PropTypes.bool,
title: PropTypes.string,
defaultChecked: PropTypes.oneOf(['twelveHours', 'roundClock', 'oneWeek', 'oneMonth']),
tableProps: PropTypes.object,
deviceParams: PropTypes.arrayOf(
PropTypes.objectOf({
deviceCode: PropTypes.string,
sensors: PropTypes.string,
deviceType: PropTypes.string,
pointAddressID: PropTypes.number, // 可选,配置了将会查询相关报警方案配置
}),
),
defaultModel: PropTypes.oneOf(['curve', 'table']),
showModels: PropTypes.arrayOf(PropTypes.oneOf(['curve', 'table'])),
};
HistoryView.defaultProps = {
grid: false,
title: '指标曲线',
defaultChecked: 'roundClock',
tableProps: {},
defaultModel: 'curve',
showModels: ['curve', 'table'],
needMarkLine: true,
grid: false,
title: '指标曲线',
defaultChecked: 'roundClock',
tableProps: {},
defaultModel: 'curve',
showModels: ['curve', 'table'],
needMarkLine: true,
};
export default HistoryView;
packages/extend-components/EC_HistoryView/src/utils.js
View file @
d35c48a5
...
...
@@ -13,13 +13,13 @@ const axisWidth = 40;
* @returns
*/
const
nameFormatter
=
(
data
,
contrast
,
contrastOption
,
nameWithSensor
)
=>
{
const
{
equipmentName
,
sensorName
,
unit
,
dataModel
,
dateFrom
,
dateTo
}
=
data
;
let
name
=
nameWithSensor
?
`
${
equipmentName
}
-
${
sensorName
}
`
:
equipmentName
;
if
(
contrast
)
{
const
time
=
dateFrom
.
slice
(
0
,
contrastOption
===
'day'
?
10
:
7
).
replace
(
/-/g
,
''
);
name
=
`
${
name
}
-
${
time
}
`
;
}
return
name
;
const
{
equipmentName
,
sensorName
,
unit
,
dataModel
,
dateFrom
,
dateTo
}
=
data
;
let
name
=
nameWithSensor
?
`
${
equipmentName
}
-
${
sensorName
}
`
:
equipmentName
;
if
(
contrast
)
{
const
time
=
dateFrom
.
slice
(
0
,
contrastOption
===
'day'
?
10
:
7
).
replace
(
/-/g
,
''
);
name
=
`
${
name
}
-
${
time
}
`
;
}
return
name
;
};
/**
...
...
@@ -31,12 +31,12 @@ const nameFormatter = (data, contrast, contrastOption, nameWithSensor) => {
* @returns 图表系列数据, [[DateTime, value]]
*/
const
dataAccessor
=
(
data
,
contrast
,
contrastOption
)
=>
{
const
{
dataModel
}
=
data
;
const
formatStr
=
contrastOption
===
'day'
?
'2020-01-01 HH:mm:00'
:
'2020-01-DD HH:mm:00'
;
return
dataModel
.
map
((
item
)
=>
{
const
time
=
contrast
?
moment
(
item
.
pt
).
format
(
formatStr
)
:
item
.
pt
;
return
[
moment
(
time
).
valueOf
(),
item
.
pv
];
});
const
{
dataModel
}
=
data
;
const
formatStr
=
contrastOption
===
'day'
?
'2020-01-01 HH:mm:00'
:
'2020-01-DD HH:mm:00'
;
return
dataModel
.
map
((
item
)
=>
{
const
time
=
contrast
?
moment
(
item
.
pt
).
format
(
formatStr
)
:
item
.
pt
;
return
[
moment
(
time
).
valueOf
(),
item
.
pv
];
});
};
/**
...
...
@@ -46,8 +46,8 @@ const dataAccessor = (data, contrast, contrastOption) => {
* @returns Null/areaStyle, 为null显示曲线图, 为areaStyle对象显示为面积图.
*/
const
areaStyleFormatter
=
(
data
)
=>
{
const
{
sensorName
}
=
data
;
return
sensorName
&&
sensorName
.
indexOf
(
'流量'
)
>
-
1
?
{}
:
null
;
const
{
sensorName
}
=
data
;
return
sensorName
&&
sensorName
.
indexOf
(
'流量'
)
>
-
1
?
{}
:
null
;
};
/**
...
...
@@ -57,110 +57,110 @@ const areaStyleFormatter = (data) => {
* @returns
*/
const
minMax
=
(
data
)
=>
{
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
];
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
];
};
const
markLineItem
=
(
name
,
value
,
color
)
=>
{
return
{
name
:
name
,
yAxis
:
value
,
value
:
value
,
lineStyle
:
{
color
:
color
||
'#000'
,
},
label
:
{
position
:
'insideEndTop'
,
color
:
color
||
'#000'
,
formatter
:
function
()
{
return
`
${
name
}
:
${
value
}
`
;
},
},
};
return
{
name
:
name
,
yAxis
:
value
,
value
:
value
,
lineStyle
:
{
color
:
color
||
'#000'
,
},
label
:
{
position
:
'insideEndTop'
,
color
:
color
||
'#000'
,
formatter
:
function
()
{
return
`
${
name
}
:
${
value
}
`
;
},
},
};
};
export
const
alarmMarkLine
=
(
dataItem
,
index
,
dataSource
,
schemes
)
=>
{
// 只有一个数据曲线时显示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
,
};
// 只有一个数据曲线时显示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
,
};
};
export
const
minMaxMarkPoint
=
(
dataItem
,
index
,
dataSource
)
=>
{
// 只有一个数据曲线时显示markline
if
(
!
dataItem
||
dataSource
.
length
!==
1
)
return
{};
const
data
=
[];
data
.
push
({
type
:
'min'
,
name
:
'最小: '
});
data
.
push
({
type
:
'max'
,
name
:
'最大: '
});
return
{
symbolSize
:
1
,
symbolOffset
:
[
0
,
'50%'
],
label
:
{
formatter
:
'{b|{b} }{c|{c}}'
,
backgroundColor
:
window
.
globalConfig
&&
window
.
globalConfig
&&
window
.
globalConfig
.
variableTheme
?.
primaryColor
?
window
.
globalConfig
.
variableTheme
.
primaryColor
:
'#0087F7'
,
borderColor
:
'#ccc'
,
borderWidth
:
1
,
borderRadius
:
4
,
padding
:
[
2
,
10
],
lineHeight
:
22
,
position
:
'top'
,
distance
:
10
,
rich
:
{
b
:
{
color
:
'#fff'
,
},
c
:
{
color
:
'#fff'
,
fontSize
:
16
,
fontWeight
:
700
,
// 只有一个数据曲线时显示markline
if
(
!
dataItem
||
dataSource
.
length
!==
1
)
return
{};
const
data
=
[];
data
.
push
({
type
:
'min'
,
name
:
'最小: '
});
data
.
push
({
type
:
'max'
,
name
:
'最大: '
});
return
{
symbolSize
:
1
,
symbolOffset
:
[
0
,
'50%'
],
label
:
{
formatter
:
'{b|{b} }{c|{c}}'
,
backgroundColor
:
window
.
globalConfig
&&
window
.
globalConfig
&&
window
.
globalConfig
.
variableTheme
?.
primaryColor
?
window
.
globalConfig
.
variableTheme
.
primaryColor
:
'#0087F7'
,
borderColor
:
'#ccc'
,
borderWidth
:
1
,
borderRadius
:
4
,
padding
:
[
2
,
10
],
lineHeight
:
22
,
position
:
'top'
,
distance
:
10
,
rich
:
{
b
:
{
color
:
'#fff'
,
},
c
:
{
color
:
'#fff'
,
fontSize
:
16
,
fontWeight
:
700
,
},
},
},
},
},
data
,
};
data
,
};
};
/**
...
...
@@ -169,27 +169,27 @@ export const minMaxMarkPoint = (dataItem, index, dataSource) => {
* @param {any} axis
*/
export
const
decorateAxisGridLine
=
(
axis
,
showGrid
)
=>
{
if
(
!
axis
)
return
;
axis
.
minorTick
=
{
lineStyle
:
{
color
:
'#e2e2e2'
,
},
...(
axis
.
minorTick
||
{}),
show
:
showGrid
,
splitNumber
:
2
,
};
axis
.
minorSplitLine
=
{
lineStyle
:
{
color
:
'#e2e2e2'
,
type
:
'dashed'
,
},
...(
axis
.
minorSplitLine
||
{}),
show
:
showGrid
,
};
axis
.
splitLine
=
{
...(
axis
.
splitLine
||
{}),
show
:
showGrid
,
};
if
(
!
axis
)
return
;
axis
.
minorTick
=
{
lineStyle
:
{
color
:
'#e2e2e2'
,
},
...(
axis
.
minorTick
||
{}),
show
:
showGrid
,
splitNumber
:
2
,
};
axis
.
minorSplitLine
=
{
lineStyle
:
{
color
:
'#e2e2e2'
,
type
:
'dashed'
,
},
...(
axis
.
minorSplitLine
||
{}),
show
:
showGrid
,
};
axis
.
splitLine
=
{
...(
axis
.
splitLine
||
{}),
show
:
showGrid
,
};
};
/**
...
...
@@ -198,35 +198,35 @@ export const decorateAxisGridLine = (axis, showGrid) => {
* @param {any} dataItem
*/
export
const
offlineArea
=
(
dataItem
)
=>
{
if
(
!
dataItem
)
return
{};
const
{
dataModel
}
=
dataItem
;
let
datas
=
new
Array
();
let
offlineData
=
[];
let
hasOffline
=
false
;
dataModel
.
forEach
((
item
)
=>
{
if
(
!
item
.
pv
&&
!
hasOffline
)
{
offlineData
=
[
{
name
:
'离线'
,
xAxis
:
new
Date
(
item
.
pt
),
if
(
!
dataItem
)
return
{};
const
{
dataModel
}
=
dataItem
;
let
datas
=
new
Array
();
let
offlineData
=
[];
let
hasOffline
=
false
;
dataModel
.
forEach
((
item
)
=>
{
if
(
!
item
.
pv
&&
!
hasOffline
)
{
offlineData
=
[
{
name
:
'离线'
,
xAxis
:
new
Date
(
item
.
pt
),
},
];
hasOffline
=
true
;
}
else
if
(
item
.
pv
&&
hasOffline
)
{
offlineData
.
push
({
xAxis
:
new
Date
(
item
.
pt
),
});
datas
.
push
(
offlineData
);
offlineData
=
[];
hasOffline
=
false
;
}
});
return
{
itemStyle
:
{
color
:
'#eee'
,
},
];
hasOffline
=
true
;
}
else
if
(
item
.
pv
&&
hasOffline
)
{
offlineData
.
push
({
xAxis
:
new
Date
(
item
.
pt
),
});
datas
.
push
(
offlineData
);
offlineData
=
[];
hasOffline
=
false
;
}
});
return
{
itemStyle
:
{
color
:
'#eee'
,
},
data
:
datas
,
};
data
:
datas
,
};
};
/**
...
...
@@ -240,148 +240,206 @@ export const offlineArea = (dataItem) => {
* @param {any} config 额外配置信息
*/
const
optionGenerator
=
(
dataSource
,
cusOption
,
contrast
,
contrastOption
,
smooth
,
config
)
=>
{
const
needUnit
=
_
.
get
(
config
,
'needUnit'
,
false
);
const
curveCenter
=
_
.
get
(
config
,
'curveCenter'
,
false
);
const
nameWithSensor
=
_
.
get
(
config
,
'nameWithSensor'
,
true
);
const
showGridLine
=
_
.
get
(
config
,
'showGridLine'
,
false
);
const
showMarkLine
=
_
.
get
(
config
,
'showMarkLine'
,
false
);
const
showPoint
=
_
.
get
(
config
,
'showPoint'
,
false
);
const
deviceAlarmSchemes
=
_
.
get
(
config
,
'deviceAlarmSchemes'
,
[]);
const
needUnit
=
_
.
get
(
config
,
'needUnit'
,
false
);
const
curveCenter
=
_
.
get
(
config
,
'curveCenter'
,
false
);
const
nameWithSensor
=
_
.
get
(
config
,
'nameWithSensor'
,
true
);
const
showGridLine
=
_
.
get
(
config
,
'showGridLine'
,
false
);
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
restOption
=
_
.
pick
(
cusOption
,
[
'title'
,
'legend'
]);
// 自定义属性
const
restOption
=
_
.
pick
(
cusOption
,
[
'title'
,
'legend'
]);
// 一种指标一个y轴
const
yAxisMap
=
new
Map
();
dataSource
.
forEach
((
item
,
index
)
=>
{
const
{
sensorName
,
unit
}
=
item
;
const
key
=
sensorName
;
// 一种指标一个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
(
!
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
);
}
// 曲线居中
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'
};
// 网格显示
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
,
};
// 根据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
,
};
// 根据"指标名称"分类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
series
=
dataSource
.
map
((
item
,
index
)
=>
{
const
{
sensorName
,
unit
}
=
item
;
const
name
=
nameFormatter
(
item
,
contrast
,
contrastOption
,
nameWithSensor
);
const
data
=
dataAccessor
(
item
,
contrast
,
contrastOption
);
const
type
=
'line'
;
const
areaStyle
=
areaStyleFormatter
(
item
);
const
yAxisIndex
=
yAxisInterator
.
get
(
sensorName
);
const
markLine
=
showMarkLine
?
alarmMarkLine
(
item
,
index
,
dataSource
,
deviceAlarmSchemes
)
:
{};
const
markPoint
=
showPoint
?
minMaxMarkPoint
(
item
,
index
,
dataSource
)
:
{};
let
markArea
=
null
;
let
offlineAreas
=
offlineArea
(
item
);
if
(
offlineAreas
.
data
?.
length
)
{
restOption
.
markArea
=
null
;
markArea
=
offlineAreas
;
}
return
{
notMerge
:
true
,
name
,
type
,
data
,
areaStyle
,
yAxisIndex
,
smooth
,
unit
,
markLine
,
markPoint
,
markArea
,
};
});
// 根据"指标名称"分类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
};
})();
// 由于series更新后,没有的数据曲线仍然停留在图表区上,导致图表可视区范围有问题
const
min
=
Math
.
min
(
...
series
.
map
((
item
)
=>
item
.
data
?.[
0
]?.[
0
]).
filter
((
item
)
=>
item
!==
undefined
),
);
const
max
=
Math
.
max
(
...
series
.
map
((
item
)
=>
item
.
data
?.[
item
.
data
.
length
-
1
]?.[
0
])
.
filter
((
item
)
=>
item
!==
undefined
),
);
let
xAxis
=
{
type
:
'time'
,
min
,
max
};
decorateAxisGridLine
(
xAxis
,
showGridLine
);
const
series
=
dataSource
.
map
((
item
,
index
)
=>
{
const
{
sensorName
,
unit
}
=
item
;
const
name
=
nameFormatter
(
item
,
contrast
,
contrastOption
,
nameWithSensor
);
const
data
=
dataAccessor
(
item
,
contrast
,
contrastOption
);
const
type
=
'line'
;
const
areaStyle
=
areaStyleFormatter
(
item
);
const
yAxisIndex
=
yAxisInterator
.
get
(
sensorName
);
const
markLine
=
showMarkLine
?
alarmMarkLine
(
item
,
index
,
dataSource
,
deviceAlarmSchemes
)
:
{};
const
markPoint
=
showPoint
?
minMaxMarkPoint
(
item
,
index
,
dataSource
)
:
{};
let
markArea
=
null
;
let
offlineAreas
=
offlineArea
(
item
);
if
(
offlineAreas
.
data
?.
length
)
{
restOption
.
markArea
=
null
;
markArea
=
offlineAreas
;
const
tooltipTimeFormat
=
!
contrast
?
'YYYY-MM-DD HH:mm:ss'
:
contrastOption
===
'day'
?
'HH:mm'
:
'DD HH:mm'
;
const
tooltip
=
{
timeFormat
:
tooltipTimeFormat
,
// trigger: 'axis',
// axisPointer: {
// type: 'cross'
// }
};
// 增加箱线图的逻辑,单曲线才存在
if
(
!
justLine
&&
chartType
)
{
if
(
chartType
===
'boxChart'
)
{
const
otherData
=
dataSource
?.[
0
]?.
dataModel
.
map
(
item
=>
{
const
{
firstPV
,
lastPV
,
maxPV
,
minPV
}
=
item
;
return
[
firstPV
,
lastPV
,
minPV
,
maxPV
]
})
||
[];
//当存在othersData的时候,只是单曲线
series
=
series
.
map
(
item
=>
{
let
_item
=
{...
item
,
symbol
:
false
};
_item
.
data
=
_item
?.
data
?.
map
(
d
=>
{
return
d
[
1
]
||
null
})
||
[];
return
_item
;
})
series
.
push
({
type
:
'candlestick'
,
name
:
'箱线图'
,
symbol
:
false
,
data
:
otherData
});
}
else
{
let
_maxData
=
[];
let
_minData
=
[];
dataSource
?.[
0
]?.
dataModel
.
forEach
(
item
=>
{
const
{
firstPV
,
lastPV
,
maxPV
,
minPV
}
=
item
;
_maxData
.
push
(
maxPV
);
_minData
.
push
(
minPV
);
});
//当存在othersData的时候,只是单曲线
series
=
series
.
map
(
item
=>
{
let
_item
=
{...
item
,
symbol
:
false
};
_item
.
data
=
_item
?.
data
?.
map
(
d
=>
{
return
d
[
1
]
||
null
})
||
[];
return
_item
;
});
[[...
_minData
],
[...
_maxData
]].
forEach
((
item
,
index
)
=>
{
series
.
push
({
name
:
index
===
0
?
'最小值'
:
'最大值'
,
type
:
'line'
,
data
:
item
,
lineStyle
:
{
opacity
:
0
},
...(
index
!==
0
?
{
areaStyle
:
{
color
:
'#ccc'
}
}
:
{}),
stack
:
'confidence-band'
,
symbol
:
'none'
});
})
}
xAxis
=
{
type
:
'category'
,
data
:
series
[
0
].
data
.
map
(
item
=>
moment
(
item
[
0
]).
format
(
'YYYY-MM-DD'
))};
tooltip
.
formatter
=
(
e
)
=>
{
console
.
log
(
e
);
}
}
debugger
return
{
notMerge
:
true
,
name
,
type
,
data
,
areaStyle
,
yAxisIndex
,
smooth
,
unit
,
markLine
,
markPoint
,
markArea
,
yAxis
,
grid
,
xAxis
,
series
,
tooltip
,
...
restOption
,
};
});
// 由于series更新后,没有的数据曲线仍然停留在图表区上,导致图表可视区范围有问题
const
min
=
Math
.
min
(
...
series
.
map
((
item
)
=>
item
.
data
?.[
0
]?.[
0
]).
filter
((
item
)
=>
item
!==
undefined
),
);
const
max
=
Math
.
max
(
...
series
.
map
((
item
)
=>
item
.
data
?.[
item
.
data
.
length
-
1
]?.[
0
])
.
filter
((
item
)
=>
item
!==
undefined
),
);
const
xAxis
=
{
type
:
'time'
,
min
,
max
};
decorateAxisGridLine
(
xAxis
,
showGridLine
);
const
tooltipTimeFormat
=
!
contrast
?
'YYYY-MM-DD HH:mm:ss'
:
contrastOption
===
'day'
?
'HH:mm'
:
'DD HH:mm'
;
const
tooltip
=
{
timeFormat
:
tooltipTimeFormat
,
// trigger: 'axis',
// axisPointer: {
// type: 'cross'
// }
};
return
{
yAxis
,
grid
,
xAxis
,
series
,
tooltip
,
...
restOption
,
};
};
export
default
optionGenerator
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment