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
658f834f
Commit
658f834f
authored
Oct 09, 2023
by
陈龙
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 历史曲线增加开关类型的图表
parent
706724f6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
473 additions
and
128 deletions
+473
-128
EC_HistoryView.md
...es/extend-components/EC_HistoryView/src/EC_HistoryView.md
+1
-1
SingleChart.js
packages/extend-components/EC_HistoryView/src/SingleChart.js
+30
-9
index.js
packages/extend-components/EC_HistoryView/src/apis/index.js
+10
-1
index.js
packages/extend-components/EC_HistoryView/src/demos/index.js
+14
-2
index.js
packages/extend-components/EC_HistoryView/src/index.js
+61
-35
utils.js
packages/extend-components/EC_HistoryView/src/utils.js
+357
-80
No files found.
packages/extend-components/EC_HistoryView/src/EC_HistoryView.md
View file @
658f834f
...
...
@@ -30,7 +30,7 @@ path: /
## 多图表
<code
src=
"./demos/GridDemo.js"
></code>
[
//
]:
#
(<code src="./demos/GridDemo.js"></code>)
## API
...
...
packages/extend-components/EC_HistoryView/src/SingleChart.js
View file @
658f834f
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
,
specialTypeChartOptionGenerator
,
statusChartOptionGenerator
}
from
'./utils'
;
import
{
isArray
,
cloneDeep
}
from
'lodash'
;
const
SingleChart
=
memo
((
props
)
=>
{
const
{
...
...
@@ -19,7 +25,7 @@ const SingleChart = memo((props) => {
special
}
=
props
;
const
chartRef
=
useRef
();
const
SpecialType
=
[
'状态值'
,
'开关值'
];
// 横向柱状条
const
option
=
useMemo
(()
=>
{
const
config
=
{
needUnit
:
true
,
...
...
@@ -32,6 +38,21 @@ const SingleChart = memo((props) => {
showBoxOption
,
special
};
let
allValDesc
=
{};
let
_allSensorType
=
special
?.
allSensorType
?.
reduce
((
final
,
curr
)
=>
{
final
[
curr
.
id
]
=
curr
.
type
;
return
final
;
},
{});
let
_allPointAddress
=
special
?.
allPointAddress
?.
reduce
((
final
,
curr
)
=>
{
final
[
curr
.
name
]
=
_allSensorType
[
curr
.
sensorTypeID
];
allValDesc
[
curr
.
name
]
=
curr
.
valDesc
;
return
final
;
},
{});
if
(
dataSource
.
length
===
1
&&
SpecialType
.
includes
(
_allPointAddress
[
dataSource
[
0
].
sensorName
]))
{
config
.
sensorType
=
_allPointAddress
[
dataSource
[
0
].
sensorName
];
config
.
special
.
allValDesc
=
allValDesc
;
return
specialTypeChartOptionGenerator
({
dataSource
,
config
});
}
return
optionGenerator
(
dataSource
,
null
,
contrast
,
contrastOption
,
smooth
,
config
,
lineDataType
);
},
[
dataSource
,
smooth
,
curveCenter
,
chartType
]);
useEffect
(()
=>
{
...
...
@@ -39,7 +60,7 @@ const SingleChart = memo((props) => {
const
chart
=
chartRef
.
current
?.
getEchartsInstance
?.();
function
hander
(
params
)
{
const
{
selected
}
=
params
;
const
{
selected
}
=
params
;
const
count
=
Object
.
values
(
selected
||
{}).
filter
((
item
)
=>
item
).
length
;
const
option
=
cloneDeep
(
chart
.
getOption
());
const
needMarkLine
=
count
===
1
;
...
...
@@ -105,7 +126,7 @@ const SingleChart = memo((props) => {
};
let
yAxis
=
axisConfig
;
if
(
isArray
(
option
.
yAxis
))
{
yAxis
=
option
.
yAxis
.
map
((
item
)
=>
({
...
axisConfig
}));
yAxis
=
option
.
yAxis
.
map
((
item
)
=>
({
...
axisConfig
}));
}
let
xAxis
=
axisConfig
;
chart
.
setOption
({
...
...
@@ -124,9 +145,9 @@ const SingleChart = memo((props) => {
[
dataSource
],
);
return
isEmpty
?
(
<
PandaEmpty
/>
<
PandaEmpty
/>
)
:
(
<
BasicChart
ref
=
{
chartRef
}
option
=
{
option
}
notMerge
style
=
{{
width
:
'100%'
,
height
:
'100%'
}}
/
>
<
BasicChart
ref
=
{
chartRef
}
option
=
{
option
}
notMerge
style
=
{{
width
:
'100%'
,
height
:
'100%'
}}
/
>
);
});
...
...
packages/extend-components/EC_HistoryView/src/apis/index.js
View file @
658f834f
...
...
@@ -8,13 +8,14 @@ const baseUrl = typeof DUMI_TYPE !== 'undefined' && DUMI_TYPE === 'dumi' ? '/api
const
monitorDeviceUrl
=
`
${
baseUrl
}
/PandaMonitor/Monitor/Device`
;
// 获取单个设备的配置信息
export
function
getPointAddress
(
params
)
{
export
function
getPointAddress
(
params
)
{
return
request
({
url
:
`/PandaMonitor/Monitor/PointAddress/GetPointAddress`
,
method
:
REQUEST_METHOD_GET
,
params
});
}
// 获取点表信息
export
function
getPointAddressEntry
(
params
)
{
return
request
({
...
...
@@ -57,4 +58,11 @@ export function getDictionaryInfoAll(params) {
method
:
REQUEST_METHOD_GET
,
params
})
}
export
function
getSensorType
()
{
return
request
({
url
:
'/PandaMonitor/Monitor/Sensor/GetSensorType'
,
method
:
REQUEST_METHOD_GET
,
})
}
\ No newline at end of file
packages/extend-components/EC_HistoryView/src/demos/index.js
View file @
658f834f
...
...
@@ -4,6 +4,18 @@ import { MobileHistoryChart } from "../mobile";
const
deviceParams
=
[
/*10.182*/
/* {
"deviceCode": "EGBF00000136",
"sensors": "进水压力,出水瞬时流量,今日供水量,1#水箱液位,视频报警",
// "sensors": "视频报警",
"deviceType": "二供泵房"
}, */
{
"deviceCode"
:
"EGJZ00000158"
,
"sensors"
:
"进水压力,出水压力,泵1状态"
,
// "sensors": "泵1状态",
"deviceType"
:
"二供机组"
}
/* {
deviceCode: 'EGBF00000141',
// sensors: '进水压力,出水瞬时流量,出水累计流量',
...
...
@@ -16,13 +28,13 @@ const deviceParams = [
"sensors": "瞬时流量",
"deviceType": "水源井"
},*/
{
/*
{
deviceCode: 'EGJZ00000197',
sensors: '进水压力,出水压力,出水瞬时流量,出水累计流量',
// sensors: '1#变频器运行频率',
deviceType: '二供机组',
// pointAddressID: 208,
},
},
*/
/* {
deviceCode: 'EGJZ00000198',
sensors: '进水压力,出水压力,出水瞬时流量,出水累计流量',
...
...
packages/extend-components/EC_HistoryView/src/index.js
View file @
658f834f
...
...
@@ -35,6 +35,7 @@ import SingleChart from './SingleChart';
import
GridChart
from
'./GridChart'
;
import
'./index.less'
;
import
{
globalConfig
}
from
'antd/lib/config-provider'
;
import
{
getSensorType
}
from
"@wisdom-components/ec_realtimeinfo/es/apis"
;
const
{
RangePicker
}
=
DatePicker
;
const
{
Option
}
=
Select
;
...
...
@@ -311,6 +312,10 @@ const HistoryView = (props) => {
const
[
percent
,
setPercent
]
=
useState
(
0
);
// 频率指标特殊业务
const
[
special1
,
setSpecial1
]
=
useState
(
null
);
const
[
allPointAddress
,
setAllPointAddress
]
=
useState
([]);
//查询所有sensorType
const
[
allSensorType
,
setAllSensorType
]
=
useState
([]);
const
[
isSingleStatusSensor
,
setIsSingleStatusSensor
]
=
useState
(
false
);
// 历史数据相关的特征描述
const
deviceConfig
=
useRef
({
oneDevice
:
deviceParams
.
length
===
1
,
//单设备
...
...
@@ -689,14 +694,14 @@ const HistoryView = (props) => {
);
};
const
renderCurveOption
=
(
isChart
,
isSingle
)
=>
{
const
renderCurveOption
=
(
isChart
,
isSingle
,
isStatus
)
=>
{
return
(
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-cover`
)}
style
=
{
isChart
&&
isSingle
?
{
width
:
'100%'
}
:
{}}
>
{
isChart
?
<>
isChart
&&
!
isStatus
?
<>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
曲线选择
<
/div
>
<
div
className
=
{
`
${
prefixCls
}
-cover-item`
}
>
<
Radio
.
Group
...
...
@@ -713,7 +718,7 @@ const HistoryView = (props) => {
<
/div
>
<
/> : '
'
}
{
isChart
&&
isSingle
&&
showBoxOption
?
(
{
isChart
&&
isSingle
&&
showBoxOption
&&
!
isStatus
?
(
<>
{
lineDataType
!==
'原始曲线'
?
<>
...
...
@@ -736,32 +741,37 @@ const HistoryView = (props) => {
)
:
(
''
)}
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
{
activeTabKey
!==
'table'
?
'曲线设置'
:
'表格设置'
}
<
/div
>
{
checkboxData
.
map
((
child
)
=>
{
const
box
=
renderCheckbox
(
child
,
isChart
&&
isSingle
);
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
>
)}
{
!
isStatus
?
<>
<
div
className
=
{
classNames
(
`
${
prefixCls
}
-label`
)}
>
{
activeTabKey
!==
'table'
?
'曲线设置'
:
'表格设置'
}
<
/div
>
{
checkboxData
.
map
((
child
)
=>
{
const
box
=
renderCheckbox
(
child
,
isChart
&&
isSingle
);
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
>
);
};
...
...
@@ -825,7 +835,7 @@ const HistoryView = (props) => {
key: dataIndex,
ellipsis: true,
align: 'center',
width:200
width:
200
};
// 同期对比
if (timeValue === 'contrast' && dataModel[0]) {
...
...
@@ -1082,14 +1092,14 @@ const HistoryView = (props) => {
{...
tableProps
}
pagination
=
{
false
}
onChange
=
{
handleChange
}
scroll
=
{{
x
:
'max-content'
,
y
:
'calc(100% - 40px)'
}}
scroll
=
{{
x
:
'max-content'
,
y
:
'calc(100% - 40px)'
}}
/
>
)
:
(
<
PandaEmpty
/>
)}
<
/div
>
<
/
>
},
[
timeOrder
,
chartDataSource
,
columns
,
tableProps
,
tableData
])
},
[
timeOrder
,
chartDataSource
,
columns
,
tableProps
,
tableData
,
isSingleStatusSensor
])
const
returnLongestPeriod
=
(
data
)
=>
{
let
_earliest
=
''
let
_latest
=
''
;
...
...
@@ -1119,6 +1129,7 @@ const HistoryView = (props) => {
{
renderCurveOption
(
true
,
deviceParams
?.
length
===
1
&&
deviceParams
?.[
0
]?.
sensors
?.
split
(
','
).
length
===
1
,
isSingleStatusSensor
)}
<
/div
>
{
...
...
@@ -1151,6 +1162,8 @@ const HistoryView = (props) => {
deviceAlarmSchemes
=
{
deviceAlarmSchemes
}
special
=
{{
special1
,
// 频率业务
allPointAddress
,
allSensorType
,
// 后续新增的开关量的特殊业务,用来处理开关量业务
}}
/
>
)}
...
...
@@ -1172,18 +1185,21 @@ const HistoryView = (props) => {
code
:
deviceCode
}))?.
data
?.[
0
]?.
id
;
let
_params
=
{
deviceType
:
deviceType
//
deviceType: deviceType
};
if
(
_id
)
_params
.
versionId
=
_id
;
// 多曲线的居中,容易导致曲线被截断,故多曲线时,不请求
let
_request0
=
getDictionaryInfoAll
({
level
:
'组件_ec_historyview'
});
// 以下请求为处理状态值、开关值的图表,只允许单曲线单指标情况下展示
let
_request1
=
getPointAddressEntry
(
_params
);
await
Promise
.
all
([
_request0
,
_request1
]).
then
(
result
=>
{
let
_request2
=
getSensorType
();
await
Promise
.
all
([
_request0
,
_request1
,
_request2
]).
then
(
result
=>
{
if
(
result
)
{
let
_res0
=
result
[
0
];
let
_res1
=
result
[
1
];
let
_res2
=
result
[
2
];
// 查字典配置
if
(
_res0
.
code
===
0
)
{
let
_opt
=
_res0
.
data
.
reduce
((
final
,
cur
)
=>
{
...
...
@@ -1203,6 +1219,7 @@ const HistoryView = (props) => {
if
(
_res1
.
code
===
0
)
{
let
_sensorConfig
=
_res1
.
data
.
find
(
item
=>
item
?.
name
.
trim
()
===
sensors
.
trim
());
let
_statusName
=
_sensorConfig
?.
statusName
;
setAllPointAddress
(
_res1
.
data
);
if
(
_statusName
)
{
let
_statusConfig
=
_res1
.
data
.
find
(
item
=>
item
?.
name
.
trim
()
===
_statusName
.
trim
());
let
_valDesc
=
_statusConfig
?.
valDesc
||
''
;
...
...
@@ -1216,6 +1233,15 @@ const HistoryView = (props) => {
});
}
}
// 标记sensor是什么类型的
if
(
_res2
.
code
===
0
)
{
setAllSensorType
(
_res2
.
data
);
let
_sensorID
=
_res1
.
data
?.
find
(
item
=>
item
.
name
===
sensors
)?.
sensorTypeID
;
let
_sensor
=
_res2
.
data
?.
find
(
item
=>
item
.
id
===
_sensorID
)?.
type
;
let
_isStatusSensor
=
[
'状态值'
,
'开关值'
].
includes
(
_sensor
);
setIsSingleStatusSensor
(
_isStatusSensor
);
}
}
});
setCompleteInit
(
true
);
...
...
packages/extend-components/EC_HistoryView/src/utils.js
View file @
658f834f
import
moment
from
'moment'
;
import
_
,
{
isArray
}
from
'lodash'
;
import
_
,
{
isArray
}
from
'lodash'
;
import
maxIcon
from
'./assets/最大实心.svg'
;
import
minIcon
from
'./assets/最小实心.svg'
;
import
minIconDownArrow
from
'./assets/最小实心箭头朝下.svg'
;
import
lineChart
from
'@wisdom-components/basicchart/es/LineChart'
;
import
*
as
echarts
from
"echarts"
;
/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
const
AXIS_WIDTH
=
40
;
...
...
@@ -67,7 +68,7 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
* @returns
*/
const
nameFormatter
=
(
data
,
contrast
,
contrastOption
,
nameWithSensor
)
=>
{
const
{
equipmentName
,
sensorName
,
unit
,
dataModel
,
dateFrom
,
dateTo
}
=
data
;
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
,
''
);
...
...
@@ -102,7 +103,7 @@ const dataAccessor = (data, contrast, contrastOption) => {
* @returns Null/areaStyle, 为null显示曲线图, 为areaStyle对象显示为面积图.
*/
const
areaStyleFormatter
=
(
data
)
=>
{
const
{
sensorName
}
=
data
;
const
{
sensorName
}
=
data
;
return
sensorName
&&
sensorName
.
indexOf
(
'流量'
)
>
-
1
?
{}
:
null
;
};
...
...
@@ -113,7 +114,7 @@ const areaStyleFormatter = (data) => {
* @returns
*/
const
minMax
=
(
data
)
=>
{
const
{
dataModel
}
=
data
;
const
{
dataModel
}
=
data
;
let
min
=
Number
.
MAX_SAFE_INTEGER
;
let
max
=
Number
.
MIN_SAFE_INTEGER
;
dataModel
.
forEach
((
item
)
=>
{
...
...
@@ -144,7 +145,7 @@ const markLineItem = (name, value, color) => {
export
const
alarmMarkLine
=
(
dataItem
,
index
,
dataSource
,
schemes
)
=>
{
// 只有一个数据曲线时显示markline
if
(
!
dataItem
||
!
schemes
||
dataSource
.
length
!==
1
)
return
{};
const
{
deviceType
,
stationCode
,
sensorName
,
decimalPoint
}
=
dataItem
;
const
{
deviceType
,
stationCode
,
sensorName
,
decimalPoint
}
=
dataItem
;
const
curSchemes
=
schemes
.
filter
(
(
item
)
=>
item
.
deviceCode
===
stationCode
&&
...
...
@@ -153,7 +154,7 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
);
const
data
=
[];
curSchemes
.
forEach
((
scheme
)
=>
{
const
{
hLimit
,
hhLimit
,
lLimit
,
llLimit
}
=
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'
));
...
...
@@ -245,7 +246,7 @@ export const decorateAxisGridLine = (axis, showGrid) => {
*/
export
const
offlineArea
=
(
dataItem
)
=>
{
if
(
!
dataItem
)
return
{};
const
{
dataModel
}
=
dataItem
;
const
{
dataModel
}
=
dataItem
;
let
datas
=
new
Array
();
let
offlineData
=
[];
let
hasOffline
=
false
;
...
...
@@ -255,7 +256,7 @@ export const offlineArea = (dataItem) => {
{
name
:
'离线'
,
xAxis
:
new
Date
(
item
.
pt
),
label
:
{
show
:
!
datas
?.
length
},
label
:
{
show
:
!
datas
?.
length
},
},
];
hasOffline
=
true
;
...
...
@@ -279,7 +280,7 @@ export const offlineArea = (dataItem) => {
// tooltip 模板
const
headTemplate
=
(
param
,
opt
)
=>
{
if
(
!
param
)
return
''
;
const
{
name
,
axisValueLabel
,
axisType
,
axisValue
}
=
param
;
const
{
name
,
axisValueLabel
,
axisType
,
axisValue
}
=
param
;
const
timeFormat
=
opt
&&
opt
.
contrast
?
opt
.
contrastOption
===
'day'
...
...
@@ -295,21 +296,21 @@ const headTemplate = (param, opt) => {
};
const
seriesTemplate
=
(
param
,
unit
)
=>
{
if
(
!
param
||
param
.
seriesName
===
'自定义'
)
return
''
;
const
{
value
,
encode
}
=
param
;
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;">
<span style="
${
isMobile
()
?
'width: '
+
handlePx
(
90
,
'px'
)
+
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
:
''
}
">
${
param
.
seriesName
}
</span>
?
'width: '
+
handlePx
(
90
,
'px'
)
+
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
:
''
}
">
${
param
.
seriesName
}
</span>
<span style="display:inline-block;">:</span>
<span style="color:
${
color
}
;margin: 0
${
handlePx
(
5
,
'px'
)}
0 auto;">
${
value
?.
toFixed
(
3
)
??
'-'
}
<
/span
>
}
<
/span
>
<
span
style
=
"font-size: ${handlePx(12, 'px')};"
>
$
{
_unit
??
''
}
<
/span
>
<
/div>`
;
return
param
.
componentSubType
!==
'candlestick'
...
...
@@ -444,7 +445,7 @@ const handleSpecial1 = (special, dataSource) => {
},
},
name: item.text,
label: {
show: true
},
label: {
show: true
},
},
{
xAxis: item.lte,
...
...
@@ -479,28 +480,28 @@ const handleSpecial1 = (special, dataSource) => {
};
// 生成x坐标
const returnXAxis = ({
dataSource,
contrast,
contrastOption,
nameWithSensor,
showMarkLine,
deviceAlarmSchemes,
showPoint,
restOption,
smooth,
special,
yAxis
}) => {
dataSource,
contrast,
contrastOption,
nameWithSensor,
showMarkLine,
deviceAlarmSchemes,
showPoint,
restOption,
smooth,
special,
yAxis
}) => {
// 根据"指标名称"分类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
};
return {
get
};
})();
let _offlineData = [];
// 生成visualMap、markArea
let {
visualMap, markArea
} = handleSpecial1(special, dataSource);
let {
visualMap, markArea
} = handleSpecial1(special, dataSource);
let _filterArr = ['是否在线'];
if (special?.special1?.name) {
_filterArr.push(special.special1.name);
...
...
@@ -514,14 +515,13 @@ const returnXAxis = ({
return !_filterArr.includes(item.sensorName);
})
.map((item, index) => {
const {
sensorName, unit
} = item;
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 _index = yAxis.findIndex(item => item.name === unit);
const yAxisIndex = _index > -1 ? _index : 0;
// console.log('yAxisIndex: ',yAxisInterator);
const markLine = showMarkLine
? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes)
: {};
...
...
@@ -628,11 +628,11 @@ const reduceYAxis = (arr, dataSource) => {
}
return final;
}, {});
console.log('datasource: ', dataSource);
// 2. 合并相同单位的数据,找出最大值
let _maxValueArr = Object.values(dataSource.reduce((final, cur) => {
let _key = cur.
name === null ? 'null' : cur.n
ame;
let _key = cur.
sensorName === null ? 'null' : cur.sensorN
ame;
let _maxValue = cur.dataModel.reduce((final, cur) => {
// eslint-disable-next-line no-param-reassign
if (cur.pv > final) final = cur.pv;
return final
}, 0);
...
...
@@ -646,9 +646,13 @@ const reduceYAxis = (arr, dataSource) => {
// 3. 合并,生成Y轴配置
return Object.values(_arr).map((item, index) => {
let _key = item.name === null ? 'null' : item.name;
let _
offset
= _maxValueArr[index - 2];
let _
lastAxisNumber
= _maxValueArr[index - 2];
let _baseOffset = _offsetValue[index - 2] ?? 0;
let _finalOffset = (_offset !== undefined ? (_offset === 0 ? 2 : _offset.toString().replaceAll('.', '').length) * 12 : 0) + _baseOffset;
let _finalOffset = (
_lastAxisNumber !== undefined ? // 没有相邻的轴
(_lastAxisNumber === 0 ? 20 : _lastAxisNumber.toFixed(2).replaceAll('.', '').length) * 12
: 0
) + _baseOffset;
_offsetValue.push(_finalOffset);
return ({
...item,
...
...
@@ -660,15 +664,23 @@ const reduceYAxis = (arr, dataSource) => {
})
});
};
const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
// 1. 新需求:合并相同的坐标轴
// 一种指标一个y轴 -------- 需求变更:相同类型的指标放到同一指标轴(单位相同) 2023年9月28日
/**
*
* 1. 生成常规的yAxis配置;
* 2. 处理sensorType为状态值的指标,生成yAxis配置
*
* @param {array} dataSource 数据源
* @param {boolean} needUnit 是否显示单位。
* @param {boolean} curveCenter 曲线是否居中。
* @param {boolean} showGridLine 是否显示网格线。
* @return {object} 返回左右轴的margin、yAxis的配置。
* */
const handleYAxis = ({ dataSource, needUnit, curveCenter, showGridLine }) => {
const yAxisMap = new Map();
// 1. 找出最大值; 2. 计算出y轴最大宽度动态计算偏移距离;
dataSource.forEach((item, index) => {
const {sensorName, unit} = item;
const { sensorName, unit } = item;
const key = sensorName;
if (!yAxisMap.has(key)) {
/* const i = yAxisMap.size;
...
...
@@ -704,7 +716,6 @@ const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
};
yAxisMap.set(key, axis);
}
// 曲线居中
if (curveCenter && item.dataModel && item.dataModel.length > 0) {
const [min, max] = minMax(item);
...
...
@@ -712,16 +723,28 @@ const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
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 ? reduceYAxis([...yAxisMap.values()], dataSource) : {
type: 'value'
};
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
};
return {
leftNum, rightNum, yAxis
};
};
/**
* 1. 最后的配置处理、合并
* dataZoom 缩放
* xAxis.minInterval X轴的最小间隔
* legend legend配置
* @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 = (restOption, xAxis, legendData, chartType, contrast, contrastOption, config) => {
restOption.dataZoom = [
{
...
...
@@ -754,7 +777,7 @@ const assignOptions = (restOption, xAxis, legendData, chartType, contrast, contr
];
xAxis.minInterval = 3600 * (1 * 1000);
if (legendData) {
restOption.legend = {
...restOption.legend, ...{data: legendData}
};
restOption.legend = {
...restOption.legend, ...{ data: legendData }
};
}
};
...
...
@@ -802,7 +825,7 @@ const handleGrid = (dataSource, needUnit, leftNum, rightNum, chartType) => {
return {
top: _base + _topForUnit,
// left: leftNum === 1 ? 10 : leftNum * AXIS_WIDTH,
left:
2
0,
left:
3
0,
right: 10,
// right: rightNum === 0 ? 20 : rightNum * AXIS_WIDTH,
bottom: 60,
...
...
@@ -879,6 +902,36 @@ const returnCustomSeries = (dataSource) => {
data: [_minNumber, _maxNumber],
};
};
const renderStatusItem = (params, api) => {
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()
}
);
}
/**
* 图表配置项生成
*
...
...
@@ -899,6 +952,7 @@ const optionGenerator = (
config,
lineDataType = '',
) => {
// 1. 处理配置,配置分配默认值;
const {
needUnit,
curveCenter,
...
...
@@ -912,17 +966,13 @@ const optionGenerator = (
restOption,
special,
} = handleDefault(config, cusOption);
const {
leftNum, rightNum, yAxis
} = handleYAxis({
const {
leftNum, rightNum, yAxis
} = handleYAxis({
dataSource,
needUnit,
curveCenter,
showGridLine,
});
console.log(yAxis);
const grid = handleGrid(dataSource, needUnit, leftNum, rightNum, chartType);
let {xAxis, series, visualMap} = returnXAxis({
let { xAxis, series, visualMap } = returnXAxis({
dataSource,
contrast,
contrastOption,
...
...
@@ -935,6 +985,8 @@ const optionGenerator = (
special,
yAxis
});
// 3. 判断是否开启网格;
const grid = handleGrid(dataSource, needUnit, leftNum, rightNum, chartType);
decorateAxisGridLine(xAxis, showGridLine);
const tooltipTimeFormat = !contrast
? 'YYYY-MM-DD HH:mm:ss'
...
...
@@ -947,16 +999,16 @@ const optionGenerator = (
if (chartType === 'boxChart' && lineDataType === '特征曲线') {
const otherData =
dataSource?.[0]?.dataModel.map((item) => {
const {
firstPV, lastPV, maxPV, minPV, pt
} = item;
const {
firstPV, lastPV, maxPV, minPV, pt
} = item;
return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV];
}) || []; //当存在othersData的时候,只是单曲线
xAxis = {
type: 'time'
};
xAxis = {
type: 'time'
};
decorateAxisGridLine(xAxis, showGridLine);
let unit = [];
series = series.map((item) => {
if (item.unit) unit.push(item.unit);
item.areaStyle = null;
return {
...item, showSymbol: false
};
return {
...item, showSymbol: false
};
});
series.push({
type: 'candlestick',
...
...
@@ -987,7 +1039,7 @@ const optionGenerator = (
* 2. 当最大值小于零时(此时,最小值一定小于零);
*/
dataSource?.[0]?.dataModel.forEach((item) => {
const {
firstPV, lastPV, maxPV, minPV, pt
} = item;
const {
firstPV, lastPV, maxPV, minPV, pt
} = item;
_maxValues.push(maxPV);
let time = contrast ? moment(pt).format(formatStr) : pt;
_maxData.push([
...
...
@@ -997,13 +1049,13 @@ const optionGenerator = (
_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'
};
xAxis = {
type: 'time'
};
decorateAxisGridLine(xAxis, showGridLine);
let _unit = '';
series = series.map((item) => {
_unit = item.unit ?? '';
item.areaStyle = null;
return {
...item, showSymbol: false
};
return {
...item, showSymbol: false
};
});
[[..._minData], [..._maxData]].forEach((item, index) => {
series.push({
...
...
@@ -1033,34 +1085,34 @@ const optionGenerator = (
<
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
>
? '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
>
5,
'px',
)} 0 auto;"
>
$
{
e
[
0
]?.
value
?.[
1
]
??
'-'
}
<
/span
>
<
span
style
=
"font-size: ${handlePx(12, 'px')};"
>
$
{
_unit
??
''
}
<
/span
>
<
/div
>
<
div
style
=
"display: ${lineDataType === '特征曲线' ? 'flex' : 'none'
}; align-items: center;"
>
}; align-items: center;"
>
<
span
>
周期最小值
<
/span><span style="display:inline-block;">:</
span
>
<
span
style
=
"color: ${COLOR.AVG};margin: 0 ${handlePx(
5,
'px',
)} 0 auto;"
>
$
{
e
?.[
1
]?.
value
?.[
1
]
??
'-'
}
<
/span
>
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;"
>
}; 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
?.[
2
]?.
dataIndex
]
??
'-'
}
<
/span
>
5,
'px',
)} 0 auto;"
>
$
{
_maxValues
[
e
?.[
2
]?.
dataIndex
]
??
'-'
}
<
/span
>
<
span
style
=
"font-size: ${handlePx(12, 'px')};"
>
$
{
_unit
??
''
}
<
/span
>
<
/div
>
<
/div
>
...
...
@@ -1076,7 +1128,7 @@ const optionGenerator = (
}
else
{
tooltip
=
tooltipAccessor
(
series
.
map
((
item
)
=>
item
.
unit
),
{
contrastOption
,
contrast
},
{
contrastOption
,
contrast
},
);
}
tooltip
.
timeFormat
=
tooltipTimeFormat
;
...
...
@@ -1093,8 +1145,232 @@ const optionGenerator = (
visualMap
,
...
restOption
,
};
console
.
log
(
'_options: '
,
_options
)
return
_options
;
};
export
default
optionGenerator
;
const
handleDataSource
=
(
dataSource
)
=>
{
let
_temp
=
null
;
let
_data
=
[];
let
_dataLength
=
dataSource
[
0
].
dataModel
.
length
;
// handleSpecial2()
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
};
const
returnLegend
=
(
sensorType
)
=>
{
const
_colorMap
=
{
变频
:
'#1685ff'
,
工频
:
'#00d0c7'
,
运行
:
'#1685ff'
,
故障
:
'#ff6b37'
,
停止
:
'#666666'
,
};
};
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'
,
停止
:
'#666666'
,
};
let
_valDescMap
=
special
.
allValDesc
[
sensorName
]?.
split
(
';'
).
reduce
((
final
,
cur
)
=>
{
let
_arr
=
cur
.
split
(
':'
);
final
[
_arr
[
0
]]
=
_arr
[
1
];
return
final
},
{});
name
=
_valDescMap
[
data1
.
pv
];
color
=
_colorMap
[
name
];
}
//2. 开关量
if
(
sensorType
===
'开关值'
)
{
const
_switchColorMap
=
{
0
:
'#666666'
,
1
:
'#1685ff'
};
const
_switchNameMap
=
{
0
:
'关'
,
1
:
'开'
}
name
=
_switchNameMap
[
data1
.
pv
];
color
=
_switchColorMap
[
data1
.
pv
];
};
value1
=
moment
(
data1
.
pt
).
valueOf
();
value2
=
moment
(
data2
.
pt
).
valueOf
();
return
{
color
,
value1
,
value2
,
name
}
};
const
handleDataToSeries
=
(
special
,
sensorName
,
sensorType
,
data
)
=>
{
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
}
`
]
});
});
return
{
data
:
_data
,
legend
:
_legend
};
};
const
specialTypeChartOptionGenerator
=
({
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
:
{
color
:
"#e2e2e2"
},
show
:
true
,
splitNumber
:
2
},
minorSplitLine
:
{
lineStyle
:
{
color
:
"#e2e2e2"
,
type
:
"dashed"
},
show
:
true
},
splitLine
:
{
show
:
true
},
"minInterval"
:
3600000
};
let
yAxis
=
{
data
:
[
dataSource
[
0
].
sensorName
],
axisLine
:
{
show
:
true
},
minorTick
:
{
lineStyle
:
{
color
:
"#e2e2e2"
},
show
:
true
,
splitNumber
:
2
},
minorSplitLine
:
{
lineStyle
:
{
color
:
"#e2e2e2"
,
type
:
"dashed"
},
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'
,
停止
:
'#666666'
,
关
:
'#666666'
,
开
:
'#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
,
left
:
'center'
,
top
:
30
}
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
;
};
export
{
specialTypeChartOptionGenerator
}
\ No newline at end of file
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