556 lines
16 KiB
Vue
556 lines
16 KiB
Vue
<template>
|
||
<div class="realDataChart">
|
||
<el-row>
|
||
<el-col :span="8" class="leftPart">
|
||
<div class="leftHeader">
|
||
<el-button type="primary" @click="addPoint">添加测点</el-button>
|
||
<span>显示区间:</span>
|
||
<el-select v-model="showTimeInterval">
|
||
<el-option label="5分钟" :value="300"></el-option>
|
||
<el-option label="10分钟" :value="600"></el-option>
|
||
<el-option label="15分钟" :value="900"></el-option>
|
||
</el-select>
|
||
</div>
|
||
<div class="leftMain">
|
||
<el-scrollbar>
|
||
<el-checkbox-group v-model="selectList" @change="changeCheck" :max="selectPointNum">
|
||
<el-checkbox
|
||
v-for="item in realDataList"
|
||
:key="item.prop"
|
||
:label="item.name"
|
||
:value="item.prop"
|
||
class="checkboxStyle"
|
||
></el-checkbox>
|
||
</el-checkbox-group>
|
||
</el-scrollbar>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="16" class="rightPart">
|
||
<div class="chartPart">
|
||
<div class="chartRef" ref="chartRef"></div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
<el-dialog v-model="selectPointVisible" title="选择测点" width="1000">
|
||
<SelectPoint ref="selectPointRef" :visible="selectPointVisible" :default-attr="selectPointAttr"></SelectPoint>
|
||
<template #footer>
|
||
<div class="selectPointDialogFooter">
|
||
<el-button type="primary" @click="saveSelectPoint">保存</el-button>
|
||
<el-button @click="selectPointVisible = false">取消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||
import * as echarts from 'echarts'
|
||
import SelectPoint from '/@/views/backend/equipment/airBlower/selectPoint.vue'
|
||
import { getRealValueListReq } from '/@/api/backend/deviceModel/request'
|
||
import { dayjs, ElMessage } from 'element-plus'
|
||
import { getCutDecimalsValue } from './utils'
|
||
const emits = defineEmits(['clearChart'])
|
||
const props = withDefaults(defineProps<{ id: string; visible: boolean }>(), {
|
||
id: '',
|
||
visible: false,
|
||
})
|
||
const showTimeInterval = ref(300)
|
||
//#region
|
||
const defaultList = [
|
||
{
|
||
prop: 'iturbineoperationmode',
|
||
name: '运行模式',
|
||
unit: '',
|
||
},
|
||
{
|
||
prop: 'iwindspeed',
|
||
name: '风速',
|
||
unit: '(m/s)',
|
||
},
|
||
{
|
||
prop: 'iwinddirection',
|
||
name: '风向',
|
||
unit: '(°)',
|
||
},
|
||
{
|
||
prop: 'igenspeed',
|
||
name: '发电机转速',
|
||
unit: '(rpm)',
|
||
},
|
||
{
|
||
prop: 'igenpower',
|
||
name: '有功功率',
|
||
unit: '(kW)',
|
||
},
|
||
{
|
||
prop: 'iactivepowersetpointvalue',
|
||
name: '有功给定',
|
||
unit: '(kW)',
|
||
},
|
||
{
|
||
prop: 'ireactivepower',
|
||
name: '无功功率',
|
||
unit: '(kVar)',
|
||
},
|
||
{
|
||
prop: 'ireactivepowersetpointvalue',
|
||
name: '无功给定',
|
||
unit: '(kVar)',
|
||
},
|
||
{
|
||
prop: 'ipitchangle1',
|
||
name: '桨叶1角度',
|
||
unit: '(°)',
|
||
},
|
||
{
|
||
prop: 'ipitchangle2',
|
||
name: '桨叶2角度',
|
||
unit: '(°)',
|
||
},
|
||
{
|
||
prop: 'ipitchangle3',
|
||
name: '桨叶3角度',
|
||
unit: '(°)',
|
||
},
|
||
]
|
||
//#endregion
|
||
const selectList = ref([])
|
||
const changeCheck = () => {
|
||
if (!timer && selectList.value[0]) {
|
||
createTimer()
|
||
}
|
||
|
||
if (selectList.value.length === 0) {
|
||
clearTimer()
|
||
chartInstance && chartInstance.clear()
|
||
}
|
||
}
|
||
const realDataList = ref<any[]>(JSON.parse(JSON.stringify(defaultList)))
|
||
|
||
const chartRef = ref()
|
||
let chartInstance: any = null
|
||
|
||
const getRealData = () => {
|
||
const params = selectList.value.map((item: string) => item.toLowerCase())
|
||
const time = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||
getRealValueListReq([
|
||
{
|
||
deviceId: props.id,
|
||
attributes: params,
|
||
},
|
||
]).then((res) => {
|
||
if (res.code === 200) {
|
||
createChartData(res.data[props.id], time)
|
||
}
|
||
})
|
||
return []
|
||
}
|
||
const selectPointNum = ref(10)
|
||
let realDataXAxis: any = []
|
||
let realDataSeries: any = []
|
||
let realDataYAxis: any = []
|
||
const getRandomDarkColor = () => {
|
||
let r = Math.floor(Math.random() * 200) // 限制在0到127之间,以生成较深的颜色
|
||
let g = Math.floor(Math.random() * 200)
|
||
let b = Math.floor(Math.random() * 200)
|
||
|
||
let color = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
|
||
return color
|
||
}
|
||
|
||
const createChartData = (data: { [k: string]: number }, time: string) => {
|
||
if (realDataXAxis.length > showTimeInterval.value) {
|
||
realDataXAxis.shift()
|
||
realDataSeries.forEach((item: any) => {
|
||
item.data.shift()
|
||
})
|
||
}
|
||
const attrCode = Object.keys(data)
|
||
realDataXAxis.push(time)
|
||
const lastSeriesId = realDataSeries.map((item: any) => item.id)
|
||
const color = getRandomDarkColor()
|
||
let clearState = null
|
||
lastSeriesId.forEach((item: any) => {
|
||
if (!attrCode.includes(item)) {
|
||
const cur = realDataSeries.findIndex((val: any) => val.id === item)
|
||
// delete cur.id
|
||
realDataSeries.splice(cur, 1)
|
||
clearState = true
|
||
}
|
||
})
|
||
if (clearState) {
|
||
chartInstance.setOption(
|
||
{
|
||
series: realDataSeries,
|
||
},
|
||
{
|
||
replaceMerge: ['series'],
|
||
}
|
||
)
|
||
}
|
||
|
||
const seriesData = attrCode.map((item) => {
|
||
const curVal = getCutDecimalsValue(data[item], 2)
|
||
if (lastSeriesId.includes(item)) {
|
||
const cur = realDataSeries.find((val: any) => val.id === item)
|
||
cur.data.push(curVal)
|
||
return cur
|
||
} else {
|
||
const info = realDataList.value.find((val) => val.prop === item)
|
||
const list = realDataSeries?.[0]?.data ?? []
|
||
const len = list.length
|
||
const fillData = new Array(len).fill('')
|
||
fillData.push(curVal)
|
||
return {
|
||
id: item,
|
||
name: info.name +' '+ (info?.unit ?? ''),
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: color,
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: true,
|
||
symbol: 'none',
|
||
data: fillData,
|
||
}
|
||
}
|
||
})
|
||
realDataSeries = seriesData
|
||
createChart()
|
||
}
|
||
const createChart = () => {
|
||
const chart = chartInstance ?? echarts.init(chartRef.value)
|
||
let option = null
|
||
if (chartInstance && realDataXAxis.length > 1) {
|
||
const nameMap: any = {}
|
||
realDataSeries.forEach((item: any) => {
|
||
const yAxisName = item.name.split(' ')[1]
|
||
if (nameMap[yAxisName] || nameMap[yAxisName]===0) {
|
||
item.yAxisIndex = nameMap[yAxisName]
|
||
} else {
|
||
const len = Object.keys(nameMap).length
|
||
item.yAxisIndex = len
|
||
nameMap[yAxisName] = len
|
||
}
|
||
})
|
||
|
||
const nameMapKeys = Object.keys(nameMap)
|
||
if (realDataSeries.length >= 4 && nameMapKeys.length === 4) {
|
||
selectPointNum.value = realDataSeries.length
|
||
}
|
||
const yAxisData = nameMapKeys.map((item, index) => {
|
||
const offset = Math.floor(index / 2) * 50
|
||
const position = index % 2 == 0 ? 'left' : 'right'
|
||
const yAxisName = item
|
||
const cacheYAxis = realDataYAxis.find((item: any) => item.name === yAxisName)
|
||
if (cacheYAxis) {
|
||
return {
|
||
...cacheYAxis,
|
||
offset,
|
||
position,
|
||
}
|
||
} else {
|
||
return {
|
||
type: 'value',
|
||
name: item,
|
||
nameTextStyle: {
|
||
color: '#4E5969',
|
||
},
|
||
axisLine: {
|
||
show: false,
|
||
onZero: false,
|
||
lineStyle: {
|
||
color: '#dadada',
|
||
width: 0,
|
||
type: 'solid',
|
||
},
|
||
},
|
||
axisLabel: {
|
||
//x轴文字的配置
|
||
show: true,
|
||
color: '#4E5969',
|
||
},
|
||
axisTick: { show: false },
|
||
splitLine: {
|
||
interval: 50,
|
||
lineStyle: {
|
||
type: 'dashed',
|
||
color: '#dadada',
|
||
},
|
||
},
|
||
offset,
|
||
position,
|
||
}
|
||
}
|
||
})
|
||
realDataYAxis = yAxisData
|
||
option = {
|
||
xAxis: {
|
||
data: realDataXAxis,
|
||
},
|
||
yAxis: realDataYAxis,
|
||
series: realDataSeries,
|
||
}
|
||
} else {
|
||
option = {
|
||
grid: {
|
||
top: 50,
|
||
right: 60,
|
||
bottom: 50,
|
||
left: 60,
|
||
containLabel: true,
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow',
|
||
},
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: '#dadada',
|
||
width: 1,
|
||
type: 'solid',
|
||
},
|
||
},
|
||
axisLabel: {
|
||
//x轴文字的配置
|
||
show: true,
|
||
color: '#4E5969',
|
||
interval: 'auto',
|
||
formatter: function (value: any) {
|
||
return value.slice(11)
|
||
},
|
||
//rotate: 45
|
||
},
|
||
splitLine: {
|
||
//分割线配置
|
||
show: false,
|
||
lineStyle: {
|
||
color: '#999999',
|
||
},
|
||
},
|
||
data: realDataXAxis,
|
||
},
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
name: '',
|
||
nameTextStyle: {
|
||
color: '#4E5969',
|
||
},
|
||
axisLine: {
|
||
show: false,
|
||
onZero: false,
|
||
lineStyle: {
|
||
color: '#dadada',
|
||
width: 0,
|
||
type: 'solid',
|
||
},
|
||
},
|
||
axisLabel: {
|
||
//x轴文字的配置
|
||
show: true,
|
||
color: '#4E5969',
|
||
},
|
||
axisTick: { show: false },
|
||
splitLine: {
|
||
interval: 50,
|
||
lineStyle: {
|
||
type: 'dashed',
|
||
color: '#dadada',
|
||
},
|
||
},
|
||
},
|
||
],
|
||
dataZoom: [
|
||
{
|
||
type: 'inside',
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
{
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
],
|
||
legend: {
|
||
data: [],
|
||
textStyle: {
|
||
color: '#73767a',
|
||
},
|
||
},
|
||
series: realDataSeries,
|
||
}
|
||
}
|
||
chart.setOption(option, { replaceMerge: ['series', 'yAxis'] })
|
||
chartInstance = chart
|
||
}
|
||
|
||
const selectPointRef = ref()
|
||
const selectPointVisible = ref(false)
|
||
const selectPointAttr = computed(() => {
|
||
return realDataList.value.map((item) => {
|
||
return {
|
||
attributeName: item.name,
|
||
attributeCode: item.prop,
|
||
}
|
||
})
|
||
})
|
||
const addPoint = () => {
|
||
selectPointVisible.value = true
|
||
}
|
||
const checkShowChart = (data: any) => {
|
||
console.log(realDataSeries)
|
||
|
||
const curCode = data.map((item: any) => item.prop)
|
||
console.log(curCode, 'curCode')
|
||
for (let i = selectList.value.length - 1; i >= 0; i--) {
|
||
if (!curCode.includes(selectList.value[i])) {
|
||
selectList.value.splice(i, 1)
|
||
realDataSeries.splice(i, 1)
|
||
changeCheck()
|
||
}
|
||
}
|
||
}
|
||
const saveSelectPoint = () => {
|
||
const data = selectPointRef.value?.getSelectList()
|
||
if (data) {
|
||
const selectList = data.map((item: any) => {
|
||
return {
|
||
prop: item.attributeCode,
|
||
name: item.attributeName,
|
||
unit: item.unit,
|
||
}
|
||
})
|
||
checkShowChart(selectList)
|
||
realDataList.value = selectList
|
||
selectPointVisible.value = false
|
||
ElMessage.success('修改成功')
|
||
}
|
||
}
|
||
let timer: any = null
|
||
const createTimer = () => {
|
||
timer = setInterval(() => {
|
||
getRealData()
|
||
}, 1000)
|
||
}
|
||
const clearTimer = () => {
|
||
timer && clearInterval(timer)
|
||
timer = null
|
||
realDataSeries = []
|
||
realDataXAxis = []
|
||
realDataYAxis = []
|
||
pauseState.value = false
|
||
emits('clearChart')
|
||
}
|
||
|
||
const pauseState = ref(false)
|
||
const saveChart = () => {
|
||
timer && clearInterval(timer)
|
||
pauseState.value = true
|
||
return {
|
||
time: realDataXAxis,
|
||
chartData: realDataSeries,
|
||
}
|
||
}
|
||
const continueChart = () => {
|
||
pauseState.value = false
|
||
const fillVal = new Array(5).fill('')
|
||
realDataXAxis.push(...fillVal)
|
||
realDataSeries.forEach((item: any) => {
|
||
item.data.push(...fillVal)
|
||
})
|
||
createTimer()
|
||
}
|
||
defineExpose({
|
||
saveChart,
|
||
continueChart,
|
||
})
|
||
onMounted(() => {
|
||
if (selectList.value.length > 0) {
|
||
createTimer()
|
||
}
|
||
})
|
||
onUnmounted(() => {
|
||
clearTimer()
|
||
chartInstance && chartInstance.dispose()
|
||
selectList.value = []
|
||
})
|
||
|
||
watch(
|
||
() => props.visible,
|
||
(v) => {
|
||
if (!v) {
|
||
clearTimer()
|
||
chartInstance && chartInstance.clear()
|
||
selectList.value = []
|
||
}
|
||
}
|
||
)
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.realDataChart {
|
||
width: 100%;
|
||
height: 100%;
|
||
.leftPart {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-right: 1px solid #edf2fa;
|
||
.leftHeader {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 10px;
|
||
span {
|
||
margin-left: auto;
|
||
}
|
||
.el-select {
|
||
width: 100px;
|
||
}
|
||
:deep(.el-select__wrapper) {
|
||
width: 100px;
|
||
}
|
||
}
|
||
.leftMain {
|
||
width: 100%;
|
||
height: 400px;
|
||
.checkboxStyle {
|
||
display: flex;
|
||
width: 100%;
|
||
height: 40px;
|
||
}
|
||
}
|
||
}
|
||
.rightPart {
|
||
width: 100%;
|
||
height: 100%;
|
||
min-height: 452px;
|
||
.chartPart {
|
||
width: 100%;
|
||
height: 100%;
|
||
min-height: 452px;
|
||
|
||
.chartRef {
|
||
width: 100%;
|
||
height: 100%;
|
||
min-height: 452px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.selectPointDialogFooter {
|
||
text-align: center;
|
||
.el-button {
|
||
width: 120px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
</style>
|