2025-01-13 17:45:55 +08:00
|
|
|
<template>
|
|
|
|
<div class="historyData">
|
|
|
|
<el-container class="container">
|
|
|
|
<el-header class="header">
|
|
|
|
<div class="timePart">
|
|
|
|
<span class="label">时间</span>
|
|
|
|
<div class="timeSelect">
|
|
|
|
<el-date-picker
|
|
|
|
v-model="searchData.time"
|
|
|
|
:type="searchData.interval == '1d' ? 'daterange' : 'datetimerange'"
|
|
|
|
:value-format="searchData.interval == '1d' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
|
|
|
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]"
|
|
|
|
:teleported="false"
|
|
|
|
:shortcuts="shortcuts"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="intervalPart">
|
|
|
|
<span class="label">间隔</span>
|
|
|
|
<el-select v-model="searchData.interval" placeholder="请选择间隔">
|
|
|
|
<el-option v-for="v in intervalOptions" :key="v.value" :label="v.label" :value="v.value"></el-option>
|
|
|
|
</el-select>
|
|
|
|
</div>
|
|
|
|
<div class="templatePart">
|
|
|
|
<span class="label">模板</span>
|
|
|
|
<el-select v-model="searchData.template" placeholder="请选择模板" clearable>
|
|
|
|
<el-option v-for="v in templateOptions" :key="v.id" :label="v.name" :value="v.id" @click="changeTemplate">
|
|
|
|
<template #default>
|
|
|
|
<div class="templateOption">
|
|
|
|
<span>{{ v.name }}</span>
|
2025-01-14 09:42:58 +08:00
|
|
|
<el-icon :size="18" style="color: red" @click.stop="delTemplate(v.id)">
|
2025-01-13 17:45:55 +08:00
|
|
|
<Delete />
|
|
|
|
</el-icon>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-option>
|
|
|
|
</el-select>
|
|
|
|
</div>
|
|
|
|
<el-button type="primary" :icon="Crop" class="pointSelect" @click="openMeasure">测点选择</el-button>
|
|
|
|
<div class="opeareBtn">
|
|
|
|
<el-button type="primary" :loading="searchLoading" @click="getData">查询</el-button>
|
|
|
|
<el-button style="color: #0064aa" @click="exportExcel">导出</el-button>
|
|
|
|
<el-button class="button" :icon="Notebook" type="primary" @click="addTemplate" plain>保存为模板</el-button>
|
|
|
|
</div>
|
|
|
|
</el-header>
|
|
|
|
<el-main class="main">
|
|
|
|
<el-table v-loading="searchLoading" :data="tableData" height="100%">
|
|
|
|
<el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label"></el-table-column>
|
|
|
|
</el-table>
|
|
|
|
</el-main>
|
|
|
|
<el-footer class="footer">
|
|
|
|
<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>
|
|
|
|
</el-footer>
|
|
|
|
</el-container>
|
|
|
|
<el-dialog v-model="pointDialogVisible" title="测点选择" width="1200">
|
|
|
|
<el-row :gutter="10" class="pointDialogRow">
|
|
|
|
<el-col :span="4" class="pointDialogCol">
|
|
|
|
<div class="pointDialogColTitle">风机列表</div>
|
|
|
|
<div class="pointDialogColContent">
|
|
|
|
<el-scrollbar>
|
|
|
|
<el-checkbox-group v-model="selectWindBlower">
|
|
|
|
<el-checkbox
|
|
|
|
v-for="v in windBlowerOptions"
|
|
|
|
:key="v.value"
|
|
|
|
:label="v.label"
|
|
|
|
:value="v.value"
|
|
|
|
size="large"
|
|
|
|
class="windBlowerCheckbox"
|
|
|
|
></el-checkbox>
|
|
|
|
</el-checkbox-group>
|
|
|
|
</el-scrollbar>
|
|
|
|
</div>
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="15" class="pointDialogCol">
|
|
|
|
<div class="pointDialogColTitle">
|
|
|
|
<span class="title">测点列表</span>
|
|
|
|
<div class="pointDialogColTitleInput">
|
|
|
|
<el-input v-model="pointDialogSearchData.inputValue" clearable @keyup.enter="pointDialogSearch"></el-input>
|
|
|
|
<el-button type="primary" @click="pointDialogSearch">查询</el-button>
|
|
|
|
</div>
|
|
|
|
<div class="pointDialogColTitleRightPart">
|
|
|
|
<el-radio-group v-model="pointDialogSearchData.type" @change="pointDialogRaidoChange">
|
|
|
|
<el-radio :value="138" label="模拟量"></el-radio>
|
|
|
|
<el-radio :value="199" label="计算量"></el-radio>
|
|
|
|
</el-radio-group>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="pointDialogColContent">
|
|
|
|
<el-table ref="pointDialogTableRef" class="pointTable" :data="pointDialogTableData">
|
|
|
|
<!-- @select="pointDialogTableSelect"
|
|
|
|
@select-all="pointDialogTableSelectAll" -->
|
|
|
|
|
|
|
|
<!-- <el-table-column type="selection" width="55"></el-table-column> -->
|
|
|
|
<el-table-column label="属性名称" prop="attributeName"></el-table-column>
|
|
|
|
<el-table-column label="属性编码" prop="attributeCode"></el-table-column>
|
|
|
|
<el-table-column label="瞬时值" prop="interpolation" width="70">
|
|
|
|
<template #default="scope">
|
|
|
|
<div class="showValueType">
|
|
|
|
<el-checkbox @change="autoSelectRow(scope, 'interpolation')" v-model="scope.row.interpolation"></el-checkbox>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="平均值" prop="average" width="70">
|
|
|
|
<template #default="scope">
|
|
|
|
<div class="showValueType">
|
|
|
|
<el-checkbox @change="autoSelectRow(scope, 'average')" v-model="scope.row.average"></el-checkbox>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="最大值" prop="max" width="70">
|
|
|
|
<template #default="scope">
|
|
|
|
<div class="showValueType">
|
|
|
|
<el-checkbox @change="autoSelectRow(scope, 'max')" v-model="scope.row.max"></el-checkbox>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="最小值" prop="min" width="70">
|
|
|
|
<template #default="scope">
|
|
|
|
<div class="showValueType">
|
|
|
|
<el-checkbox @change="autoSelectRow(scope, 'min')" v-model="scope.row.min"></el-checkbox>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table>
|
|
|
|
<div class="footer">
|
|
|
|
<el-pagination
|
|
|
|
v-model:current-page="pointDialogPageSetting.current"
|
|
|
|
v-model:page-size="pointDialogPageSetting.pageSize"
|
|
|
|
:total="pointDialogPageSetting.total"
|
|
|
|
:page-sizes="pointDialogPageSetting.pageSizes"
|
|
|
|
background
|
|
|
|
:pager-count="4"
|
|
|
|
layout="prev, pager, next, jumper,sizes,total"
|
|
|
|
@change="getcurrentPageForPointDialog"
|
|
|
|
></el-pagination>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</el-col>
|
|
|
|
<el-col :span="5" class="pointDialogCol">
|
|
|
|
<div class="pointDialogColTitle">
|
|
|
|
<span>已选择测点列表</span>
|
|
|
|
<el-button @click="clearmultipleSelection">清空</el-button>
|
|
|
|
</div>
|
|
|
|
<div class="pointDialogSelectedList">
|
|
|
|
<el-scrollbar>
|
|
|
|
<div class="selectItem" v-for="(item, index) in multipleSelection" :key="item.prop">
|
|
|
|
<span>{{ item.label }}</span>
|
|
|
|
<div class="moveIcon">
|
|
|
|
<el-icon v-if="index !== 0" :size="18" @click="moveUp(index)"><Top /></el-icon>
|
|
|
|
<el-icon v-if="index !== multipleSelection.length - 1" :size="18" @click="moveDown(index)"><Bottom /></el-icon>
|
|
|
|
<el-icon v-else :size="18"></el-icon>
|
|
|
|
<el-icon :size="18" @click="moveRemove(index, item)"><Close /></el-icon>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</el-scrollbar>
|
|
|
|
</div>
|
|
|
|
</el-col>
|
|
|
|
</el-row>
|
|
|
|
<template #footer>
|
|
|
|
<div class="dialog-footer">
|
|
|
|
<el-button type="primary" @click="submitPointDialog"> 确认 </el-button>
|
2025-01-14 14:12:34 +08:00
|
|
|
<el-button @click="cancelPointDialog">取消</el-button>
|
2025-01-13 17:45:55 +08:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
|
|
import { Crop, Notebook, Delete, Top, Bottom, Close } from '@element-plus/icons-vue'
|
|
|
|
import { dayjs, ElMessageBox, ElMessage, TableInstance } from 'element-plus'
|
|
|
|
import { getAirBlowerListReq } from '/@/api/backend/airBlower/request'
|
|
|
|
import { getModelAttributeListReq, getRealValueRangeReq } from '/@/api/backend/deviceModel/request'
|
|
|
|
import { getTemplateListReq, addTemplateListReq, delTemplateListReq } from '/@/api/backend/historyData/request'
|
|
|
|
import { windowReq } from '/@/api/backend/statAnalysis/request'
|
|
|
|
import { selectData } from '/@/views/backend/historyData/type'
|
2025-01-14 09:29:31 +08:00
|
|
|
import { getCutDecimalsValue } from '/@/views/backend/equipment/airBlower/utils'
|
2025-01-13 17:45:55 +08:00
|
|
|
|
|
|
|
const shortcuts = [
|
|
|
|
{
|
|
|
|
text: '今天',
|
|
|
|
value: () => {
|
|
|
|
const start = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
const end = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
return [start, end]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: '昨天',
|
|
|
|
value: () => {
|
|
|
|
const start = dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
const end = dayjs().subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
return [start, end]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: '前3天',
|
|
|
|
value: () => {
|
|
|
|
const start = dayjs().subtract(3, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
const end = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
return [start, end]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: '本周',
|
|
|
|
value: () => {
|
|
|
|
const start = dayjs().startOf('week').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
const end = dayjs().endOf('week').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
return [start, end]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: '本月',
|
|
|
|
value: () => {
|
|
|
|
const start = dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
const end = dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
return [start, end]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
const intervalOptions = [
|
|
|
|
{ label: '一分钟', value: '1m' },
|
|
|
|
{ label: '五分钟', value: '5m' },
|
|
|
|
{ label: '十分钟', value: '10m' },
|
|
|
|
{ label: '十五分钟', value: '15m' },
|
|
|
|
{ label: '一小时', value: '1h' },
|
|
|
|
{ label: '一天', value: '1d' },
|
|
|
|
{ label: '原始', value: 'NONE' },
|
|
|
|
]
|
|
|
|
|
|
|
|
const templateOptions = ref<
|
|
|
|
{
|
|
|
|
name: string
|
|
|
|
time: string[]
|
|
|
|
id: string
|
|
|
|
interval: string
|
|
|
|
column: { label: string; prop: string }[]
|
|
|
|
windBlowerList: string[]
|
|
|
|
}[]
|
|
|
|
>([])
|
|
|
|
|
|
|
|
const changeTemplate = () => {
|
|
|
|
const templateData = templateOptions.value.find((item) => item.id === searchData.template)
|
|
|
|
if (templateData) {
|
|
|
|
searchData.interval = templateData?.interval ?? searchData.interval
|
|
|
|
searchData.time = templateData?.time ?? searchData.time
|
|
|
|
tableColumn.value = templateData?.column ? [...oririnTableColumn, ...templateData.column] : tableColumn.value
|
|
|
|
// selectWindBlower.value = templateData?.windBlowerList ?? selectWindBlower.value
|
|
|
|
submitParams.windBlowerList = templateData?.windBlowerList ?? selectWindBlower.value
|
|
|
|
submitParams.column = templateData?.column ?? multipleSelection.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const delTemplate = (id: string) => {
|
|
|
|
ElMessageBox.confirm('确定删除该模板吗?', '删除模板', {
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
type: 'warning',
|
|
|
|
}).then(() => {
|
|
|
|
delTemplateListReq({ id }).then((res) => {
|
|
|
|
if (res.code === 200) {
|
|
|
|
ElMessage.success('删除成功')
|
|
|
|
if (searchData.template === id) {
|
|
|
|
searchData.template = ''
|
|
|
|
}
|
|
|
|
getTemplateList()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const openMeasure = () => {
|
|
|
|
selectWindBlower.value = JSON.parse(JSON.stringify(submitParams.windBlowerList))
|
|
|
|
multipleSelection.value = JSON.parse(JSON.stringify(submitParams.column))
|
|
|
|
pointDialogTableData.value.forEach((item) => {
|
|
|
|
item.interpolation = false
|
|
|
|
item.average = false
|
|
|
|
item.max = false
|
|
|
|
item.min = false
|
|
|
|
})
|
|
|
|
multipleSelection.value.forEach((item) => {
|
|
|
|
const attrList = item.prop.split('--')
|
|
|
|
const type = attrList[1] as 'interpolation' | 'average' | 'max' | 'min'
|
|
|
|
const rowIndex = pointDialogTableData.value.findIndex((r) => r.attributeCode === attrList[0])!
|
|
|
|
pointDialogTableData.value[rowIndex][type] = true
|
|
|
|
})
|
|
|
|
pointDialogVisible.value = true
|
|
|
|
}
|
|
|
|
|
|
|
|
const searchLoading = ref(false)
|
|
|
|
const getData = () => {
|
|
|
|
searchLoading.value = true
|
|
|
|
getRealValueRange()
|
|
|
|
}
|
|
|
|
const getRealValueRange = () => {
|
|
|
|
const startTime = dayjs(searchData.time[0]).valueOf()
|
|
|
|
const endTime = dayjs(searchData.time[1]).valueOf()
|
|
|
|
const interval = searchData.interval
|
|
|
|
const interpolationAttr: string[] = []
|
|
|
|
const averageAttr: string[] = []
|
|
|
|
const maxAttr: string[] = []
|
|
|
|
const minAttr: string[] = []
|
|
|
|
const deviceNameData: any = {}
|
|
|
|
submitParams.windBlowerList.forEach((item: string) => {
|
|
|
|
deviceNameData[item] = windBlowerOptions.value.find((item1) => item1.value === item)?.label
|
|
|
|
})
|
|
|
|
for (let item of submitParams.column) {
|
|
|
|
const attributeCode = item.prop.split('--')
|
|
|
|
if (attributeCode[1] === 'interpolation') {
|
|
|
|
interpolationAttr.push(attributeCode[0])
|
|
|
|
}
|
|
|
|
if (attributeCode[1] === 'average') {
|
|
|
|
averageAttr.push(attributeCode[0])
|
|
|
|
}
|
|
|
|
if (attributeCode[1] === 'max') {
|
|
|
|
maxAttr.push(attributeCode[0])
|
|
|
|
}
|
|
|
|
if (attributeCode[1] === 'min') {
|
|
|
|
minAttr.push(attributeCode[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const reqList: any[] = [null, null, null, null]
|
|
|
|
if (interpolationAttr.length) {
|
|
|
|
const devices = submitParams.windBlowerList.map((item: string) => {
|
|
|
|
return {
|
|
|
|
deviceId: item,
|
|
|
|
attributes: interpolationAttr,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
reqList[0] = getRealValueRangeReq({
|
|
|
|
devices,
|
|
|
|
interval,
|
|
|
|
startTime,
|
|
|
|
endTime,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (averageAttr.length) {
|
|
|
|
const devices = submitParams.windBlowerList.map((item: string) => {
|
|
|
|
return {
|
|
|
|
deviceId: item,
|
|
|
|
attributes: averageAttr,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
reqList[1] = windowReq({
|
|
|
|
devices,
|
|
|
|
interval,
|
|
|
|
startTime,
|
|
|
|
endTime,
|
|
|
|
calFunction: 'average',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (maxAttr.length) {
|
|
|
|
const devices = submitParams.windBlowerList.map((item: string) => {
|
|
|
|
return {
|
|
|
|
deviceId: item,
|
|
|
|
attributes: maxAttr,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
reqList[2] = windowReq({
|
|
|
|
devices,
|
|
|
|
interval,
|
|
|
|
startTime,
|
|
|
|
endTime,
|
|
|
|
calFunction: 'max',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (minAttr.length) {
|
|
|
|
const devices = submitParams.windBlowerList.map((item: string) => {
|
|
|
|
return {
|
|
|
|
deviceId: item,
|
|
|
|
attributes: minAttr,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
reqList[3] = windowReq({
|
|
|
|
devices,
|
|
|
|
interval,
|
|
|
|
startTime,
|
|
|
|
endTime,
|
|
|
|
calFunction: 'min',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
const reqPromise = Promise.all(reqList)
|
|
|
|
reqPromise.then((res) => {
|
|
|
|
const [interpolationRes, averageRes, maxRes, minRes] = res
|
|
|
|
const tableDataList: any = []
|
|
|
|
if (interpolationRes && interpolationRes.code === 200) {
|
|
|
|
submitParams.windBlowerList.forEach((deviceId) => {
|
|
|
|
const device = interpolationRes.data[deviceId]
|
|
|
|
interpolationAttr.forEach((attr) => {
|
|
|
|
device[attr].times.forEach((t: number, index: number) => {
|
|
|
|
const attrNameKey = attr + '--interpolation'
|
2025-01-14 09:29:31 +08:00
|
|
|
const val = getCutDecimalsValue(device[attr].values[index], 2)
|
2025-01-13 17:45:55 +08:00
|
|
|
tableDataList.push({
|
|
|
|
name: deviceNameData[deviceId],
|
|
|
|
time: dayjs(t).format('YYYY-MM-DD HH:mm:ss'),
|
2025-01-14 09:29:31 +08:00
|
|
|
[attrNameKey]: val,
|
2025-01-13 17:45:55 +08:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (averageRes && averageRes.code === 200) {
|
|
|
|
submitParams.windBlowerList.forEach((deviceId) => {
|
|
|
|
const device = averageRes.data[deviceId]
|
|
|
|
averageAttr.forEach((attr) => {
|
|
|
|
device[attr].times.forEach((t: number, index: number) => {
|
|
|
|
const attrNameKey = attr + '--average'
|
2025-01-14 09:29:31 +08:00
|
|
|
const val = getCutDecimalsValue(device[attr].values[index], 2)
|
2025-01-13 17:45:55 +08:00
|
|
|
const tableDataIndex = tableDataList.findIndex(
|
|
|
|
(data: any) => data.name == deviceNameData[deviceId] && data.time == dayjs(t).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
)
|
2025-01-14 09:29:31 +08:00
|
|
|
tableDataList[tableDataIndex][attrNameKey] = val
|
2025-01-13 17:45:55 +08:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (maxRes && maxRes.code === 200) {
|
|
|
|
submitParams.windBlowerList.forEach((deviceId) => {
|
|
|
|
const device = maxRes.data[deviceId]
|
|
|
|
maxAttr.forEach((attr) => {
|
|
|
|
device[attr].times.forEach((t: number, index: number) => {
|
|
|
|
const attrNameKey = attr + '--max'
|
2025-01-14 09:29:31 +08:00
|
|
|
const val = getCutDecimalsValue(device[attr].values[index], 2)
|
2025-01-13 17:45:55 +08:00
|
|
|
const tableDataIndex = tableDataList.findIndex(
|
|
|
|
(data: any) => data.name == deviceNameData[deviceId] && data.time == dayjs(t).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
)
|
2025-01-14 09:29:31 +08:00
|
|
|
tableDataList[tableDataIndex][attrNameKey] = val
|
2025-01-13 17:45:55 +08:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (minRes && minRes.code === 200) {
|
|
|
|
submitParams.windBlowerList.forEach((deviceId) => {
|
|
|
|
const device = minRes.data[deviceId]
|
|
|
|
minAttr.forEach((attr) => {
|
|
|
|
device[attr].times.forEach((t: number, index: number) => {
|
|
|
|
const attrNameKey = attr + '--min'
|
2025-01-14 09:29:31 +08:00
|
|
|
const val = getCutDecimalsValue(device[attr].values[index], 2)
|
2025-01-13 17:45:55 +08:00
|
|
|
const tableDataIndex = tableDataList.findIndex(
|
|
|
|
(data: any) => data.name == deviceNameData[deviceId] && data.time == dayjs(t).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
)
|
2025-01-14 09:29:31 +08:00
|
|
|
tableDataList[tableDataIndex][attrNameKey] = val
|
2025-01-13 17:45:55 +08:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
allTableData = tableDataList
|
|
|
|
pageSetting.total = tableDataList.length
|
|
|
|
pageSetting.current = 1
|
|
|
|
triggerTableRefresh.value = !triggerTableRefresh.value
|
|
|
|
searchLoading.value = false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const addTemplate = () => {
|
|
|
|
ElMessageBox.prompt('请输入模板名称', '添加模板', {
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
})
|
|
|
|
.then(({ value }) => {
|
|
|
|
const template = {
|
|
|
|
name: value,
|
|
|
|
time: searchData.time,
|
|
|
|
interval: searchData.interval,
|
|
|
|
column: submitParams.column,
|
|
|
|
windBlowerList: submitParams.windBlowerList,
|
|
|
|
}
|
|
|
|
addTemplateListReq({
|
|
|
|
category: '历史数据',
|
|
|
|
template: JSON.stringify(template),
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
ElMessage.success('添加成功')
|
|
|
|
getTemplateList()
|
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
ElMessage.error('添加失败')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(() => {})
|
|
|
|
}
|
|
|
|
|
|
|
|
const searchData = reactive({
|
|
|
|
time: shortcuts[0].value(),
|
|
|
|
interval: '5m',
|
|
|
|
template: '',
|
|
|
|
})
|
|
|
|
|
|
|
|
const getTemplateList = () => {
|
|
|
|
getTemplateListReq({
|
|
|
|
category: '历史数据',
|
|
|
|
pageNum: 1,
|
|
|
|
pageSize: 100,
|
|
|
|
}).then((res) => {
|
|
|
|
if (res.code === 200) {
|
|
|
|
const templateData = res.data.rows.map((item) => {
|
|
|
|
const templateData = JSON.parse(item.template)
|
|
|
|
return {
|
|
|
|
id: item.id,
|
|
|
|
name: templateData.name,
|
|
|
|
time: templateData.time,
|
|
|
|
interval: templateData.interval,
|
|
|
|
column: templateData.column,
|
|
|
|
windBlowerList: templateData.windBlowerList,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
templateOptions.value = templateData
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
getTemplateList()
|
|
|
|
|
|
|
|
const oririnTableColumn = [
|
|
|
|
{
|
|
|
|
label: '风机名称',
|
|
|
|
prop: 'name',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: '时间',
|
|
|
|
prop: 'time',
|
|
|
|
},
|
|
|
|
]
|
|
|
|
const tableColumn = ref(oririnTableColumn)
|
|
|
|
|
|
|
|
let allTableData: any = []
|
|
|
|
const triggerTableRefresh = ref(false)
|
|
|
|
const tableData = computed(() => {
|
|
|
|
triggerTableRefresh.value
|
|
|
|
return allTableData.slice((pageSetting.current - 1) * pageSetting.pageSize, pageSetting.current * pageSetting.pageSize)
|
|
|
|
})
|
|
|
|
|
|
|
|
const pageSetting = reactive({
|
|
|
|
current: 1,
|
|
|
|
pageSize: 20,
|
|
|
|
total: 0,
|
|
|
|
pageSizes: [20, 50, 100],
|
|
|
|
})
|
|
|
|
const getcurrentPage = () => {}
|
|
|
|
|
|
|
|
const pointDialogVisible = ref(false)
|
|
|
|
|
|
|
|
const selectWindBlower = ref<string[]>([])
|
|
|
|
const windBlowerOptions = ref<{ label: string; value: string }[]>([])
|
|
|
|
|
|
|
|
const getAirBlowerList = () => {
|
|
|
|
return getAirBlowerListReq().then((res) => {
|
|
|
|
if (res.success) {
|
|
|
|
const data = res.data.map((item) => {
|
|
|
|
return {
|
|
|
|
label: item.name,
|
|
|
|
value: item.irn,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
pointDialogSearchData.modelId = res.data?.[0].modelId ?? ''
|
|
|
|
windBlowerOptions.value = data
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
getAirBlowerList().then(() => {
|
|
|
|
getModelAttributeList()
|
|
|
|
})
|
|
|
|
const pointDialogSearchData = reactive<{
|
|
|
|
inputValue: string
|
|
|
|
type: 138 | 199
|
|
|
|
modelId: string
|
|
|
|
}>({
|
|
|
|
inputValue: '',
|
|
|
|
type: 138,
|
|
|
|
modelId: '',
|
|
|
|
})
|
|
|
|
|
|
|
|
const pointDialogSearch = () => {
|
|
|
|
getModelAttributeList()
|
|
|
|
}
|
|
|
|
|
|
|
|
const pointDialogRaidoChange = () => {
|
|
|
|
if (!pointDialogVisible.value) return
|
|
|
|
getModelAttributeList()
|
|
|
|
}
|
|
|
|
|
|
|
|
const pointDialogTableRef = ref<TableInstance>()
|
|
|
|
const pointDialogTableData = ref<selectData[]>([])
|
|
|
|
|
|
|
|
const selectRowList = ref<selectData[]>([])
|
|
|
|
const autoSelectRow = (scope: any, type: 'interpolation' | 'average' | 'max' | 'min') => {
|
|
|
|
const state = scope.row[type]
|
|
|
|
if (state) {
|
|
|
|
const exist = selectRowList.value.find((item) => item.id === scope.row.id)
|
|
|
|
if (!exist) {
|
|
|
|
selectRowList.value.push(scope.row)
|
|
|
|
} else {
|
|
|
|
exist[type] = state
|
|
|
|
}
|
|
|
|
getAddColumn()
|
|
|
|
} else {
|
|
|
|
const index = multipleSelection.value.findIndex((item) => {
|
|
|
|
const attrList = item.prop.split('--')
|
|
|
|
return attrList[0] === scope.row.attributeCode && attrList[1] === type
|
|
|
|
})
|
|
|
|
if (index > -1) {
|
|
|
|
multipleSelection.value.splice(index, 1)
|
|
|
|
}
|
|
|
|
if (!scope.row.interpolation && !scope.row.average && !scope.row.max && !scope.row.min) {
|
|
|
|
const index = selectRowList.value.findIndex((item) => item.id === scope.row.id)
|
|
|
|
if (index > -1) {
|
|
|
|
selectRowList.value.splice(index, 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const pointDialogPageSetting = reactive({
|
|
|
|
current: 1,
|
|
|
|
pageSize: 20,
|
|
|
|
total: 0,
|
|
|
|
pageSizes: [20, 50, 100],
|
|
|
|
})
|
|
|
|
|
|
|
|
const getModelAttributeList = () => {
|
|
|
|
const reqParams = {
|
|
|
|
iotModelId: pointDialogSearchData.modelId,
|
|
|
|
attributeName: pointDialogSearchData.inputValue,
|
|
|
|
attributeType: pointDialogSearchData.type,
|
|
|
|
pageNum: pointDialogPageSetting.current,
|
|
|
|
pageSize: pointDialogPageSetting.pageSize,
|
|
|
|
}
|
|
|
|
return getModelAttributeListReq(reqParams).then((res) => {
|
|
|
|
if (res.code == 200) {
|
|
|
|
const data = res.rows.map((item) => {
|
|
|
|
const compareData = {
|
|
|
|
interpolation: false,
|
|
|
|
average: false,
|
|
|
|
max: false,
|
|
|
|
min: false,
|
|
|
|
}
|
|
|
|
if (pointDialogVisible.value) {
|
|
|
|
const selectedInfo = selectRowList.value.find((select) => select.id == item.id)
|
|
|
|
if (selectedInfo) {
|
|
|
|
compareData.interpolation = selectedInfo.interpolation
|
|
|
|
compareData.average = selectedInfo.average
|
|
|
|
compareData.max = selectedInfo.max
|
|
|
|
compareData.min = selectedInfo.min
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
id: item.id!,
|
|
|
|
attributeName: item.attributeName,
|
|
|
|
attributeCode: item.attributeCode,
|
|
|
|
...compareData,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
pointDialogTableData.value = data
|
|
|
|
pointDialogPageSetting.total = res.total
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const getcurrentPageForPointDialog = () => {
|
|
|
|
if (!pointDialogVisible.value) return
|
|
|
|
getModelAttributeList()
|
|
|
|
}
|
|
|
|
|
|
|
|
const getAddColumn = () => {
|
|
|
|
const dynamicColumn: { label: string; prop: string }[] = []
|
|
|
|
for (let item of selectRowList.value) {
|
|
|
|
if (item.interpolation) {
|
|
|
|
dynamicColumn.push({
|
|
|
|
label: item.attributeName,
|
|
|
|
prop: item.attributeCode + '--interpolation',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (item.average) {
|
|
|
|
dynamicColumn.push({
|
|
|
|
label: item.attributeName + '-平均值',
|
|
|
|
prop: item.attributeCode + '--average',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (item.max) {
|
|
|
|
dynamicColumn.push({
|
|
|
|
label: item.attributeName + '-最大值',
|
|
|
|
prop: item.attributeCode + '--max',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (item.min) {
|
|
|
|
dynamicColumn.push({
|
|
|
|
label: item.attributeName + '-最小值',
|
|
|
|
prop: item.attributeCode + '--min',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
multipleSelection.value = dynamicColumn
|
|
|
|
}
|
|
|
|
|
|
|
|
const submitParams: {
|
|
|
|
column: { label: string; prop: string }[]
|
|
|
|
windBlowerList: string[]
|
|
|
|
} = {
|
|
|
|
column: [],
|
|
|
|
windBlowerList: [],
|
|
|
|
}
|
|
|
|
|
|
|
|
const multipleSelection = ref<{ label: string; prop: string }[]>([])
|
|
|
|
const moveUp = (index: number) => {
|
|
|
|
const temp = multipleSelection.value[index]
|
|
|
|
multipleSelection.value[index] = multipleSelection.value[index - 1]
|
|
|
|
multipleSelection.value[index - 1] = temp
|
|
|
|
}
|
|
|
|
const moveDown = (index: number) => {
|
|
|
|
const temp = multipleSelection.value[index]
|
|
|
|
multipleSelection.value[index] = multipleSelection.value[index + 1]
|
|
|
|
multipleSelection.value[index + 1] = temp
|
|
|
|
}
|
|
|
|
const moveRemove = (index: number, item: { label: string; prop: string }) => {
|
|
|
|
multipleSelection.value.splice(index, 1)
|
|
|
|
const realProp = item.prop.split('--')
|
|
|
|
const type = realProp[1] as 'interpolation' | 'average' | 'max' | 'min'
|
|
|
|
const selectRowIndex = selectRowList.value.findIndex((row) => row.attributeCode === realProp[0])!
|
2025-01-14 14:12:34 +08:00
|
|
|
if(selectRowIndex > -1){
|
|
|
|
selectRowList.value[selectRowIndex][type] = false
|
|
|
|
}
|
2025-01-13 17:45:55 +08:00
|
|
|
if (
|
2025-01-14 14:12:34 +08:00
|
|
|
!selectRowList.value[selectRowIndex]?.interpolation &&
|
|
|
|
!selectRowList.value[selectRowIndex]?.average &&
|
|
|
|
!selectRowList.value[selectRowIndex]?.max &&
|
|
|
|
!selectRowList.value[selectRowIndex]?.min
|
2025-01-13 17:45:55 +08:00
|
|
|
) {
|
|
|
|
selectRowList.value.splice(selectRowIndex, 1)
|
|
|
|
}
|
|
|
|
const rowIndex = pointDialogTableData.value.findIndex((row) => row.attributeCode === realProp[0])!
|
|
|
|
if (rowIndex > -1) {
|
|
|
|
pointDialogTableData.value[rowIndex][type] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const clearmultipleSelection = () => {
|
|
|
|
multipleSelection.value = []
|
|
|
|
pointDialogTableRef.value?.clearSelection()
|
|
|
|
selectRowList.value.forEach((item) => {
|
|
|
|
const row = pointDialogTableData.value.find((row) => row.id === item.id)
|
|
|
|
if (row) {
|
|
|
|
row.interpolation = false
|
|
|
|
row.average = false
|
|
|
|
row.max = false
|
|
|
|
row.min = false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
selectRowList.value = []
|
|
|
|
}
|
|
|
|
|
|
|
|
const submitPointDialog = () => {
|
|
|
|
if (!selectWindBlower.value.length) {
|
|
|
|
ElMessage.error('请选择风机')
|
|
|
|
return
|
|
|
|
}
|
2025-01-14 14:12:34 +08:00
|
|
|
if (!multipleSelection.value.length) {
|
2025-01-13 17:45:55 +08:00
|
|
|
ElMessage.error('请选择测点')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
submitParams.windBlowerList = JSON.parse(JSON.stringify(selectWindBlower.value))
|
|
|
|
submitParams.column = JSON.parse(JSON.stringify(multipleSelection.value))
|
|
|
|
tableColumn.value = [...oririnTableColumn, ...submitParams.column]
|
|
|
|
pointDialogVisible.value = false
|
|
|
|
}
|
2025-01-14 14:12:34 +08:00
|
|
|
const cancelPointDialog = () => {
|
|
|
|
pointDialogVisible.value = false
|
|
|
|
}
|
2025-01-13 17:45:55 +08:00
|
|
|
const exportExcel = () => {
|
|
|
|
const title = tableColumn.value.map((item: any) => item.label).join('\t,') + '\n'
|
|
|
|
const titleKeyArr = tableColumn.value.map((item: any) => item.prop)
|
|
|
|
let str = ''
|
|
|
|
allTableData.forEach((item: any) => {
|
|
|
|
titleKeyArr.forEach((prop: any) => {
|
|
|
|
const val = typeof item[prop] === 'string' ? item[prop] : item[prop] ? String(item[prop]) : ''
|
|
|
|
str += val + '\t' + ','
|
|
|
|
})
|
|
|
|
str += '\n'
|
|
|
|
})
|
|
|
|
str = title + str
|
|
|
|
let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str)
|
|
|
|
let link = document.createElement('a')
|
|
|
|
link.href = uri
|
|
|
|
link.download = '历史数据' + searchData.time[0] + '-' + searchData.time[1] + '.csv'
|
|
|
|
document.body.appendChild(link)
|
|
|
|
link.click()
|
|
|
|
document.body.removeChild(link)
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
.historyData {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
.container {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
.header {
|
|
|
|
height: 60px;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
.timePart {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
width: 450px;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
.label {
|
|
|
|
margin: 0 10px;
|
|
|
|
}
|
|
|
|
.timeSelect {
|
|
|
|
width: 400px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.intervalPart {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
width: 250px;
|
|
|
|
height: 100%;
|
|
|
|
.label {
|
|
|
|
margin: 0 10px;
|
|
|
|
}
|
|
|
|
.el-select {
|
|
|
|
width: 200px;
|
|
|
|
}
|
|
|
|
:deep(.el-select__wrapper) {
|
|
|
|
width: 200px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.templatePart {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
width: 250px;
|
|
|
|
height: 100%;
|
|
|
|
.label {
|
|
|
|
margin: 0 10px;
|
|
|
|
}
|
|
|
|
.el-select {
|
|
|
|
width: 200px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
:deep(.el-select__wrapper) {
|
|
|
|
width: 200px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.pointSelect {
|
|
|
|
margin: 0 10px;
|
|
|
|
width: 100px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
.opeareBtn {
|
|
|
|
margin-left: auto;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
width: 350px;
|
|
|
|
height: 100%;
|
|
|
|
.el-button {
|
|
|
|
margin: 0 10px;
|
|
|
|
width: 100px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.main {
|
|
|
|
height: calc(100% - 100px);
|
|
|
|
}
|
|
|
|
.footer {
|
|
|
|
display: flex;
|
|
|
|
justify-content: right;
|
|
|
|
align-items: center;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.pointDialogRow {
|
|
|
|
width: 100%;
|
|
|
|
.pointDialogCol {
|
|
|
|
width: 100%;
|
|
|
|
height: 600px;
|
|
|
|
.pointDialogColTitle {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
padding: 0 10px;
|
|
|
|
width: 100%;
|
|
|
|
height: 40px;
|
|
|
|
background-color: #f7f9fc;
|
|
|
|
border-radius: 6px 6px 0 0;
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
border-bottom: none;
|
|
|
|
.title {
|
|
|
|
margin: 0 10px;
|
|
|
|
}
|
|
|
|
.pointDialogColTitleInput {
|
|
|
|
.el-input {
|
|
|
|
width: 200px;
|
|
|
|
}
|
|
|
|
.el-button {
|
|
|
|
margin-left: 10px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.pointDialogColTitleRightPart {
|
|
|
|
margin-left: auto;
|
|
|
|
width: 170px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.pointDialogColContent {
|
|
|
|
width: 100%;
|
|
|
|
height: 560px;
|
|
|
|
padding: 0 10px;
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
border-top: none;
|
|
|
|
border-radius: 0 0 6px 6px;
|
|
|
|
.windBlowerCheckbox {
|
|
|
|
margin: 0;
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
.pointTable {
|
|
|
|
width: 100%;
|
|
|
|
height: 520px;
|
|
|
|
}
|
|
|
|
.footer {
|
|
|
|
display: flex;
|
|
|
|
justify-content: right;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.pointDialogSelectedList {
|
|
|
|
width: 100%;
|
|
|
|
height: 560px;
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
border-top: none;
|
|
|
|
border-radius: 0 0 6px 6px;
|
|
|
|
.selectItem {
|
|
|
|
margin: 5px;
|
|
|
|
padding-left: 10px;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
min-height: 40px;
|
|
|
|
border-radius: 4px;
|
|
|
|
background-color: #eff0f1;
|
|
|
|
.moveIcon {
|
|
|
|
display: flex;
|
|
|
|
justify-content: right;
|
|
|
|
align-items: center;
|
|
|
|
width: 60px;
|
|
|
|
.el-icon {
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-01-14 14:12:34 +08:00
|
|
|
|
2025-01-13 17:45:55 +08:00
|
|
|
}
|
2025-01-14 09:42:58 +08:00
|
|
|
.templateOption {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: space-between;
|
|
|
|
}
|
2025-01-14 14:12:34 +08:00
|
|
|
|
2025-01-13 17:45:55 +08:00
|
|
|
</style>
|