1100 lines
38 KiB
Vue
1100 lines
38 KiB
Vue
<template>
|
||
<div class="malfunction">
|
||
<el-container class="container">
|
||
<el-aside class="aside">
|
||
<div class="searchTree">
|
||
<el-input v-model="searchTreeValue" clearable placeholder="搜索" :suffix-icon="Search" @change="searchTree"> </el-input>
|
||
</div>
|
||
<div class="treeMain">
|
||
<el-scrollbar>
|
||
<el-tree
|
||
ref="treeRef"
|
||
highlight-current
|
||
:data="treeData"
|
||
:props="defaultProps"
|
||
node-key="id"
|
||
:default-expanded-keys="defaultExpandKeys"
|
||
@node-click="handleNodeClick"
|
||
@node-contextmenu="rightClick"
|
||
></el-tree>
|
||
</el-scrollbar>
|
||
</div>
|
||
</el-aside>
|
||
<el-container class="mainContainer">
|
||
<el-header class="header">
|
||
<div class="searchFileName">
|
||
<span>文件名称</span>
|
||
<el-input v-model="searchData.fileName"></el-input>
|
||
</div>
|
||
<div class="searchDate">
|
||
<span>日期</span>
|
||
<el-date-picker
|
||
v-model="searchData.date"
|
||
type="daterange"
|
||
unlink-panels
|
||
start-placeholder="开始时间"
|
||
end-placeholder="结束时间"
|
||
format="YYYY-MM-DD"
|
||
date-format="YYYY/MM/DD"
|
||
:shortcuts="shortcuts"
|
||
@change="getListForAirBlower"
|
||
></el-date-picker>
|
||
</div>
|
||
<div class="btnPart">
|
||
<!-- <el-button type="primary" v-show="curTreeData.id !== '0'" :icon="Setting" @click="openConfigDialog">配置</el-button> -->
|
||
<!--
|
||
<el-button type="primary" :icon="Download">下载</el-button>
|
||
<el-button type="primary" plain :icon="Delete">删除</el-button>
|
||
-->
|
||
</div>
|
||
</el-header>
|
||
<el-main class="main">
|
||
<el-tabs v-model="activeName" @tab-change="checkTab" class="tabs">
|
||
<el-tab-pane label="故障录波文件" name="malFunction" class="tabPane">
|
||
<div class="tableMain">
|
||
<el-table :data="tableData" class="tableClass">
|
||
<el-table-column type="selection" width="55" />
|
||
<el-table-column label="文件名称" prop="name" align="center"></el-table-column>
|
||
<el-table-column label="修改时间" prop="lastModified" align="center" width="250"></el-table-column>
|
||
<el-table-column label="文件大小" prop="size" align="center" width="120"></el-table-column>
|
||
<el-table-column label="操作" align="center" width="140">
|
||
<template #default="scope">
|
||
<div class="tableBtnPart">
|
||
<el-button text type="primary" @click="readFile(scope.row)">查看</el-button>
|
||
<el-button text type="danger" @click="downloadFile(scope.row)">下载</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<div class="footerPart">
|
||
<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="total,prev, pager, next, jumper,sizes"
|
||
@change="changePageSetting"
|
||
></el-pagination>
|
||
</div>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="运行日志文件" name="logManage" class="tabPane">
|
||
<div class="tableMain">
|
||
<el-table :data="logTableData" class="tableClass">
|
||
<el-table-column type="selection" width="55" />
|
||
<el-table-column label="文件名称" prop="name" align="center"></el-table-column>
|
||
<el-table-column label="修改时间" prop="lastModified" align="center" width="250"></el-table-column>
|
||
<el-table-column label="文件大小" prop="size" align="center" width="120"></el-table-column>
|
||
<el-table-column label="操作" align="center" width="140">
|
||
<template #default="scope">
|
||
<div class="tableBtnPart">
|
||
<el-button text type="primary" @click="readFile(scope.row)">查看</el-button>
|
||
<el-button text type="danger" @click="downloadFile(scope.row)">下载</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<div class="footerPart">
|
||
<el-pagination
|
||
v-model:current-page="logPageSetting.current"
|
||
v-model:page-size="logPageSetting.pageSize"
|
||
:total="logPageSetting.total"
|
||
:page-sizes="logPageSetting.pageSizes"
|
||
background
|
||
:pager-count="7"
|
||
layout="total,prev, pager, next, jumper,sizes"
|
||
@change="changePageSetting"
|
||
></el-pagination>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-main>
|
||
</el-container>
|
||
</el-container>
|
||
<el-dialog v-model="configDialogVisible" title="配置信息" width="500" @close="closeConfigDialog">
|
||
<el-form ref="configFormRef" :model="configFormData" label-width="120" :rules="configFormRules">
|
||
<el-form-item prop="timeFormat" label="时间格式">
|
||
<el-input v-model="configFormData.timeFormat"></el-input>
|
||
</el-form-item>
|
||
<el-form-item prop="delimiter" label="分隔符">
|
||
<el-input v-model="configFormData.delimiter"></el-input>
|
||
</el-form-item>
|
||
<el-form-item prop="validStartLine" label="有效起始行数">
|
||
<el-input-number v-model="configFormData.validStartLine"></el-input-number>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button type="primary" @click="submitConfig">保存</el-button>
|
||
<el-button @click="cancelConfig">取消</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog v-model="previewFileDialogVisible" title="数据分析" width="1000" @close="closePreviewFileDialog">
|
||
<el-row class="previewFilePart">
|
||
<el-col :span="8" class="colPart leftCol" v-loading="previewTreeLoading">
|
||
<div class="search">
|
||
<el-input
|
||
v-model="previewSearchTreeVal"
|
||
@change="previewSearchTree"
|
||
class="previewSearchTree"
|
||
:suffix-icon="Search"
|
||
clearable
|
||
></el-input>
|
||
<el-button @click="clearPreviewTree">清空</el-button>
|
||
</div>
|
||
<el-scrollbar class="previewTreeScrollbar">
|
||
<el-tree
|
||
ref="previewTreeRef"
|
||
:data="previewTreeData"
|
||
:props="previewTreeProps"
|
||
show-checkbox
|
||
node-key="key"
|
||
@check="handleCheckChange"
|
||
></el-tree>
|
||
<!--
|
||
@node-click="previewNodeClick"
|
||
-->
|
||
</el-scrollbar>
|
||
</el-col>
|
||
<el-col :span="16" class="colPart">
|
||
<div class="title">{{ fileName }}</div>
|
||
<div class="chartPart">
|
||
<div ref="previewChartRef" class="previewChart"></div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</el-dialog>
|
||
<ContextMenu v-model:visible="contextMenuInfo.visible" :pos="contextMenuInfo.pos">
|
||
<template #default>
|
||
<div class="contextMenuBtns">
|
||
<el-button size="small" @click="openConfigDialog('malFunction')">故障录波配置</el-button>
|
||
<el-button size="small" @click="openConfigDialog('logManage')">运行日志配置</el-button>
|
||
</div>
|
||
</template>
|
||
</ContextMenu>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, computed, onMounted, nextTick, onUnmounted } from 'vue'
|
||
import { dayjs, ElMessage, FormInstance, TreeInstance } from 'element-plus'
|
||
import { Search, Setting } from '@element-plus/icons-vue'
|
||
import { getMalFunctionListReq, setConfigReq, previewFileReq, downloadFileReq, getFileKeyEnumsReq } from '/@/api/backend/malfunction/request'
|
||
import {
|
||
getLogRecordListReq,
|
||
setConfigReq as setLogConfigReq,
|
||
previewFileReq as previewLogFileReq,
|
||
downloadFileReq as downloadLogFileReq,
|
||
getFileKeyEnumsReq as getLogFileKeyEnumsReq,
|
||
} from '/@/api/backend/logRecord/request'
|
||
import { equipList } from '/@/api/backend/temperature/request'
|
||
import * as echarts from 'echarts'
|
||
import { tableItemData } from './type'
|
||
import ContextMenu from '/@/views/backend/auth/model/contextMenu.vue'
|
||
import { useRouter, useRoute } from 'vue-router'
|
||
|
||
const route = useRoute()
|
||
|
||
const defaultProps = {
|
||
children: 'children',
|
||
label: 'label',
|
||
}
|
||
|
||
const contextMenuInfo = reactive({
|
||
visible: false,
|
||
pos: {
|
||
x: 0,
|
||
y: 0,
|
||
},
|
||
})
|
||
|
||
const treeRef = ref()
|
||
let originTreeChildData: {
|
||
label: string
|
||
id: string
|
||
code: string
|
||
children?: any[]
|
||
}[] = []
|
||
const treeData = ref<
|
||
{
|
||
label: string
|
||
id: string
|
||
code: string
|
||
options: any
|
||
children?: any[]
|
||
}[]
|
||
>([
|
||
{
|
||
label: '风机列表',
|
||
id: '0',
|
||
code: '0',
|
||
options: {},
|
||
children: [],
|
||
},
|
||
])
|
||
const defaultExpandKeys: string[] = ['0']
|
||
|
||
const searchTreeValue = ref('')
|
||
const searchTree = (val: string) => {
|
||
if (val == '') {
|
||
treeData.value[0].children = originTreeChildData
|
||
return
|
||
}
|
||
const reg = new RegExp(val, 'i')
|
||
treeData.value[0].children =
|
||
originTreeChildData.filter((item: any) => {
|
||
return reg.test(item.label)
|
||
}) ?? []
|
||
}
|
||
|
||
const curTreeData = ref<{
|
||
label: string
|
||
id: string
|
||
code: string
|
||
model: string
|
||
madeinFactory: string
|
||
options: any
|
||
children?: any[]
|
||
}>({
|
||
label: '风机列表',
|
||
id: '0',
|
||
code: '0',
|
||
model: '',
|
||
madeinFactory: '',
|
||
options: {},
|
||
})
|
||
|
||
const handleNodeClick = (target: { label: string; id: string; code: string; model: string; madeinFactory: string; options: any }) => {
|
||
curTreeData.value = target
|
||
if (target.id === '0') return
|
||
getListForAirBlower()
|
||
}
|
||
|
||
const rightClickData = ref()
|
||
const rightClick = (
|
||
event: any,
|
||
data: {
|
||
label: string
|
||
id: string
|
||
code: string
|
||
model: string
|
||
madeinFactory: string
|
||
options: any
|
||
children?: any[]
|
||
}
|
||
) => {
|
||
rightClickData.value = data
|
||
contextMenuInfo.visible = true
|
||
contextMenuInfo.pos = {
|
||
x: event.clientX,
|
||
y: event.clientY,
|
||
}
|
||
}
|
||
|
||
const getTreeDataList = () => {
|
||
return new Promise((resolve) => {
|
||
equipList({ objectType: 10002 }).then((res) => {
|
||
const data = res.data.map((item: any) => {
|
||
return {
|
||
label: item.name,
|
||
code: item.code,
|
||
id: item.id,
|
||
model: item.model,
|
||
madeinFactory: item.madeinFactory,
|
||
options: item.options ? JSON.parse(item.options) : {},
|
||
}
|
||
})
|
||
originTreeChildData = data
|
||
treeData.value[0].children = data
|
||
resolve(data[0])
|
||
})
|
||
})
|
||
}
|
||
const initDatestart = route.query.eventTime ? dayjs(Number(route.query.eventTime)) : dayjs().startOf('month')
|
||
const initDateend = route.query.eventTime ? dayjs(Number(route.query.eventTime)) : dayjs()
|
||
|
||
const searchData = reactive<{ fileName: ''; date: Date[] }>({
|
||
fileName: '',
|
||
date: [initDatestart.toDate(), initDateend.toDate()],
|
||
})
|
||
|
||
const shortcuts = [
|
||
{
|
||
text: '本月',
|
||
value: () => {
|
||
const start = dayjs().startOf('month').toDate()
|
||
const end = dayjs().endOf('month').toDate()
|
||
return [start, end]
|
||
},
|
||
},
|
||
{
|
||
text: '上月',
|
||
value: () => {
|
||
const start = dayjs().subtract(1, 'month').startOf('month').toDate()
|
||
const end = dayjs().subtract(1, 'month').endOf('month').toDate()
|
||
return [start, end]
|
||
},
|
||
},
|
||
{
|
||
text: '近三月',
|
||
value: () => {
|
||
const start = dayjs().subtract(2, 'month').startOf('month').toDate()
|
||
const end = dayjs().toDate()
|
||
return [start, end]
|
||
},
|
||
},
|
||
]
|
||
|
||
let originTableData = ref<tableItemData[]>([])
|
||
const tableData = computed(() => {
|
||
let data = originTableData.value
|
||
if (searchData.fileName) {
|
||
const reg = new RegExp(searchData.fileName, 'i')
|
||
data = data.filter((item) => reg.test(item.name))
|
||
}
|
||
const res = data.slice((pageSetting.current - 1) * pageSetting.pageSize, pageSetting.current * pageSetting.pageSize)
|
||
pageSetting.total = res.length
|
||
return res
|
||
})
|
||
const pageSetting = reactive({
|
||
current: 1,
|
||
pageSize: 20,
|
||
total: 0,
|
||
pageSizes: [20, 50, 100],
|
||
})
|
||
|
||
const changePageSetting = () => {}
|
||
|
||
const getListForAirBlower = () => {
|
||
const data = {
|
||
deviceCode: curTreeData.value.code,
|
||
startTime: dayjs(searchData.date[0]).format('YYYY-MM-DD'),
|
||
endTime: dayjs(searchData.date[1]).format('YYYY-MM-DD'),
|
||
}
|
||
if (activeName.value === 'malFunction') {
|
||
getMalFunctionListReq(data).then((res) => {
|
||
if (res.success) {
|
||
pageSetting.total = res.data.length
|
||
originTableData.value = res.data
|
||
}
|
||
})
|
||
} else if (activeName.value === 'logManage') {
|
||
getLogRecordListReq(data).then((res) => {
|
||
if (res.success) {
|
||
logPageSetting.total = res.data.length
|
||
logOriginTableData.value = res.data
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
const configDialogVisible = ref(false)
|
||
|
||
const configFormRef = ref<FormInstance>()
|
||
const closeConfigDialog = () => {
|
||
configFormRef.value?.resetFields()
|
||
}
|
||
|
||
const configFormData = reactive({
|
||
timeFormat: '',
|
||
delimiter: '',
|
||
validStartLine: 4,
|
||
})
|
||
const defaultConfigData = {
|
||
plcTimeFormat: 'dd.MM.yyyy HH:mm:ss,SSS',
|
||
plcDelimiter: '\t',
|
||
plcValidStartLine: 12,
|
||
fdrTimeFormat: 'yyyy-MM-dd_HH-mm-ss.SSS',
|
||
fdrDelimiter: ';',
|
||
fdrValidStartLine: 7,
|
||
}
|
||
|
||
const setConfigType = ref<'malFunction' | 'logManage'>('malFunction')
|
||
const openConfigDialog = (type: 'malFunction' | 'logManage') => {
|
||
configDialogVisible.value = true
|
||
setConfigType.value = type
|
||
if (type === 'malFunction') {
|
||
configFormData.delimiter = rightClickData.value.options?.fdrFormat?.delimiter ?? defaultConfigData.fdrDelimiter
|
||
configFormData.timeFormat = rightClickData.value.options?.fdrFormat?.timeFormat ?? defaultConfigData.fdrTimeFormat
|
||
configFormData.validStartLine = rightClickData.value.options?.fdrFormat?.validStartLine ?? defaultConfigData.fdrValidStartLine
|
||
} else if (type === 'logManage') {
|
||
configFormData.delimiter = rightClickData.value.options?.plcFormat?.delimiter ?? defaultConfigData.plcDelimiter
|
||
configFormData.timeFormat = rightClickData.value.options?.plcFormat?.timeFormat ?? defaultConfigData.plcTimeFormat
|
||
configFormData.validStartLine = rightClickData.value.options?.plcFormat?.validStartLine ?? defaultConfigData.plcValidStartLine
|
||
}
|
||
}
|
||
|
||
const configFormRules = {
|
||
timeFormat: [
|
||
{
|
||
required: true,
|
||
message: '请输入时间格式',
|
||
trigger: 'blur',
|
||
},
|
||
],
|
||
delimiter: [
|
||
{
|
||
required: true,
|
||
message: '请输入分隔符',
|
||
trigger: 'blur',
|
||
},
|
||
],
|
||
validStartLine: [
|
||
{
|
||
required: true,
|
||
message: '请输入有效起始行',
|
||
trigger: 'change',
|
||
},
|
||
],
|
||
}
|
||
|
||
const submitConfig = () => {
|
||
configFormRef.value?.validate((valid) => {
|
||
if (valid) {
|
||
setAirBlowerConfig()
|
||
.then(() => {
|
||
ElMessage.success('配置成功')
|
||
configDialogVisible.value = false
|
||
})
|
||
.then(() => {
|
||
return getTreeDataList()
|
||
})
|
||
.then(() => {
|
||
nextTick(() => {
|
||
treeRef.value?.setCurrentKey(curTreeData.value.id, false)
|
||
curTreeData.value = treeRef.value?.getCurrentNode()
|
||
})
|
||
})
|
||
.catch((err) => {
|
||
ElMessage.error(err)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
const cancelConfig = () => {
|
||
configDialogVisible.value = false
|
||
configFormRef.value?.resetFields()
|
||
}
|
||
|
||
const setAirBlowerConfig = () => {
|
||
return new Promise((resolve) => {
|
||
if (setConfigType.value === 'malFunction') {
|
||
const stringify = JSON.stringify({ fdrFormat: configFormData })
|
||
setConfigReq({
|
||
id: rightClickData.value.id,
|
||
options: stringify,
|
||
})
|
||
.then(() => {
|
||
resolve(true)
|
||
})
|
||
.catch(() => {
|
||
throw '配置失败'
|
||
})
|
||
} else if (setConfigType.value === 'logManage') {
|
||
const stringify = JSON.stringify({ plcFormat: configFormData })
|
||
setLogConfigReq({
|
||
id: rightClickData.value.id,
|
||
options: stringify,
|
||
})
|
||
.then(() => {
|
||
resolve(true)
|
||
})
|
||
.catch(() => {
|
||
throw '配置失败'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
const fileName = ref('')
|
||
const warningInfo = ref()
|
||
const previewFileDialogVisible = ref(false)
|
||
const readFile = (data: tableItemData) => {
|
||
fileName.value = activeName.value === 'malFunction' ? data.name.slice(24) : data.name
|
||
previewFileDialogVisible.value = true
|
||
getFileKeyEnum().finally(() => {
|
||
getFileData(data.path)
|
||
.then((res: { data: any; faultTime?: any }) => {
|
||
previewChartData = res.data
|
||
const attrName = Object.keys(res.data)
|
||
const data: any = []
|
||
let hasAddFaultTime = false
|
||
attrName.forEach((item) => {
|
||
if (item === 'TimeStamp') {
|
||
let timeStamp: any = []
|
||
previewChartData.TimeStamp = previewChartData.TimeStamp.map((item: any) => {
|
||
const parseTime = dayjs(item).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||
if (!hasAddFaultTime && res?.faultTime && item > res?.faultTime) {
|
||
const parseFaultTime = dayjs(res.faultTime).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||
warningInfo.value = parseFaultTime
|
||
timeStamp.push(parseFaultTime)
|
||
hasAddFaultTime = true
|
||
}
|
||
timeStamp.push(parseTime)
|
||
return parseTime
|
||
})
|
||
warningChartData.TimeStamp = timeStamp
|
||
} else if (item === 'TimeStampUTC') {
|
||
previewChartData.TimeStamp = previewChartData.TimeStampUTC.map((item: any) => {
|
||
return dayjs(item).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||
})
|
||
} else {
|
||
data.push({
|
||
label: fileKeyEnums?.[item] ?? item,
|
||
key: item,
|
||
isLeaf: true,
|
||
})
|
||
}
|
||
})
|
||
|
||
previewTreeData.value = data
|
||
originPreviewTreeData = data
|
||
})
|
||
.catch((err) => {
|
||
const info = err.response?.data?.msg
|
||
ElMessage.error(info || '文件读取失败')
|
||
})
|
||
.finally(() => {
|
||
previewTreeLoading.value = false
|
||
})
|
||
})
|
||
}
|
||
const downloadFile = (data: tableItemData) => {
|
||
runDownLoad(data)
|
||
.then((res) => {
|
||
const url = window.URL.createObjectURL(res.data)
|
||
const a = document.createElement('a')
|
||
a.style.display = 'none'
|
||
a.href = url
|
||
a.download = data.name
|
||
document.body.appendChild(a)
|
||
a.click()
|
||
window.URL.revokeObjectURL(url)
|
||
document.body.removeChild(a)
|
||
})
|
||
.catch((err) => {
|
||
ElMessage.error(err)
|
||
})
|
||
}
|
||
|
||
const runDownLoad = (data: tableItemData) => {
|
||
if (activeName.value === 'malFunction') {
|
||
return downloadFileReq({ url: data.path })
|
||
} else if (activeName.value === 'logManage') {
|
||
return downloadLogFileReq({ url: data.path })
|
||
}
|
||
return Promise.reject()
|
||
}
|
||
|
||
const closePreviewFileDialog = () => {
|
||
previewTreeData.value = []
|
||
previewChartData = []
|
||
selectPreviewTree = []
|
||
curSeries = []
|
||
previewChartInstance && previewChartInstance.clear()
|
||
}
|
||
|
||
const previewTreeLoading = ref(false)
|
||
const previewTreeRef = ref<TreeInstance>()
|
||
const previewTreeData = ref()
|
||
let originPreviewTreeData: any = []
|
||
const previewTreeProps = {
|
||
children: 'children',
|
||
label: 'label',
|
||
isLeaf: 'isLeaf',
|
||
}
|
||
const previewSearchTreeVal = ref('')
|
||
const previewSearchTree = (val: string) => {
|
||
nextTick(() => {
|
||
previewTreeRef.value?.setCheckedKeys(selectPreviewTree)
|
||
})
|
||
if (val === '') {
|
||
previewTreeData.value = originPreviewTreeData
|
||
return
|
||
}
|
||
const regex = new RegExp(val, 'i')
|
||
const filterData = originPreviewTreeData.filter((item: any) => regex.test(item.label))
|
||
previewTreeData.value = filterData
|
||
}
|
||
const clearPreviewTree = () => {
|
||
nextTick(() => {
|
||
selectPreviewTree = []
|
||
previewTreeRef.value?.setCheckedKeys(selectPreviewTree)
|
||
})
|
||
previewChartInstance && previewChartInstance.clear()
|
||
}
|
||
|
||
let selectPreviewTree: any = []
|
||
const handleCheckChange = (data: any, state: { checkedKeys: string[] }) => {
|
||
previewChartInstance && previewChartInstance.clear()
|
||
selectPreviewTree = state.checkedKeys
|
||
if (selectPreviewTree.length > 0) {
|
||
initPreviewChart()
|
||
} else {
|
||
previewChartInstance && previewChartInstance.clear()
|
||
}
|
||
}
|
||
// const previewNodeClick = (data: any) => {
|
||
// if (selectPreviewTree.includes(data.key)) {
|
||
// previewTreeRef.value?.setChecked(data.key, false, false)
|
||
// }else{
|
||
// previewTreeRef.value?.setChecked(data.key, true, false)
|
||
// }
|
||
// }
|
||
let previewChartData: any = {}
|
||
let warningChartData: any = {}
|
||
let curSeries: any = []
|
||
let previewChartInstance: any = null
|
||
const previewChartRef = ref()
|
||
|
||
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 createSeriresData = () => {
|
||
const seriesData: any = []
|
||
const curAttr = curSeries.map((item: any) => item.id)
|
||
selectPreviewTree.forEach((item: string) => {
|
||
if (curAttr.includes(item)) {
|
||
const seriesItem = curSeries.find((cur: any) => cur.id === item)
|
||
seriesData.push(seriesItem)
|
||
} else {
|
||
const color = getRandomDarkColor()
|
||
const createName = fileKeyEnums?.[item] ? fileKeyEnums[item] + (fileKeyUnit[item] ? `(${fileKeyUnit[item] ?? ''})` : '') : item
|
||
|
||
const data = {
|
||
id: item,
|
||
name: createName,
|
||
type: 'line',
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
color: color,
|
||
barBorderRadius: 2,
|
||
},
|
||
smooth: 0.6,
|
||
symbol: 'none',
|
||
xAxisIndex: 0,
|
||
data: previewChartData[item],
|
||
}
|
||
seriesData.push(data)
|
||
}
|
||
})
|
||
curSeries = seriesData
|
||
return seriesData
|
||
}
|
||
const initPreviewChart = () => {
|
||
const chart = previewChartInstance ?? echarts.init(previewChartRef.value)
|
||
const series = createSeriresData()
|
||
|
||
const option = {
|
||
grid: {
|
||
top: 50,
|
||
right: 23,
|
||
bottom: 50,
|
||
left: 18,
|
||
containLabel: true,
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'line',
|
||
},
|
||
},
|
||
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: previewChartData['TimeStamp'],
|
||
},
|
||
{
|
||
type: 'category',
|
||
show: false,
|
||
data: warningChartData.TimeStamp,
|
||
},
|
||
],
|
||
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:
|
||
activeName.value === 'malFunction'
|
||
? [
|
||
...series,
|
||
{
|
||
type: 'line',
|
||
name: '报警值',
|
||
data: [],
|
||
xAxisIndex: 1,
|
||
tooltip: {
|
||
show: false,
|
||
},
|
||
markLine: {
|
||
symbol: 'none',
|
||
data: [
|
||
{
|
||
xAxis: warningInfo.value,
|
||
},
|
||
],
|
||
animation: false,
|
||
},
|
||
},
|
||
]
|
||
: series,
|
||
}
|
||
chart.setOption(option)
|
||
previewChartInstance = chart
|
||
}
|
||
|
||
const getFileData = (url: string) => {
|
||
previewTreeLoading.value = true
|
||
if (activeName.value === 'malFunction') {
|
||
return previewFileReq({
|
||
deviceCode: curTreeData.value.code,
|
||
url: url,
|
||
}).then((res) => {
|
||
return { data: res.data.dataCurve, faultTime: res.data?.faultTime }
|
||
})
|
||
} else if (activeName.value === 'logManage') {
|
||
return previewLogFileReq({
|
||
deviceCode: curTreeData.value.code,
|
||
url: url,
|
||
}).then((res) => {
|
||
return { data: res.data }
|
||
})
|
||
}
|
||
return Promise.reject()
|
||
}
|
||
|
||
const fileKeyEnums: any = {}
|
||
const fileKeyUnit: any = {}
|
||
|
||
const getFileKeyEnum = () => {
|
||
return new Promise((resolve, reject) => {
|
||
if (activeName.value === 'malFunction') {
|
||
getFileKeyEnumsReq({
|
||
madeinfactory: curTreeData.value.madeinFactory,
|
||
model: curTreeData.value.model,
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
res.data.forEach((item: any) => {
|
||
fileKeyEnums[item.variable] = item?.description ?? item.variable
|
||
fileKeyUnit[item.variable] = item?.unit ?? ''
|
||
})
|
||
resolve(true)
|
||
}
|
||
})
|
||
.catch(() => {
|
||
reject(false)
|
||
})
|
||
} else if (activeName.value === 'logManage') {
|
||
getLogFileKeyEnumsReq({
|
||
madeinfactory: curTreeData.value.madeinFactory,
|
||
model: curTreeData.value.model,
|
||
})
|
||
.then((res) => {
|
||
if (res.success) {
|
||
res.data.forEach((item: any) => {
|
||
fileKeyEnums[item.variable] = item?.description ?? item.variable
|
||
fileKeyUnit[item.variable] = item?.unit ?? ''
|
||
})
|
||
resolve(true)
|
||
}
|
||
})
|
||
.catch(() => {
|
||
reject(false)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
let logOriginTableData = ref<tableItemData[]>([])
|
||
const logTableData = computed(() => {
|
||
let data = logOriginTableData.value
|
||
if (searchData.fileName) {
|
||
const reg = new RegExp(searchData.fileName, 'i')
|
||
data = data.filter((item) => reg.test(item.name))
|
||
}
|
||
const res = data.slice((logPageSetting.current - 1) * logPageSetting.pageSize, logPageSetting.current * logPageSetting.pageSize)
|
||
logPageSetting.total = res.length
|
||
return res
|
||
})
|
||
const logPageSetting = reactive({
|
||
current: 1,
|
||
pageSize: 20,
|
||
total: 0,
|
||
pageSizes: [20, 50, 100],
|
||
})
|
||
|
||
const activeName = ref<'malFunction' | 'logManage'>('malFunction')
|
||
const checkTab = () => {
|
||
getListForAirBlower()
|
||
}
|
||
|
||
onMounted(() => {
|
||
getTreeDataList().then((data: any) => {
|
||
const queryId = route.query.deviceId ?? data.id
|
||
treeRef.value && treeRef.value.setCurrentKey(queryId, true)
|
||
curTreeData.value = treeRef.value?.getCurrentNode()
|
||
getListForAirBlower()
|
||
})
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
previewChartInstance && previewChartInstance.dispose()
|
||
previewChartInstance = null
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.malfunction {
|
||
width: 100%;
|
||
height: 100%;
|
||
.container {
|
||
width: 100%;
|
||
height: 100%;
|
||
.aside {
|
||
width: 260px;
|
||
height: 100%;
|
||
padding: 0 20px;
|
||
border-right: 1px solid #eaebed;
|
||
.searchTree {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
width: 100%;
|
||
height: 80px;
|
||
.el-input {
|
||
width: 220px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
.treeMain {
|
||
width: 100%;
|
||
height: calc(100% - 80px);
|
||
}
|
||
}
|
||
.mainContainer {
|
||
width: calc(100% - 260px);
|
||
height: 100%;
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
height: 80px;
|
||
.searchFileName {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 320px;
|
||
height: 40px;
|
||
span {
|
||
width: 70px;
|
||
}
|
||
.el-input {
|
||
width: 220px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
.searchDate {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 400px;
|
||
height: 40px;
|
||
span {
|
||
flex-shrink: 0;
|
||
width: 45px;
|
||
}
|
||
:deep(.el-date-editor) {
|
||
width: 400px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
.btnPart {
|
||
margin-left: auto;
|
||
width: 360px;
|
||
text-align: right;
|
||
.el-button {
|
||
margin: 0 10px;
|
||
width: 100px;
|
||
height: 40px;
|
||
}
|
||
}
|
||
}
|
||
.main {
|
||
width: 100%;
|
||
height: calc(100% - 80px);
|
||
.tabs {
|
||
width: 100%;
|
||
height: 100%;
|
||
:deep(.el-tabs__content) {
|
||
width: 100%;
|
||
height: calc(100% - 60px);
|
||
.tabPane {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
.tableMain {
|
||
width: 100%;
|
||
height: calc(100% - 32px);
|
||
.tableClass {
|
||
width: 100%;
|
||
height: 100%;
|
||
.tableBtnPart {
|
||
display: flex;
|
||
justify-content: center;
|
||
.el-button {
|
||
margin: 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.footerPart {
|
||
display: flex;
|
||
justify-content: right;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.el-input-number {
|
||
width: 100%;
|
||
}
|
||
.previewFilePart {
|
||
width: 100%;
|
||
height: 500px;
|
||
.colPart {
|
||
width: 100%;
|
||
height: 100%;
|
||
.title {
|
||
padding-left: 20px;
|
||
width: 100%;
|
||
height: 30px;
|
||
font-size: 20px;
|
||
}
|
||
.previewTreeScrollbar {
|
||
height: calc(100% - 40px);
|
||
}
|
||
.chartPart {
|
||
position: relative;
|
||
width: 100%;
|
||
height: calc(100% - 40px);
|
||
.label {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 100px;
|
||
font-size: 20px;
|
||
}
|
||
.previewChart {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
.leftCol {
|
||
border-right: 1px solid #eaebed;
|
||
.search {
|
||
display: flex;
|
||
}
|
||
.previewSearchTree {
|
||
width: calc(100% - 80px);
|
||
height: 40px;
|
||
padding: 0 10px 10px 20px;
|
||
}
|
||
.el-button {
|
||
margin: 0 10px 10px 0;
|
||
width: 70px;
|
||
height: 30px;
|
||
}
|
||
}
|
||
}
|
||
.contextMenuBtns {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
height: 90px;
|
||
.el-button {
|
||
margin: 0;
|
||
height: 40px;
|
||
}
|
||
}
|
||
</style>
|