feat: 对接Data Recevice status Monitoring右侧图表,自定义并实现图例功能

This commit is contained in:
Xu Zhimeng 2023-06-21 18:31:31 +08:00
parent 10bae9ed27
commit 86c8e1b69e
4 changed files with 348 additions and 162 deletions

View File

@ -22,7 +22,7 @@
"cron-parser": "^2.10.0",
"dayjs": "^1.8.0",
"dom-align": "1.12.0",
"echarts": "^4.9.0",
"echarts": "^5.4.2",
"enquire.js": "^2.1.6",
"js-cookie": "^2.2.0",
"lodash.get": "^4.4.2",

View File

@ -1,26 +1,43 @@
<template>
<div class="custom-chart" ref="containerRef"></div>
<div class="custom-chart" ref="containerRef" :style="{ height: height + 'px' }"></div>
</template>
<script>
import echarts from 'echarts'
import * as echarts from 'echarts'
export default {
props: {
option : {
type: Object,
default: () => ({})
}
props: {
option: {
type: Object,
default: () => ({})
},
height: {
type: Number,
default: 0
}
},
data() {
return {}
},
mounted() {
this.chart = echarts.init(this.$refs.containerRef)
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>
<style lang="less" scoped>
.custom-chart {
height: 100%;
height: 100%;
}
</style>

View File

@ -223,19 +223,8 @@
<!-- 右侧图表展示栏 -->
<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-for="(item, index) in statusList">
<RealTimeDataChart :legendList="legendList" :title="item.title" :key="index" />
<a-divider :key="index + '_divider'" v-if="index !== statusList.length - 1"></a-divider>
</template>
<RealTimeDataChart :list="statusList" :scale-settings="initialDataRecieveSettings" />
</template>
</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)
const rules = {
cacheTime: {
@ -441,7 +403,6 @@ export default {
leftPaneShow: true, //
legendList, //
statusList: [], //
isGettingStatusList: false,
showChart: true,
@ -785,12 +746,30 @@ export default {
//
async getDataRecieveStatusList() {
try {
console.log('%c [ ]-777', 'font-size:13px; background:pink; color:#bf2c9f;', this.initialDataRecieveSettings)
this.isGettingStatusList = true
const res = await getAction('/jeecg-station-operation/stationOperation/getDataReceivingStatus', {
userId: this.$store.getters.userInfo.id
})
console.log('%c [ res ]-640', 'font-size:13px; background:pink; color:#bf2c9f;', res)
const { success, result, message } = await getAction(
'/jeecg-station-operation/stationOperation/getDataReceivingStatus',
{
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
})
})
})
})
this.statusList = statusList
} else {
this.$message.error(message)
}
} catch (error) {
console.error(error)
} finally {
@ -1203,29 +1182,6 @@ export default {
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 {

View File

@ -1,113 +1,326 @@
<template>
<v-chart :force-fit="true" :height="height" :data="dataSource" :padding="[0, 0, 40, 60]">
<v-axis
data-key="name"
:title="{ text: title, textStyle: { fill: '#5b9cba', fontSize: 14 }, offset: 45 }"
:label="{ textStyle: { fill: '#ade6ee' } }"
:grid="{ lineStyle: { stroke: '#406979', strokeOpacity: 0.2, lineDash: [0, 0] } }"
:line="{ stroke: '#406979', strokeOpacity: 0.5 }"
/>
<v-axis
data-key="time"
:label="{ textStyle: { fill: '#ade6ee' }, offset: 25 }"
:grid="{ lineStyle: { stroke: '#406979', strokeOpacity: 0.2, lineDash: [0, 0] }, hideLastLine: true }"
:line="{ stroke: '#406979', strokeOpacity: 0.5 }"
:tick-line="null"
/>
<v-polygon position="time*name" shape="custom" :v-style="{ lineWidth: 0 }" />
</v-chart>
<div>
<!-- 图例 -->
<div class="legend">
<div
class="legend-item"
:class="{ active: legend.isShow }"
v-for="(legend, index) in legendList"
:key="index"
@click="handleLegendChange(legend)"
>
<span class="legend-item-color" :style="{ backgroundColor: legend.color }"></span>
<span>{{ legend.title }}</span>
</div>
</div>
<!-- 图例结束 -->
<custom-chart ref="customChart" :option="option" :height="list.length * 295"></custom-chart>
</div>
</template>
<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', {
draw: function(cfg, container) {
const color = cfg.origin._origin.color // ,datasourcecolor
const typeList = ['MET', 'SOH', 'QC', 'PHD'] //
//
return color
? container.addShape('rect', {
attrs: {
x: cfg.x - 32,
y: cfg.y - 10,
width: 32,
height: 20,
fill: color,
stroke: color
}
})
: null
//
const legendList = [
{
title: 'SPHDPREL',
color: '#17a840',
isShow: true
},
{
title: 'SPHDF',
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
}
})
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 {
props: {
legendList: {
type: Array
},
height: {
type: Number,
default: 250
},
title: {
type: String
},
list: {
type: Array,
default: () => []
},
scaleSettings: {
type: Object,
required: true
}
},
components: {
CustomChart
},
data() {
return {
dataSource: []
legendList,
option: {}
}
},
created() {
const dataSource = typeList.reduce((prev, type) => {
return prev.concat(
timeList.map(time => {
return {
name: type,
color: '',
time
methods: {
renderItem(params, api) {
let categoryIndex = api.value(0)
let start = api.coord([api.value(1), categoryIndex])
let end = api.coord([api.value(2), categoryIndex])
let height = 20
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'
dataSource[37].color = '#d3ad16'
dataSource[38].color = '#d3ad16'
dataSource[39].color = '#d3ad16'
dataSource[40].color = '#db6423'
dataSource[41].color = '#17a840'
dataSource[42].color = '#17a840'
dataSource[43].color = '#17a840'
dataSource[44].color = '#17a840'
dataSource[45].color = '#17a840'
dataSource[46].color = '#8c6513'
dataSource[47].color = '#d3ad16'
dataSource[28].color = '#1c82eb'
dataSource[34].color = '#1c82eb'
yAxis.push({
gridIndex: index,
name: item.title,
nameLocation: 'middle', //
nameTextStyle: {
//
fontSize: 14,
color: '#5b9cba'
},
nameGap: 46,
data: typeList,
splitLine: {
show: true,
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>
<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>