map/ui/dasadmin/src/views/backend/equipment/equipmentManagement/measurement.vue
高云鹏 2765ed10e2 单风机:调整背景图相关内容
设备:量测时间间隔添加1分钟10分钟
2024-11-29 16:28:21 +08:00

530 lines
16 KiB
Vue

<template>
<div class="measurement">
<el-table :columns="tableColumn" :data="tableData" @sort-change="sortChange" max-height="495">
<el-table-column
v-for="item in tableColumn"
:key="item.prop"
:label="item.label"
:prop="item.prop"
:width="item.width ?? ''"
:align="item.align"
:sortable="item.sortable"
>
<template #default="scope">
<div v-if="item.prop === 'realTimeValue'" class="realTimeValue">
<div class="realTimeValueText">{{ scope.row.realTimeValue }}</div>
</div>
<div v-if="item.prop === 'operate'" @click="openLineChart(scope.row)" class="operate">
<template v-if="!enumStore.keys.includes(scope.row.attributeCode)">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="20px" height="20px" viewBox="0 0 1024 1024" version="1.1">
{{ scope.row }}
<path
d="M896 896H96a32 32 0 0 1-32-32V224a32 32 0 0 1 64 0v608h768a32 32 0 1 1 0 64zM247.008 640a32 32 0 0 1-20.992-56.192l200.992-174.24a32 32 0 0 1 42.272 0.288l172.128 153.44 229.088-246.304a32 32 0 0 1 46.88 43.616l-250.432 269.216a31.936 31.936 0 0 1-44.704 2.08l-174.56-155.52-179.744 155.84a31.872 31.872 0 0 1-20.928 7.776z"
/>
</svg>
</template>
</div>
</template>
</el-table-column>
</el-table>
<div>
<el-pagination
v-model:current-page="pageSetting.current"
v-model:page-size="pageSetting.pageSize"
:total="pageSetting.total"
:page-sizes="pageSetting.pageSizes"
background
:pager-count="7"
layout="prev, pager, next, jumper,sizes,total"
@change="getcurrentPage"
></el-pagination>
</div>
</div>
<el-dialog v-model="lineChartVisible" title="历史曲线" @close="closeLineChart" :width="910">
<el-form :inline="true" :model="seachOptions" :rules="searchRules" ref="searchFormRef">
<div class="searchPart">
<div>
<el-form-item prop="datePickerValue" label="历史区间:">
<el-date-picker
v-model="seachOptions.datePickerValue"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY/MM/DD"
time-format="HH:mm:ss"
:shortcuts="shortcuts"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
</div>
<div>
<el-form-item prop="interval" label="时间间隔:">
<el-select v-model="seachOptions.interval" placeholder="请选择时间间隔" style="width: 100px">
<el-option label="原始" value="40s"></el-option>
<el-option label="1分钟" value="1m"></el-option>
<el-option label="5分钟" value="5m"></el-option>
<el-option label="10分钟" value="10m"></el-option>
<el-option label="15分钟" value="15m"></el-option>
<el-option label="1小时" value="1h"></el-option>
<el-option label="1天" value="1d"></el-option>
</el-select>
</el-form-item>
</div>
<el-button type="primary" @click="getChartData" :loading="loading">查询</el-button>
</div>
</el-form>
<div class="chartPart">
<div class="lineChart" ref="chartRef"></div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, watch, onMounted } from 'vue'
import { ElMessage, FormInstance, dayjs } from 'element-plus'
import type { ModelAttributeFieldsEnums, GetModelAttributeType } from '/@/views/backend/auth/model/type'
import { ModelAttributeType } from '/@/views/backend/auth/model/type'
import { getModelAttributeListReq, getRealValueListReq } from '/@/api/backend/deviceModel/request'
import * as echarts from 'echarts'
import { getRealValueRangeReq } from '/@/api/backend/deviceModel/request'
import { useEnumStore } from '/@/stores/enums'
const enumStore = useEnumStore()
const props = withDefaults(
defineProps<{ iotModelId: string; deviceId: string; show: boolean; autoUpdate: boolean; attributeType: ModelAttributeType }>(),
{
iotModelId: '',
deviceId: '',
show: false,
autoUpdate: false,
attributeType: 138,
}
)
const tableColumn = [
{
label: '序号',
prop: 'porder',
width: 76,
align: 'center',
sortable: 'custom',
},
{
label: '属性名称',
prop: 'attributeName',
align: 'left',
sortable: 'custom',
},
{
label: '属性编码',
prop: 'attributeCode',
align: 'left',
width: 200,
sortable: 'custom',
},
{
label: '子系统',
prop: 'subSystem',
align: 'left',
width: 110,
sortable: false,
},
{
label: '实时值',
prop: 'realTimeValue',
width: 140,
align: 'center',
sortable: false,
},
{
label: '历史曲线',
prop: 'operate',
align: 'center',
width: 80,
sortable: false,
},
]
const tableData = ref<any[]>([])
const getAttributeList = () => {
const requestData: GetModelAttributeType = {
iotModelId: props.iotModelId,
pageNum: pageSetting.current,
pageSize: pageSetting.pageSize,
orderColumn: sortData.orderColumn,
orderType: sortData.orderType,
attributeType: props.attributeType,
}
return new Promise((resolve) => {
getModelAttributeListReq(requestData)
.then((res) => {
if (res.rows && res.rows.length > 0) {
const codeList: any = []
const data = res.rows!.map((item) => {
codeList.push(item.attributeCode)
return {
...item,
attributeTypeName:
item.attributeType === 138
? '模拟量'
: item.attributeType === 139
? '累积量'
: item.attributeType === 140
? '离散量'
: item.attributeType!,
}
})
pageSetting.total = res.total
resolve({ data, codeList })
} else {
if (res.rows && res.rows.length === 0) {
tableData.value = []
} else {
ElMessage.error(res.msg)
}
}
})
.catch((err) => {
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
})
})
}
const getRealValueList = (data: { deviceId: string; attributes: string[] }, list?: any) => {
return new Promise((resolve) => {
getRealValueListReq([data]).then((res) => {
if (res.success && res.data) {
resolve({ realVal: res.data, list })
}
})
})
}
const getCompleteData = () => {
getAttributeList()
.then(({ data, codeList }: any) => {
return getRealValueList({ deviceId: props.deviceId, attributes: codeList }, data)
})
.then((realData: any) => {
console.log(realData)
const data = realData.list.map((item: any) => {
let realValItem = realData.realVal[props.deviceId]?.[item.attributeCode?.toLowerCase()]
if (enumStore.keys.includes(item.attributeCode)) {
realValItem = enumStore.data[item.attributeCode][realValItem]
}
return {
...item,
realTimeValue:
typeof realValItem === 'number'
? realValItem
? realValItem % 1 === 0
? realValItem
: realValItem.toFixed(3)
: '-'
: realValItem,
}
})
tableData.value = data
})
}
const sortData = reactive<{ orderColumn?: keyof typeof ModelAttributeFieldsEnums; orderType?: 'asc' | 'desc' }>({
orderColumn: 'porder',
orderType: 'asc',
})
const sortChange = ({ prop, order }: { prop: keyof typeof ModelAttributeFieldsEnums; order: 'ascending' | 'descending' | null }) => {
const propEnums = {
attributeCode: 'attribute_code',
attributeName: 'attribute_name',
attributeTypeName: 'attribute_type',
porder: 'porder',
serviceCode: 'service_code',
serviceName: 'service_name',
serviceTypeName: 'service_type',
}
const orderType = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : undefined
const filed = propEnums[prop as keyof typeof propEnums] as keyof typeof ModelAttributeFieldsEnums
sortData.orderColumn = orderType ? filed : undefined
sortData.orderType = orderType
getCompleteData()
}
const pageSetting = reactive({
current: 1,
pageSize: 20,
total: 0,
pageSizes: [20, 50, 100],
})
const getcurrentPage = () => {
getCompleteData()
}
const openLineChart = (data: any) => {
lineChartVisible.value = true
searchInfo.attr = data.attributeCode
searchInfo.name = data.attributeName
searchInfo.unit = data.unit
}
watch(
() => props.show,
(newVal) => {
if (newVal) {
getCompleteData()
} else {
autoUpdateTimer.value && clearInterval(autoUpdateTimer.value)
autoUpdateTimer.value = null
}
},
{
immediate: true,
}
)
const autoUpdateTimer: any = ref(null)
watch(
() => props.autoUpdate,
(newVal) => {
if (newVal) {
if (!autoUpdateTimer.value) {
autoUpdateTimer.value = setInterval(() => {
getCompleteData()
}, 2000)
}
} else {
clearInterval(autoUpdateTimer.value)
autoUpdateTimer.value = null
}
}
)
const loading = ref(false)
const searchFormRef = ref<FormInstance>()
const lineChartVisible = ref(false)
const searchInfo = reactive({
unit: '',
name: '',
attr: '',
})
const shortcuts = [
{
text: '今天',
value: () => {
const start = dayjs().startOf('day').toDate()
const end = dayjs().endOf('day').toDate()
return [start, end]
},
},
{
text: '昨天',
value: () => {
const start = dayjs().subtract(1, 'day').startOf('day').toDate()
const end = dayjs().subtract(1, 'day').endOf('day').toDate()
return [start, end]
},
},
{
text: '前三天',
value: () => {
const start = dayjs().subtract(4, 'day').startOf('day').toDate()
const end = dayjs().subtract(1, 'day').endOf('day').toDate()
return [start, end]
},
},
]
const getChartData = () => {
searchFormRef.value?.validate((valid) => {
if (valid) {
loading.value = true
getRealValueRangeReq({
startTime: dayjs(seachOptions.datePickerValue[0]).valueOf(),
endTime: dayjs(seachOptions.datePickerValue[1]).valueOf(),
devices: [
{
deviceId: props.deviceId,
attributes: [searchInfo.attr],
},
],
interval: seachOptions.interval,
}).then((res) => {
initChart(res.data?.[props.deviceId]?.[searchInfo.attr])
})
}
})
}
const chartRef = ref()
let chartInstance: any = null
const seachOptions = reactive({
datePickerValue: [0, 0],
interval: '5m',
})
const searchRules = {
datePickerValue: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value[0] || !value[1]) {
callback(new Error('请选择时间范围'))
return
}
callback()
},
trigger: 'change',
},
],
interval: [
{
required: true,
message: '请输入时间间隔',
trigger: 'input',
},
],
}
const initChart = (data: { values: number[]; times: number[] }) => {
chartInstance && chartInstance.clear()
chartInstance = chartInstance ? chartInstance : echarts.init(chartRef.value)
const times = data?.times.map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
const option = {
animation: false,
grid: {
top: 50,
right: 23,
bottom: 10,
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',
//rotate: 45
},
splitLine: {
//分割线配置
show: false,
lineStyle: {
color: '#999999',
},
},
data: times ?? [],
},
yAxis: [
{
type: 'value',
name: searchInfo.unit,
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',
},
},
},
],
legend: {
data: [searchInfo.name],
textStyle: {
color: '#73767a',
},
},
series: [
{
name: searchInfo.name,
type: 'line',
barWidth: 20,
itemStyle: {
color: '#00A7EB',
barBorderRadius: 2,
},
smooth: 0.6,
symbol: 'none',
data: data?.values ?? []
},
],
}
chartInstance.setOption(option)
loading.value = false
}
const closeLineChart = () => {
chartInstance && chartInstance.clear()
searchFormRef.value!.resetFields()
}
watch(
() => props.attributeType,
() => {
getCompleteData()
}
)
</script>
<style scoped lang="scss">
.realTimeValueText {
color: #0064aa;
}
.operate {
&:hover {
cursor: pointer;
}
}
.searchPart {
width: 100%;
display: flex;
justify-content: space-between;
}
.chartPart {
width: 100%;
height: 400px;
.lineChart {
width: 100%;
height: 100%;
}
}
</style>