feat: 对接Data Recevice status Monitoring右侧图表,自定义并实现图例功能
This commit is contained in:
parent
10bae9ed27
commit
86c8e1b69e
|
@ -22,7 +22,7 @@
|
||||||
"cron-parser": "^2.10.0",
|
"cron-parser": "^2.10.0",
|
||||||
"dayjs": "^1.8.0",
|
"dayjs": "^1.8.0",
|
||||||
"dom-align": "1.12.0",
|
"dom-align": "1.12.0",
|
||||||
"echarts": "^4.9.0",
|
"echarts": "^5.4.2",
|
||||||
"enquire.js": "^2.1.6",
|
"enquire.js": "^2.1.6",
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="custom-chart" ref="containerRef"></div>
|
<div class="custom-chart" ref="containerRef" :style="{ height: height + 'px' }"></div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
option : {
|
option: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -16,6 +20,19 @@ export default {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.chart = echarts.init(this.$refs.containerRef)
|
this.chart = echarts.init(this.$refs.containerRef)
|
||||||
this.chart.setOption(this.option)
|
this.chart.setOption(this.option)
|
||||||
|
},
|
||||||
|
watch : {
|
||||||
|
option: {
|
||||||
|
handler() {
|
||||||
|
this.chart && this.chart.setOption(this.option)
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
height() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.chart && this.chart.resize()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -223,19 +223,8 @@
|
||||||
|
|
||||||
<!-- 右侧图表展示栏 -->
|
<!-- 右侧图表展示栏 -->
|
||||||
<div class="data-receive-status-chart" :class="{ 'on-screen': !leftPaneShow }">
|
<div class="data-receive-status-chart" :class="{ 'on-screen': !leftPaneShow }">
|
||||||
<!-- 图例 -->
|
|
||||||
<div class="legend">
|
|
||||||
<div class="legend-item" v-for="(legend, index) in legendList" :key="index">
|
|
||||||
<span class="legend-item-color" :style="{ backgroundColor: legend.color }"></span>
|
|
||||||
<span>{{ legend.title }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 图例结束 -->
|
|
||||||
<template v-if="showChart">
|
<template v-if="showChart">
|
||||||
<template v-for="(item, index) in statusList">
|
<RealTimeDataChart :list="statusList" :scale-settings="initialDataRecieveSettings" />
|
||||||
<RealTimeDataChart :legendList="legendList" :title="item.title" :key="index" />
|
|
||||||
<a-divider :key="index + '_divider'" v-if="index !== statusList.length - 1"></a-divider>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<!-- 右侧图表展示栏结束 -->
|
<!-- 右侧图表展示栏结束 -->
|
||||||
|
@ -350,33 +339,6 @@ const columns = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const legendList = [
|
|
||||||
{
|
|
||||||
title: 'SPHDPREL',
|
|
||||||
color: '#17a840'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'SPHDF',
|
|
||||||
color: '#0cbfb0'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'QC',
|
|
||||||
color: '#1c82eb'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'GASBKPHDPREL',
|
|
||||||
color: '#d3ad16'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'GASBKPHDF',
|
|
||||||
color: '#db6423'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'SOH/MET',
|
|
||||||
color: '#8852da'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Attribute Configuration 检验规则(自定义,非a-form校验)
|
// Attribute Configuration 检验规则(自定义,非a-form校验)
|
||||||
const rules = {
|
const rules = {
|
||||||
cacheTime: {
|
cacheTime: {
|
||||||
|
@ -441,7 +403,6 @@ export default {
|
||||||
|
|
||||||
leftPaneShow: true, // 左侧抽屉
|
leftPaneShow: true, // 左侧抽屉
|
||||||
|
|
||||||
legendList, // 图例列表
|
|
||||||
statusList: [], // 数据接收状态列表
|
statusList: [], // 数据接收状态列表
|
||||||
isGettingStatusList: false,
|
isGettingStatusList: false,
|
||||||
showChart: true,
|
showChart: true,
|
||||||
|
@ -785,12 +746,30 @@ export default {
|
||||||
// 获取数据接收状态列表
|
// 获取数据接收状态列表
|
||||||
async getDataRecieveStatusList() {
|
async getDataRecieveStatusList() {
|
||||||
try {
|
try {
|
||||||
console.log('%c [ ]-777', 'font-size:13px; background:pink; color:#bf2c9f;', this.initialDataRecieveSettings)
|
|
||||||
this.isGettingStatusList = true
|
this.isGettingStatusList = true
|
||||||
const res = await getAction('/jeecg-station-operation/stationOperation/getDataReceivingStatus', {
|
const { success, result, message } = await getAction(
|
||||||
|
'/jeecg-station-operation/stationOperation/getDataReceivingStatus',
|
||||||
|
{
|
||||||
userId: this.$store.getters.userInfo.id
|
userId: this.$store.getters.userInfo.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (success) {
|
||||||
|
const statusList = []
|
||||||
|
result.forEach(item => {
|
||||||
|
const detectorArr = item.detectors[item.stationId]
|
||||||
|
detectorArr.forEach(detector => {
|
||||||
|
Object.values(detector).forEach(value => {
|
||||||
|
statusList.push({
|
||||||
|
title: `${item.stationCode} ${value.detectorId}`,
|
||||||
|
dataList: value.dataList
|
||||||
})
|
})
|
||||||
console.log('%c [ res ]-640', 'font-size:13px; background:pink; color:#bf2c9f;', res)
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.statusList = statusList
|
||||||
|
} else {
|
||||||
|
this.$message.error(message)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -1203,29 +1182,6 @@ export default {
|
||||||
background-color: rgba(65, 111, 127, 0.5);
|
background-color: rgba(65, 111, 127, 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend {
|
|
||||||
margin: 32px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
color: #ade6ee;
|
|
||||||
line-height: 12px;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-color {
|
|
||||||
display: inline-block;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullscreen {
|
.fullscreen {
|
||||||
|
|
|
@ -1,113 +1,326 @@
|
||||||
<template>
|
<template>
|
||||||
<v-chart :force-fit="true" :height="height" :data="dataSource" :padding="[0, 0, 40, 60]">
|
<div>
|
||||||
<v-axis
|
<!-- 图例 -->
|
||||||
data-key="name"
|
<div class="legend">
|
||||||
:title="{ text: title, textStyle: { fill: '#5b9cba', fontSize: 14 }, offset: 45 }"
|
<div
|
||||||
:label="{ textStyle: { fill: '#ade6ee' } }"
|
class="legend-item"
|
||||||
:grid="{ lineStyle: { stroke: '#406979', strokeOpacity: 0.2, lineDash: [0, 0] } }"
|
:class="{ active: legend.isShow }"
|
||||||
:line="{ stroke: '#406979', strokeOpacity: 0.5 }"
|
v-for="(legend, index) in legendList"
|
||||||
/>
|
:key="index"
|
||||||
<v-axis
|
@click="handleLegendChange(legend)"
|
||||||
data-key="time"
|
>
|
||||||
:label="{ textStyle: { fill: '#ade6ee' }, offset: 25 }"
|
<span class="legend-item-color" :style="{ backgroundColor: legend.color }"></span>
|
||||||
:grid="{ lineStyle: { stroke: '#406979', strokeOpacity: 0.2, lineDash: [0, 0] }, hideLastLine: true }"
|
<span>{{ legend.title }}</span>
|
||||||
:line="{ stroke: '#406979', strokeOpacity: 0.5 }"
|
</div>
|
||||||
:tick-line="null"
|
</div>
|
||||||
/>
|
<!-- 图例结束 -->
|
||||||
<v-polygon position="time*name" shape="custom" :v-style="{ lineWidth: 0 }" />
|
<custom-chart ref="customChart" :option="option" :height="list.length * 295"></custom-chart>
|
||||||
</v-chart>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { registerShape } from 'viser-vue'
|
import CustomChart from '@/components/CustomChart/index.vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
registerShape('polygon', 'custom', {
|
const typeList = ['MET', 'SOH', 'QC', 'PHD'] //
|
||||||
draw: function(cfg, container) {
|
|
||||||
const color = cfg.origin._origin.color // 颜色,取自datasource原始数据中的color
|
|
||||||
|
|
||||||
// 绘制色块
|
// 自定义图例列表
|
||||||
return color
|
const legendList = [
|
||||||
? container.addShape('rect', {
|
{
|
||||||
attrs: {
|
title: 'SPHDPREL',
|
||||||
x: cfg.x - 32,
|
color: '#17a840',
|
||||||
y: cfg.y - 10,
|
isShow: true
|
||||||
width: 32,
|
},
|
||||||
height: 20,
|
{
|
||||||
fill: color,
|
title: 'SPHDF',
|
||||||
stroke: color
|
color: '#0cbfb0',
|
||||||
|
isShow: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'QC',
|
||||||
|
color: '#1c82eb',
|
||||||
|
isShow: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GASBKPHDPREL',
|
||||||
|
color: '#d3ad16',
|
||||||
|
isShow: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GASBKPHDF',
|
||||||
|
color: '#db6423',
|
||||||
|
isShow: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'SOH/MET',
|
||||||
|
color: '#8852da',
|
||||||
|
isShow: true
|
||||||
}
|
}
|
||||||
})
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const timeList = [
|
|
||||||
'08:00\n04/06',
|
|
||||||
'10:00\n04/06',
|
|
||||||
'12:00\n04/06',
|
|
||||||
'14:00\n04/06',
|
|
||||||
'16:00\n04/06',
|
|
||||||
'18:00\n04/06',
|
|
||||||
'20:00\n04/06',
|
|
||||||
'22:00\n04/06',
|
|
||||||
'00:00\n04/07',
|
|
||||||
'02:00\n04/07',
|
|
||||||
'04:00\n04/07',
|
|
||||||
'06:00\n04/07',
|
|
||||||
// '08:00\n04/07'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const typeList = ['MET', 'SOH', 'QC', 'PHD']
|
// 图表配置
|
||||||
|
const initialOption = {
|
||||||
|
legend: [
|
||||||
|
{
|
||||||
|
data: legendList.map(legend => legend.title),
|
||||||
|
itemGap: 30,
|
||||||
|
itemWidth: 12,
|
||||||
|
itemHeight: 12,
|
||||||
|
textStyle: {
|
||||||
|
color: '#ade6ee'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
formatter: params => {
|
||||||
|
return `${params.marker}${params.name}: ${(params.value[3] / 1000 / 60).toFixed()}min`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: [],
|
||||||
|
xAxis: [],
|
||||||
|
yAxis: [],
|
||||||
|
series: [],
|
||||||
|
dataZoom: []
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
legendList: {
|
|
||||||
type: Array
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 250
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
type: String
|
type: String
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
scaleSettings: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
CustomChart
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dataSource: []
|
legendList,
|
||||||
|
option: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
methods: {
|
||||||
const dataSource = typeList.reduce((prev, type) => {
|
renderItem(params, api) {
|
||||||
return prev.concat(
|
let categoryIndex = api.value(0)
|
||||||
timeList.map(time => {
|
let start = api.coord([api.value(1), categoryIndex])
|
||||||
return {
|
let end = api.coord([api.value(2), categoryIndex])
|
||||||
name: type,
|
let height = 20
|
||||||
color: '',
|
|
||||||
time
|
let 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',
|
||||||
|
shape: rectShape,
|
||||||
|
style: api.style()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
convertStatus(item) {
|
||||||
|
switch (item.status) {
|
||||||
|
case 'SFULL':
|
||||||
|
item.status = 'SPHDF'
|
||||||
|
break
|
||||||
|
case 'SPREL':
|
||||||
|
item.status = 'SPHDPREL'
|
||||||
|
break
|
||||||
|
case 'GPREL':
|
||||||
|
item.status = 'GASBKPHDPREL'
|
||||||
|
break
|
||||||
|
case 'GFULL':
|
||||||
|
item.status = 'GASBKPHDF'
|
||||||
|
break
|
||||||
|
case 'QC':
|
||||||
|
item.status = 'QC'
|
||||||
|
break
|
||||||
|
case 'SOH':
|
||||||
|
case 'MET':
|
||||||
|
item.status = 'SOH/MET'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initChartOption() {
|
||||||
|
const max = new Date('2015/12/25 07:00:00').getTime()
|
||||||
|
const min = max - this.scaleSettings.cacheTime * 24 * 60 * 60 * 1000
|
||||||
|
const option = cloneDeep(initialOption)
|
||||||
|
const { grid, xAxis, yAxis, series, dataZoom } = option
|
||||||
|
this.list.forEach((item, index) => {
|
||||||
|
console.log('%c [ item ]-185', 'font-size:13px; background:pink; color:#bf2c9f;', item)
|
||||||
|
grid.push({
|
||||||
|
left: 70,
|
||||||
|
right: 20,
|
||||||
|
top: 70 * index + 225 * index,
|
||||||
|
height: 225
|
||||||
|
})
|
||||||
|
|
||||||
|
xAxis.push({
|
||||||
|
gridIndex: index,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
interval: this.scaleSettings.scaleInterval * 60 * 1000,
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
formatter: val => {
|
||||||
|
let dateTime = new Date(val)
|
||||||
|
return dayjs(dateTime).format('HH:mm\nMM/DD')
|
||||||
|
},
|
||||||
|
color: '#ade6ee'
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#214751'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
dataSource[36].color = '#d3ad16'
|
yAxis.push({
|
||||||
dataSource[37].color = '#d3ad16'
|
gridIndex: index,
|
||||||
dataSource[38].color = '#d3ad16'
|
name: item.title,
|
||||||
dataSource[39].color = '#d3ad16'
|
nameLocation: 'middle', // 设置标题位置为中间
|
||||||
dataSource[40].color = '#db6423'
|
nameTextStyle: {
|
||||||
dataSource[41].color = '#17a840'
|
// 设置标题样式
|
||||||
dataSource[42].color = '#17a840'
|
fontSize: 14,
|
||||||
dataSource[43].color = '#17a840'
|
color: '#5b9cba'
|
||||||
dataSource[44].color = '#17a840'
|
},
|
||||||
dataSource[45].color = '#17a840'
|
nameGap: 46,
|
||||||
dataSource[46].color = '#8c6513'
|
data: typeList,
|
||||||
dataSource[47].color = '#d3ad16'
|
splitLine: {
|
||||||
dataSource[28].color = '#1c82eb'
|
show: true,
|
||||||
dataSource[34].color = '#1c82eb'
|
lineStyle: {
|
||||||
|
color: '#214751'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#214751'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#ade6ee'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.dataSource = dataSource
|
const data = []
|
||||||
|
item.dataList.forEach(item => {
|
||||||
|
this.convertStatus(item)
|
||||||
|
|
||||||
|
const startTime = new Date(item.beginTime * 1000).getTime()
|
||||||
|
const endTime = new Date(item.endTime * 1000).getTime()
|
||||||
|
const duration = endTime - startTime
|
||||||
|
const index = typeList.findIndex(type => item.type == type)
|
||||||
|
const find = legendList.find(legendItem => legendItem.title == item.status)
|
||||||
|
if (find.isShow) {
|
||||||
|
data.push({
|
||||||
|
name: item.status,
|
||||||
|
value: [index, startTime, endTime, duration],
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: find.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
series.push({
|
||||||
|
type: 'custom',
|
||||||
|
renderItem: this.renderItem,
|
||||||
|
encode: {
|
||||||
|
x: [1, 2],
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
xAxisIndex: index,
|
||||||
|
yAxisIndex: index
|
||||||
|
})
|
||||||
|
|
||||||
|
dataZoom.push({
|
||||||
|
type: 'inside',
|
||||||
|
xAxisIndex: index,
|
||||||
|
start: 100 - (this.scaleSettings.timelineLength / (this.scaleSettings.cacheTime * 24 * 60)) * 100,
|
||||||
|
end: 100,
|
||||||
|
zoomLock: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.option = option
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图例控制图表
|
||||||
|
handleLegendChange(legend) {
|
||||||
|
legend.isShow = !legend.isShow
|
||||||
|
this.initChartOption()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
list: {
|
||||||
|
handler() {
|
||||||
|
this.initChartOption()
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style lang="less" scoped>
|
||||||
|
.legend {
|
||||||
|
margin: 32px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
color: #ade6ee;
|
||||||
|
line-height: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-color {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 6px;
|
||||||
|
vertical-align: baseline;
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.legend-item-color {
|
||||||
|
filter: grayscale(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user