feat: 对接Data Recevice status Monitoring右侧图表,自定义并实现图例功能
This commit is contained in:
parent
10bae9ed27
commit
86c8e1b69e
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 // 颜色,取自datasource原始数据中的color
|
||||
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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user