2024-11-26 10:51:14 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="realDataChart">
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="8" class="leftPart">
|
|
|
|
|
<div class="leftHeader">
|
|
|
|
|
<el-button type="primary" @click="addPoint">添加测点</el-button>
|
2024-11-26 17:36:51 +08:00
|
|
|
|
<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>
|
2024-11-26 10:51:14 +08:00
|
|
|
|
</div>
|
|
|
|
|
<div class="leftMain">
|
|
|
|
|
<el-scrollbar>
|
2024-11-26 17:36:51 +08:00
|
|
|
|
<el-checkbox-group v-model="selectList" @change="changeCheck">
|
2024-11-26 10:51:14 +08:00
|
|
|
|
<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'
|
2024-11-26 17:36:51 +08:00
|
|
|
|
import { dayjs, ElMessage } from 'element-plus'
|
|
|
|
|
import { getCutDecimalsValue } from './utils'
|
2024-11-26 10:51:14 +08:00
|
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<{ id: string; visible: boolean }>(), {
|
|
|
|
|
id: '',
|
|
|
|
|
visible: false,
|
|
|
|
|
})
|
2024-11-26 17:36:51 +08:00
|
|
|
|
const showTimeInterval = ref(300)
|
2024-11-26 10:51:14 +08:00
|
|
|
|
//#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([])
|
2024-11-26 17:36:51 +08:00
|
|
|
|
const changeCheck = () => {
|
|
|
|
|
if (!timer && selectList.value[0]) {
|
|
|
|
|
createTimer()
|
2024-11-26 10:51:14 +08:00
|
|
|
|
}
|
2024-11-26 17:36:51 +08:00
|
|
|
|
|
|
|
|
|
if (selectList.value.length === 0) {
|
|
|
|
|
clearTimer()
|
|
|
|
|
chartInstance && chartInstance.clear()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// watch(
|
|
|
|
|
// () => selectList.value,
|
|
|
|
|
// () => {
|
|
|
|
|
// if (!timer && selectList.value[0]) {
|
|
|
|
|
// createTimer()
|
|
|
|
|
// }
|
|
|
|
|
// if (selectList.value.length === 0) {
|
|
|
|
|
// clearTimer()
|
|
|
|
|
// chartInstance && chartInstance.clear()
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// )
|
2024-11-26 10:51:14 +08:00
|
|
|
|
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 []
|
|
|
|
|
}
|
|
|
|
|
let realDataXAxis: any = []
|
|
|
|
|
let realDataSeries: 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) => {
|
2024-11-26 17:36:51 +08:00
|
|
|
|
if (realDataXAxis.length > showTimeInterval.value) {
|
2024-11-26 10:51:14 +08:00
|
|
|
|
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.find((val: any) => val.id === item)
|
|
|
|
|
delete cur.id
|
|
|
|
|
clearState = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (clearState) {
|
|
|
|
|
chartInstance.setOption(
|
|
|
|
|
{
|
|
|
|
|
series: realDataSeries,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
replaceMerge: ['series'],
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const seriesData = attrCode.map((item) => {
|
2024-11-26 17:36:51 +08:00
|
|
|
|
const curVal = getCutDecimalsValue(data[item], 2)
|
2024-11-26 10:51:14 +08:00
|
|
|
|
if (lastSeriesId.includes(item)) {
|
|
|
|
|
const cur = realDataSeries.find((val: any) => val.id === item)
|
2024-11-26 17:36:51 +08:00
|
|
|
|
cur.data.push(curVal)
|
2024-11-26 10:51:14 +08:00
|
|
|
|
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('')
|
2024-11-26 17:36:51 +08:00
|
|
|
|
fillData.push(curVal)
|
2024-11-26 10:51:14 +08:00
|
|
|
|
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) {
|
|
|
|
|
option = {
|
|
|
|
|
xAxis: {
|
|
|
|
|
data: realDataXAxis,
|
|
|
|
|
},
|
|
|
|
|
series: realDataSeries,
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
option = {
|
|
|
|
|
grid: {
|
|
|
|
|
top: 50,
|
|
|
|
|
right: 23,
|
|
|
|
|
bottom: 50,
|
|
|
|
|
left: 18,
|
|
|
|
|
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,
|
|
|
|
|
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' })
|
|
|
|
|
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 saveSelectPoint = () => {
|
|
|
|
|
const data = selectPointRef.value?.getSelectList()
|
|
|
|
|
if (data) {
|
|
|
|
|
const selectList = data.map((item: any) => {
|
|
|
|
|
return {
|
|
|
|
|
prop: item.attributeCode,
|
|
|
|
|
name: item.attributeName,
|
|
|
|
|
unit: item.unit,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
realDataList.value = selectList
|
2024-11-26 17:36:51 +08:00
|
|
|
|
selectPointVisible.value = false
|
|
|
|
|
ElMessage.success('添加成功')
|
2024-11-26 10:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let timer: any = null
|
|
|
|
|
const createTimer = () => {
|
|
|
|
|
timer = setInterval(() => {
|
|
|
|
|
getRealData()
|
|
|
|
|
}, 1000)
|
|
|
|
|
}
|
|
|
|
|
const clearTimer = () => {
|
|
|
|
|
timer && clearInterval(timer)
|
|
|
|
|
timer = null
|
|
|
|
|
realDataSeries = []
|
|
|
|
|
realDataXAxis = []
|
|
|
|
|
}
|
2024-11-26 17:36:51 +08:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
})
|
2024-11-26 10:51:14 +08:00
|
|
|
|
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 {
|
2024-11-26 17:36:51 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
2024-11-26 10:51:14 +08:00
|
|
|
|
padding: 10px;
|
2024-11-26 17:36:51 +08:00
|
|
|
|
span {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
}
|
|
|
|
|
.el-select {
|
|
|
|
|
width: 100px;
|
|
|
|
|
}
|
|
|
|
|
:deep(.el-select__wrapper) {
|
|
|
|
|
width: 100px;
|
|
|
|
|
}
|
2024-11-26 10:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-26 17:36:51 +08:00
|
|
|
|
.selectPointDialogFooter {
|
|
|
|
|
text-align: center;
|
|
|
|
|
.el-button {
|
|
|
|
|
width: 120px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-26 10:51:14 +08:00
|
|
|
|
</style>
|