优化暂态事件列表

This commit is contained in:
guanj
2026-06-09 19:51:31 +08:00
parent 03d302ded8
commit bda7373133
48 changed files with 1834 additions and 1474 deletions

View File

@@ -1,38 +1,21 @@
<template> <template>
<div> <div>
<!--F47曲线 --> <!--F47曲线 -->
<TableHeader <TableHeader ref="TableHeaderRef" :showReset="false" :timeKeyList="prop.timeKey" @selectChange="selectChange"
ref="TableHeaderRef" datePicker v-if="fullscreen"></TableHeader>
:showReset="false"
:timeKeyList="prop.timeKey"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<el-descriptions class="mt2" direction="vertical" :column="4" border> <el-descriptions class="mt2" direction="vertical" :column="4" border>
<el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item> <el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item>
<el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item> <el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item>
<el-descriptions-item align="center" label="可容忍">{{ data.krr }}</el-descriptions-item> <el-descriptions-item align="center" label="可容忍">{{ data.krr }}</el-descriptions-item>
<el-descriptions-item align="center" label="不可容忍">{{ data.bkrr }}</el-descriptions-item> <el-descriptions-item align="center" label="不可容忍">{{ data.bkrr }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<my-echart <my-echart v-loading="tableStore.table.loading" ref="chartRef" class="tall" :options="echartList" :style="{
v-loading="tableStore.table.loading" width: prop.width,
ref="chartRef" height: `calc(${prop.height} - 80px - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
class="tall" }" @chart-click="handleChartClick" />
:options="echartList"
:style="{
width: prop.width,
height: `calc(${prop.height} - 80px - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
@chart-click="handleChartClick"
/>
<el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%"> <el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%">
<waveFormAnalysis <waveFormAnalysis v-loading="loading" ref="waveFormAnalysisRef" @handleHideCharts="isWaveCharts = false"
v-loading="loading" :wp="wp" />
ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false"
:wp="wp"
/>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@@ -151,7 +134,7 @@ const tableStore: any = new TableStore({
relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>' relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>' relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + a.value[1].toFixed(2) + '%</font>' relVal += "<font style='color:" + "'>特征幅值:" + Math.floor(a.value[1] * 100) / 100 + '%</font>'
return relVal return relVal
} }
}, },
@@ -173,10 +156,10 @@ const tableStore: any = new TableStore({
// max: function (value: any) { // max: function (value: any) {
// return value.max + 20 // return value.max + 20
// }, // },
max: function (value) { // max: function (value) {
// 先取原始最大值+20再向上取整到最近的10的倍数确保刻度够用且规整 // // 先取原始最大值+20再向上取整到最近的10的倍数确保刻度够用且规整
return Math.ceil((value.max + 20) / 10) * 10 // return Math.ceil((value.max + 20) / 10) * 10
}, // },
// splitNumber: 10, // splitNumber: 10,
// interval: 10, // interval: 10,
// minInterval: 10, // minInterval: 10,
@@ -461,7 +444,7 @@ const handleTolerableEventClick = async (row: any) => {
if (res != undefined) { if (res != undefined) {
boxoList.value = { boxoList.value = {
persistTime: row.value[0], //持续时间 persistTime: row.value[0], //持续时间
featureAmplitude: (row.value[1] / 100).toFixed(2), //残余电压 featureAmplitude: (row.value[1] / 100), //残余电压
startTime: row.value[2], //时间 startTime: row.value[2], //时间
lineName: row.value[4] //监测点名称 lineName: row.value[4] //监测点名称
} }
@@ -510,6 +493,6 @@ watch(
} }
) )
const addMenu = () => {} const addMenu = () => { }
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -135,7 +135,7 @@ const tableStore: any = new TableStore({
echartList.value = { echartList.value = {
title: { title: {
text: '指标越限严重度' text: '指标越限度'
}, },
xAxis: { xAxis: {
@@ -154,7 +154,7 @@ const tableStore: any = new TableStore({
series: [ series: [
{ {
type: 'bar', type: 'bar',
name: '指标越限严重度', name: '指标越限度',
data: tableStore.table.data.map((item: any) => Math.floor(item.extent * 100) / 100), data: tableStore.table.data.map((item: any) => Math.floor(item.extent * 100) / 100),
barMaxWidth: 30 barMaxWidth: 30
} }

View File

@@ -101,7 +101,7 @@ const tableStore: any = new TableStore({
setTime() setTime()
}, },
loadCallback: () => { loadCallback: () => {
value.value = tableStore.table.params.searchBeginTime value.value = dayjs(tableStore.table.params.searchBeginTime).toDate()
if (tableStore.table.data && tableStore.table.data.length > 0) { if (tableStore.table.data && tableStore.table.data.length > 0) {
list.value = tableStore.table.data.map((item: any) => { list.value = tableStore.table.data.map((item: any) => {
// 将 items 数组转换为带换行的文本 // 将 items 数组转换为带换行的文本

View File

@@ -1,95 +1,64 @@
<template> <template>
<div> <div class="device-control">
<!--指标拟合图 --> <!--指标拟合图 -->
<TableHeader <div v-show="fullscreen">
datePicker <PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
@selectChange="selectChange" </div>
v-if="fullscreen" <div>
ref="TableHeaderRef" <TableHeader datePicker @selectChange="selectChange" v-if="fullscreen" ref="TableHeaderRef"
:timeKeyList="prop.timeKey" :timeKeyList="prop.timeKey">
> <template v-slot:select>
<template v-slot:select> <!-- <el-form-item label="监测点">
<el-form-item label="监测点"> <el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable> <el-option v-for="item in lineList" :key="item.lineId" :label="item.name"
<el-option :value="item.lineId" />
v-for="item in lineList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item>
<el-form-item label="用户功率">
<el-select
filterable
v-model="tableStore.table.params.power"
placeholder="请选择用户功率"
clearable
>
<el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 120px !important"
placeholder="请选择"
v-model="tableStore.table.params.valueType"
filterable
>
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
<el-option value="cp95" label="cp95"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电能质量指标">
<el-select
filterable
v-model="tableStore.table.params.indicator"
placeholder="请选择电能质量指标"
clearable
>
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<div v-if="shouldShowHarmonicCount()" style="display: flex; color: var(--el-text-color-regular)">
<span style="width: 160px">{{ getHarmonicTypeName() }}谐波次数</span>
<el-select
v-model="tableStore.table.params.harmonicCount"
placeholder="请选择谐波次数"
style="min-width: 80px !important"
filterable
>
<el-option
v-for="num in harmonicCountOptions"
:key="num"
:label="num"
:value="num"
></el-option>
</el-select> </el-select>
</div> </el-form-item> -->
</el-form-item> <el-form-item label="用户功率">
</template> <el-select filterable v-model="tableStore.table.params.power" placeholder="请选择用户功率" clearable>
</TableHeader> <el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
<div v-loading="tableStore.table.loading"> </el-select>
<my-echart </el-form-item>
class="tall" <el-form-item label="统计类型">
v-if="lineShow" <el-select style="min-width: 120px !important" placeholder="请选择"
:options="echartList" v-model="tableStore.table.params.valueType" filterable>
:style="{ <el-option value="max" label="最大值"></el-option>
width: prop.width, <el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
<el-option value="cp95" label="cp95"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电能质量指标">
<el-select filterable v-model="tableStore.table.params.indicator" placeholder="请选择电能质量指标"
clearable>
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<div v-if="shouldShowHarmonicCount()"
style="display: flex; color: var(--el-text-color-regular)">
<span style="width: 160px">{{ getHarmonicTypeName() }}谐波次数</span>
<el-select v-model="tableStore.table.params.harmonicCount" placeholder="请选择谐波次数"
style="min-width: 80px !important" filterable>
<el-option v-for="num in harmonicCountOptions" :key="num" :label="num"
:value="num"></el-option>
</el-select>
</div>
</el-form-item>
</template>
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart class="tall" v-if="lineShow" :options="echartList" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`
}" }" />
/> <el-empty v-else description="暂无监测点" :style="{
<el-empty width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
v-else
description="暂无监测点"
:style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)` height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}" }" />
/> </div>
</div> </div>
</div> </div>
</template> </template>
@@ -103,7 +72,7 @@ import { cslineList, fittingData } from '@/api/harmonic-boot/cockpit/cockpit'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree' import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getTime } from '@/utils/formatTime' import { getTime } from '@/utils/formatTime'
import PointTree from '@/components/tree/govern/pointTree.vue'
const prop = defineProps({ const prop = defineProps({
w: { type: [String, Number] }, w: { type: [String, Number] },
h: { type: [String, Number] }, h: { type: [String, Number] },
@@ -111,7 +80,8 @@ const prop = defineProps({
height: { type: [String, Number] }, height: { type: [String, Number] },
timeKey: { type: Array as () => string[] }, timeKey: { type: Array as () => string[] },
timeValue: { type: Object }, timeValue: { type: Object },
interval: { type: Number } interval: { type: Number },
flag: { type: Boolean }
}) })
const TableHeaderRef = ref() const TableHeaderRef = ref()
@@ -128,6 +98,8 @@ const lineShow = ref(true)
const fullscreen = computed(() => { const fullscreen = computed(() => {
const w = Number(prop.w) const w = Number(prop.w)
const h = Number(prop.h) const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) { if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑 // 执行相应逻辑
return true return true
@@ -148,18 +120,29 @@ const exceedingTheLimitList: any = ref([
const indicatorList = ref() const indicatorList = ref()
const initLineList = async () => { // const initLineList = async () => {
cslineList({}).then(res => { // cslineList({}).then(res => {
setTime() // setTime()
if (res.data.length == 0) { // if (res.data.length == 0) {
lineShow.value = false // lineShow.value = false
return (tableStore.table.loading = false) // return (tableStore.table.loading = false)
} // }
lineShow.value = true // lineShow.value = true
lineList.value = res.data // lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId // tableStore.table.params.lineId = lineList.value[0].lineId
// initCode()
// })
// }
const nodeClick = (e: any) => {
if (e == undefined) {
}
if (e.level == 3) {
tableStore.table.params.lineId = e.id
initCode() initCode()
}) }
}
const pointTypeChange = (val: any, obj: any) => {
nodeClick(obj)
} }
const echartList = ref() const echartList = ref()
@@ -222,19 +205,19 @@ const setEchart = () => {
{}, {},
indicatorName indicatorName
? { ? {
min: 0, min: 0,
max: 1, max: 1,
axisLabel: { axisLabel: {
formatter: function (value: number) { formatter: function (value: number) {
if (value === 0) { if (value === 0) {
return '不越限' return '不越限'
} else if (value === 1) { } else if (value === 1) {
return '越限' return '越限'
} }
return value return value
} }
} }
} }
: {} : {}
], ],
grid: { grid: {
@@ -451,7 +434,7 @@ watch(
) )
onMounted(async () => { onMounted(async () => {
await initLineList() // await initCode()
}) })
watch( watch(
@@ -493,4 +476,11 @@ watch(
// :deep(.el-select) { // :deep(.el-select) {
// min-width: 80px; // min-width: 80px;
// } // }
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style> </style>

View File

@@ -187,6 +187,14 @@ const tableStore: any = new TableStore({
null: '/' null: '/'
} }
}, },
{
title: '电压等级(kV)',
field: 'volGrade',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue || '/'
}
},
{ {
title: '治理对象', title: '治理对象',
field: 'sensitiveUser', field: 'sensitiveUser',
@@ -196,14 +204,7 @@ const tableStore: any = new TableStore({
} }
}, },
{
title: '电压等级',
field: 'volGrade',
minWidth: '80',
formatter: (row: any) => {
return row.cellValue == 0 ? '/' : row.cellValue + 'kV' || '/'
}
},
{ {
title: '是否治理', title: '是否治理',
field: 'govern', field: 'govern',
@@ -242,7 +243,7 @@ const tableStore: any = new TableStore({
{ {
title: '报告', title: '报告',
field: 'reportFilePath', field: 'reportFilePath',
minWidth: '120', minWidth: '150',
formatter: (row: any) => { formatter: (row: any) => {
return row.cellValue == null ? '/' : row.cellValue.split('/').pop() return row.cellValue == null ? '/' : row.cellValue.split('/').pop()
} }
@@ -373,7 +374,9 @@ const forceDownloadPdf = async (pdfUrl:any, fileName = '文件.pdf') => {
// 4. 清理资源(避免内存泄漏) // 4. 清理资源(避免内存泄漏)
document.body.removeChild(a) document.body.removeChild(a)
URL.revokeObjectURL(blobUrl) URL.revokeObjectURL(blobUrl)
setTimeout(() => {
ElMessage.success('下载成功') ElMessage.success('下载成功')
}, 2000)
} catch (error) { } catch (error) {
console.error('下载失败:', error) console.error('下载失败:', error)
// ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus // ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus

View File

@@ -131,7 +131,7 @@ const tableStore: any = new TableStore({
}, },
loadCallback: () => { loadCallback: () => {
value.value = tableStore.table.params.searchBeginTime value.value = dayjs(tableStore.table.params.searchBeginTime).toDate()
list.value = tableStore.table.data list.value = tableStore.table.data
} }
}) })

View File

@@ -259,7 +259,7 @@ self.onmessage = function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'%  持续时间:' + '%  持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
@@ -283,7 +283,7 @@ self.onmessage = function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' + '% 持续时间:' +
boxoList.persistTime + boxoList.persistTime +
's' 's'
@@ -296,7 +296,7 @@ self.onmessage = function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'

View File

@@ -132,7 +132,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'
@@ -149,6 +149,7 @@ self.addEventListener('message', function (e) {
boxoList.evtParamTm + boxoList.evtParamTm +
's' 's'
} else if (boxoList.systemType == 'YPT') { } else if (boxoList.systemType == 'YPT') {
console.log("🚀 ~ boxoList.featureAmplitude:", boxoList.featureAmplitude)
titles = titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) + (boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' + ' 监测点名称:' +
@@ -156,7 +157,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' + '% 持续时间:' +
boxoList.persistTime + boxoList.persistTime +
's' 's'
@@ -169,7 +170,7 @@ self.addEventListener('message', function (e) {
' 发生时刻:' + ' 发生时刻:' +
boxoList.startTime + boxoList.startTime +
' 暂降幅值:' + ' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) + Math.floor(boxoList.featureAmplitude * 10000) / 100 +
'% 持续时间:' + '% 持续时间:' +
boxoList.duration + boxoList.duration +
's' 's'

View File

@@ -1,25 +1,12 @@
<template> <template>
<div style="width: 540px"> <div style="width: 540px">
<el-select <el-select v-model.trim="interval" style="min-width: 90px; width: 90px; margin-right: 10px"
v-model.trim="interval" @change="timeChange">
style="min-width: 90px; width: 90px; margin-right: 10px"
@change="timeChange"
>
<el-option v-for="item in filteredTimeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in filteredTimeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-date-picker <el-date-picker v-model.trim="timeValue" type="daterange" :disabled="disabledPicker"
v-model.trim="timeValue" style="width: 220px; margin-right: 10px" unlink-panels :clearable="false" range-separator=""
type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" :shortcuts="shortcuts" />
:disabled="disabledPicker"
style="width: 220px; margin-right: 10px"
unlink-panels
:clearable="false"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="shortcuts"
/>
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button> <el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button> <el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
@@ -95,7 +82,12 @@ const shortcuts = [
// 计算过滤后的 timeOptions // 计算过滤后的 timeOptions
const filteredTimeOptions = computed(() => { const filteredTimeOptions = computed(() => {
console.log("🚀 ~ props.timeKeyList :", props.timeKeyList )
// console.log("🚀 ~ props.timeKeyList:", props.timeKeyList)
if (props.timeKeyList.length > 0 && !props.timeKeyList.includes(interval.value.toString())) {
interval.value = Number(props.timeKeyList[0])
}
if (!props.timeKeyList || props.timeKeyList.length === 0) { if (!props.timeKeyList || props.timeKeyList.length === 0) {
return timeOptions.value return timeOptions.value
} }
@@ -182,10 +174,10 @@ const timeChange = (e: number) => {
timeFlag.value = 1 timeFlag.value = 1
} }
nextTick(() => { nextTick(() => {
// 检查按钮状态 // 检查按钮状态
checkInitialButtonStatus() checkInitialButtonStatus()
}) })
// 触发 change 事件 // 触发 change 事件
emitChange() emitChange()

View File

@@ -12,7 +12,7 @@ export interface LineTreeDecorators {
statusColor: (comFlag: number) => string statusColor: (comFlag: number) => string
applyMeta: ( applyMeta: (
node: any, node: any,
meta: { icon: string; color?: string; level?: number; disabled?: boolean } meta: { icon: string; color?: string; level?: number; disabled?: boolean; pname?: string }
) => void ) => void
} }
@@ -88,8 +88,10 @@ export function decorateLineTree(
applyMeta(leaf, { applyMeta(leaf, {
icon: 'local-监测点', icon: 'local-监测点',
color: statusColor(leaf.comFlag), color: statusColor(leaf.comFlag),
...LINE_LEAF_META ...LINE_LEAF_META
}) })
leaf.pname=item.name,
leaves.engineering.push(leaf) leaves.engineering.push(leaf)
}) })
}) })
@@ -113,8 +115,10 @@ export function decorateLineTree(
applyMeta(l4, { applyMeta(l4, {
icon: 'local-监测点', icon: 'local-监测点',
color: statusColor(l4.comFlag), color: statusColor(l4.comFlag),
...LINE_LEAF_META ...LINE_LEAF_META
}) })
l4.pname=item.name,
leaves.govern.push(l4) leaves.govern.push(l4)
}) })
}) })
@@ -127,8 +131,10 @@ export function decorateLineTree(
applyMeta(l2, { applyMeta(l2, {
icon: 'local-监测点', icon: 'local-监测点',
color: statusColor(l2.comFlag), color: statusColor(l2.comFlag),
...LINE_LEAF_META ...LINE_LEAF_META
}) })
l2.pname=item.name,
leaves.portable.push(l2) leaves.portable.push(l2)
}) })
}) })
@@ -148,8 +154,10 @@ export function decorateLineTree(
applyMeta(l4, { applyMeta(l4, {
icon: 'local-监测点', icon: 'local-监测点',
color: statusColor(l4.comFlag), color: statusColor(l4.comFlag),
...LINE_LEAF_META ...LINE_LEAF_META
}) })
l4.pname=item.name,
leaves.monitor.push(l4) leaves.monitor.push(l4)
}) })
}) })

View File

@@ -6,6 +6,7 @@
default-expand-all default-expand-all
@changePointType="changePointType" @changePointType="changePointType"
@changeTreeType="loadTree" @changeTreeType="loadTree"
:height="height"
/> />
</template> </template>
@@ -25,10 +26,12 @@ import {
interface Props { interface Props {
template?: boolean template?: boolean
height?: number
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
template: false template: false,
height: 0
}) })
defineOptions({ defineOptions({

View File

@@ -181,13 +181,15 @@ interface Props {
canExpand?: boolean canExpand?: boolean
type?: string type?: string
data?: any[] data?: any[]
height?: number
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
width: '100%', width: '100%',
canExpand: true, canExpand: true,
type: '', type: '',
data: () => [] data: () => [],
height: 0
}) })
const route = useRoute() const route = useRoute()
@@ -212,11 +214,11 @@ const zlDevList = ref<any[]>([])
const bxsDeviceData = ref<any[]>([]) const bxsDeviceData = ref<any[]>([])
const yqfDeviceData = ref<any[]>([]) const yqfDeviceData = ref<any[]>([])
const governTreeHeight = computed(() => 'calc(100vh - 380px)') const governTreeHeight = computed(() => `calc(100vh - 380px - ${props.height}px)`)
const otherTreeHeight = computed(() => const otherTreeHeight = computed(() =>
zlDeviceData.value.length ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' zlDeviceData.value.length ? `calc(100vh - 340px - ${props.height}px)` : `calc(100vh - 238px - ${props.height}px)`
) )
const engineeringTreeHeight = computed(() => 'calc(100vh - 188px)') const engineeringTreeHeight = computed(() => `calc(100vh - 188px - ${props.height}px)`)
const treeRef1 = ref<InstanceType<typeof ElTree>>() const treeRef1 = ref<InstanceType<typeof ElTree>>()
const treeRef2 = ref<InstanceType<typeof ElTree>>() const treeRef2 = ref<InstanceType<typeof ElTree>>()

View File

@@ -1,3 +1,4 @@
/* 修复 Chrome 浏览器输入框内选中字符行高异常的bug-s */ /* 修复 Chrome 浏览器输入框内选中字符行高异常的bug-s */
.el-input .el-input__inner { .el-input .el-input__inner {
height: 30px; height: 30px;

View File

@@ -1,36 +1,38 @@
@use 'sass:map'; @use 'sass:map';
@use 'mixins' as *; @use 'mixins' as *;
// 后台主体窗口左右间距 // 后台主体窗口左右间距
$main-space: 10px; $main-space: 10px;
$primary-light: #3f6ad8; $primary-light: #3f6ad8;
// --ba-background // --ba-background
$bg-color: () !default; $bg-color: () !default;
$bg-color: map.merge( $bg-color: map.merge(
( (
'': #edf0f3, '': #edf0f3,
'overlay': #ffffff, 'overlay': #ffffff
), ),
$bg-color $bg-color
); );
// --ba-border-color // --ba-border-color
$border-color: () !default; $border-color: () !default;
$border-color: map.merge( $border-color: map.merge(
( (
'': #f6f6f6, '': #f6f6f6
), ),
$border-color $border-color
); );
:root { :root {
@include set-css-var-value('main-space', $main-space); @include set-css-var-value('main-space', $main-space);
@include set-css-var-value('color-primary-light', $primary-light); @include set-css-var-value('color-primary-light', $primary-light);
@include set-component-css-var('bg-color', $bg-color); @include set-component-css-var('bg-color', $bg-color);
@include set-component-css-var('border-color', $border-color); @include set-component-css-var('border-color', $border-color);
// --vxe-table-row-current-background-color: var(--el-color-primary-light-7); --el-color-danger: #e26257;
// --vxe-table-row-hover-background-color: var(--el-color-primary-light-9); --el-color-success: #2ab914;
// --vxe-table-row-hover-current-background-color: var(--el-color-primary-light-7); // --vxe-table-row-current-background-color: var(--el-color-primary-light-7);
// --vxe-table-row-hover-striped-background-color: var(--el-color-primary-light-9); // --vxe-table-row-hover-background-color: var(--el-color-primary-light-9);
} // --vxe-table-row-hover-current-background-color: var(--el-color-primary-light-7);
// --vxe-table-row-hover-striped-background-color: var(--el-color-primary-light-9);
}

View File

@@ -10,7 +10,9 @@ export const downLoadFile = (name: string, key: string, res: any) => {
document.body.appendChild(link) document.body.appendChild(link)
link.click() //执行下载 link.click() //执行下载
document.body.removeChild(link) document.body.removeChild(link)
ElMessage.success('下载成功') setTimeout(() => {
ElMessage.success('下载成功')
}, 2000)
} }
const getFileType = (url: string) => { const getFileType = (url: string) => {

View File

@@ -22,7 +22,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item prop="path" label="接口路径"> <el-form-item prop="path" label="接口路径">
<el-input maxlength="32" show-word-limit v-model.trim="form.path" placeholder="请输入接口路径" /> <el-input v-model.trim="form.path" placeholder="请输入接口路径" />
</el-form-item> </el-form-item>
<el-form-item prop="type" label="接口类型"> <el-form-item prop="type" label="接口类型">
<el-radio-group v-model.trim="form.type"> <el-radio-group v-model.trim="form.type">

View File

@@ -18,10 +18,10 @@
<IconSelector v-model.trim="form.icon" placeholder="请选择图标" /> <IconSelector v-model.trim="form.icon" placeholder="请选择图标" />
</el-form-item> </el-form-item>
<el-form-item label="菜单路由" prop="path"> <el-form-item label="菜单路由" prop="path">
<el-input maxlength="32" show-word-limit v-model.trim="form.path" placeholder="请输入菜单名称" /> <el-input v-model.trim="form.path" placeholder="请输入菜单名称" />
</el-form-item> </el-form-item>
<el-form-item label="组件路径" prop="routeName"> <el-form-item label="组件路径" prop="routeName">
<el-input maxlength="32" show-word-limit <el-input
v-model.trim="form.routeName" v-model.trim="form.routeName"
placeholder="请输入组件路径,如/src/views/dashboard/index.vue" placeholder="请输入组件路径,如/src/views/dashboard/index.vue"
/> />

View File

@@ -110,14 +110,14 @@ provide('tableStore', tableStore)
// "target": [], // "target": [],
// "type": "", // "type": "",
// "userId": "" // "userId": ""
tableStore.table.params.engineeringid = '' // tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = '' // tableStore.table.params.projectId = ''
tableStore.table.params.deviceId = '' // tableStore.table.params.deviceId = ''
tableStore.table.params.type = 3 tableStore.table.params.type = 3
tableStore.table.params.eventIds = [] // tableStore.table.params.eventIds = []
tableStore.table.params.status = '' // tableStore.table.params.status = ''
tableStore.table.params.target = [] // tableStore.table.params.target = []
tableStore.table.params.userId = '' // tableStore.table.params.userId = ''
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
const sourceChange = (e: any) => { const sourceChange = (e: any) => {

View File

@@ -15,7 +15,7 @@
</el-form-item> --> </el-form-item> -->
<el-form-item label="关键字筛选"> <el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit style="width: 240px" <el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点、设备、项目、工程" /> v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点名称" />
</el-form-item> </el-form-item>
<el-form-item label="级别"> <el-form-item label="级别">
<el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable> <el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable>
@@ -193,18 +193,18 @@ provide('tableStore', tableStore)
// "type": "", // "type": "",
// "userId": "" // "userId": ""
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
tableStore.table.params.cascader = '' // tableStore.table.params.cascader = ''
tableStore.table.params.level = '' // tableStore.table.params.level = ''
tableStore.table.params.engineeringid = '' // tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = '' // tableStore.table.params.projectId = ''
tableStore.table.params.deviceId = '' // tableStore.table.params.deviceId = ''
tableStore.table.params.type = 3 tableStore.table.params.type = 3
tableStore.table.params.eventIds = [] // tableStore.table.params.eventIds = []
tableStore.table.params.status = '' // tableStore.table.params.status = ''
tableStore.table.params.target = [] // tableStore.table.params.target = []
tableStore.table.params.userId = '' // tableStore.table.params.userId = ''
tableStore.table.params.deviceTypeId = '' // tableStore.table.params.deviceTypeId = ''
tableStore.table.params.deviceTypeName = '' // tableStore.table.params.deviceTypeName = ''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => { deviceTreeOptions.value.map((item: any, index: any) => {
if (item?.children.length == 0) { if (item?.children.length == 0) {

View File

@@ -6,7 +6,7 @@
v-model.trim="tableStore.table.params.searchValue" v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入前置服务器名称ip" placeholder="请输入前置服务器名称ip"
/> />
</el-form-item> </el-form-item>
<el-form-item label="级别"> <el-form-item label="级别">

View File

@@ -8,7 +8,7 @@
</el-form-item> --> </el-form-item> -->
<el-form-item label="关键字筛选"> <el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit style="width: 240px" <el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点、设备、项目、工程" /> v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点名称" />
</el-form-item> </el-form-item>
<!-- <el-form-item label="级别"> <!-- <el-form-item label="级别">
<el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable> <el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable>
@@ -98,17 +98,17 @@ provide('tableStore', tableStore)
// "type": "", // "type": "",
// "userId": "" // "userId": ""
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
tableStore.table.params.engineeringid = '' // tableStore.table.params.engineeringid = ''
tableStore.table.params.deviceTypeId = '' // tableStore.table.params.deviceTypeId = ''
tableStore.table.params.projectId = '' // tableStore.table.params.projectId = ''
tableStore.table.params.deviceId = '' // tableStore.table.params.deviceId = ''
tableStore.table.params.type = 1 tableStore.table.params.type = 1
tableStore.table.params.eventIds = [] // tableStore.table.params.eventIds = []
tableStore.table.params.status = '' // tableStore.table.params.status = ''
tableStore.table.params.target = [] // tableStore.table.params.target = []
tableStore.table.params.userId = '' // tableStore.table.params.userId = ''
tableStore.table.params.cascader = '' // tableStore.table.params.cascader = ''
tableStore.table.params.deviceTypeName = '' // tableStore.table.params.deviceTypeName = ''
// tableStore.table.params.level='' // tableStore.table.params.level=''
const deviceTreeOptions = ref<any>(props.deviceTree) const deviceTreeOptions = ref<any>(props.deviceTree)

View File

@@ -10,18 +10,23 @@
</el-form-item> --> </el-form-item> -->
<el-form-item label="关键字筛选"> <el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit style="width: 240px" <el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点、设备、项目、工程" /> v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点名称" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="openFilterDialog">多条件筛选</el-button>
</el-form-item> </el-form-item>
<!-- <el-form-item>
<el-button type="primary" plain @click="openFilterDialog">事件筛选</el-button>
</el-form-item> -->
</template>
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template> </template>
</TableHeader> </TableHeader>
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" <MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
ref="multiConditionRef" @confirm="onFilterConfirm" /> @confirm="onFilterConfirm" />
<Table></Table> <Table></Table>
<!-- 补召日志 -->
<analysisList ref="analysisListRef"></analysisList>
</div> </div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef" <waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" /> @handleHideCharts="isWaveCharts = false" :wp="wp" />
@@ -32,13 +37,15 @@ import { ref, onMounted, provide, nextTick } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import MultiCondition from './multiCondition.vue' import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue' import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import { analyseWave, getFileByEventId } from '@/api/common' import { analyseWave, getFileByEventId } from '@/api/common'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend' import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree'
import analysisList from '@/views/govern/device/control/analysisList/index.vue'
const props = defineProps(['deviceTree']) const props = defineProps(['deviceTree'])
const refheader = ref() const refheader = ref()
@@ -50,15 +57,12 @@ const showBoxi = ref(true)
const loading = ref(false) const loading = ref(false)
const bxactiveName = ref('ssbx') const bxactiveName = ref('ssbx')
const boxoList: any = ref({}) const boxoList: any = ref({})
const analysisListRef = ref()
const wp = ref({}) const wp = ref({})
const dictData = useDictData() const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason') const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList = [ const DeviceType = ref([])
{ label: '电压暂降', value: '电压暂降' }, const EventTypeList: any = dictData.getBasicData('Event_Type')
{ label: '电压暂升', value: '电压暂升' },
{ label: '电压中断', value: '电压中断' }
]
const filterVisible = ref(false) const filterVisible = ref(false)
const multiConditionRef = ref<InstanceType<typeof MultiCondition>>() const multiConditionRef = ref<InstanceType<typeof MultiCondition>>()
@@ -82,16 +86,21 @@ const tableStore = new TableStore({
{ title: '暂降幅值(%)', minWidth: 120, field: 'evtParamVVaDepth', align: 'center', sortable: true, }, { title: '暂降幅值(%)', minWidth: 120, field: 'evtParamVVaDepth', align: 'center', sortable: true, },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true }, { title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true },
{
title: '严重度', field: 'severity', minWidth: 80, align: 'center', sortable: true, formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/'
}
},
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' }, { title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 120, align: 'center' }, { title: '触发类型', field: 'showName', minWidth: 100, align: 'center' },
{ {
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知' return ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ {
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知' return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
@@ -104,11 +113,11 @@ const tableStore = new TableStore({
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' }, { title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{ {
title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => { title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => {
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知' return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
} }
}, },
{ {
title: '操作', title: '操作',
fixed: 'right', fixed: 'right',
@@ -209,30 +218,53 @@ const tableStore = new TableStore({
}, },
{ {
name: 'edit', name: 'edit',
text: '暂无波形', text: '暂不支持补召',
type: 'info', type: 'info',
icon: 'el-icon-DataLine', icon: 'el-icon-DataLine',
render: 'basicButton', render: 'basicButton',
disabled: row => {
let code = DeviceType.value.filter((item: any) => item.id == row.devType)[0]?.code
return !(code == 'Direct_Connected_Device' || code == 'Gateway')
},
},
{
name: 'edit',
text: '波形补召',
type: 'primary',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading2',
disabled: row => { disabled: row => {
return row.wavePath return row.wavePath
},
click: row => {
let code = DeviceType.value.filter((item: any) => item.id == row.devType)[0]?.code
if (code == 'Portable') {
// 便携式设备
analysisListRef.value &&
analysisListRef.value.open({
lineId: row.lineId,
deviceData: row,
deviceId: row.deviceId
})
} else if (code == 'DEV_CLD') {
row.loading2 = true
// 监测设备
getFileByEventId(row.id).then(res => {
ElMessage.success(res.message)
tableStore.index()
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
} }
}, },
// {
// name: 'edit',
// title: '波形补召',
// type: 'primary',
// icon: 'el-icon-Check',
// render: 'basicButton',
// disabled: row => {
// return row.wavePath || row.showName === '未知'
// },
// click: row => {
// getFileByEventId(row.id).then(res => {
// ElMessage.success(res.message)
// tableStore.index()
// })
// }
// }
] ]
} }
], ],
@@ -257,21 +289,24 @@ provide('tableStore', tableStore)
// "type": "", // "type": "",
// "userId": "" // "userId": ""
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
tableStore.table.params.engineeringid = '' // tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = '' // tableStore.table.params.projectId = ''
tableStore.table.params.deviceTypeId = '' // tableStore.table.params.deviceTypeId = ''
tableStore.table.params.deviceId = '' // tableStore.table.params.deviceId = ''
tableStore.table.params.type = 0 tableStore.table.params.type = 0
tableStore.table.params.eventIds = [] // tableStore.table.params.eventIds = []
tableStore.table.params.status = '' // tableStore.table.params.status = ''
tableStore.table.params.target = [] // tableStore.table.params.target = []
tableStore.table.params.userId = '' // tableStore.table.params.userId = ''
tableStore.table.params.cascader = '' // tableStore.table.params.cascader = ''
tableStore.table.params.deviceTypeName = '' // tableStore.table.params.deviceTypeName = ''
Object.assign(tableStore.table.params, { Object.assign(tableStore.table.params, {
featureAmplitude: '', featureAmplitudeMin: undefined,
evtParamTm: '', featureAmplitudeMax: undefined,
showName: '', evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: '' fileFlag: ''
}) })
// tableStore.table.params.level='' // tableStore.table.params.level=''
@@ -345,6 +380,11 @@ const onResetForm = () => {
} }
onMounted(() => { onMounted(() => {
queryByCode('Device_Type').then(res => {
queryCsDictTree(res.data.id).then((list: any) => {
DeviceType.value = list.data
})
})
tableStore.index() tableStore.index()
}) })
const bxecharts = mainHeight(175).height as any const bxecharts = mainHeight(175).height as any

View File

@@ -1,37 +1,42 @@
<template> <template>
<el-dialog v-model="visible" title="多条件筛选" width="450px" append-to-body draggable <el-dialog v-model="visible" title="件筛选" width="450px" append-to-body draggable class="transient-filter-dialog">
class="transient-filter-dialog">
<el-form label-width="auto" class="filter-form"> <el-form label-width="auto" class="filter-form">
<el-form-item label="暂态幅值(%)"> <el-form-item label="暂态幅值(%)">
<div class="range-inputs"> <div class="range-inputs">
<el-input-number v-model="filterForm.featureAmplitudeMin" class="range-input-item" :min="0" <el-input-number v-model="params.featureAmplitudeMin" class="range-input-item" :min="0" :max="180"
:max="200" :precision="3" :step="0.01" :controls="false" placeholder="0" :precision="2" :step="0.01" :controls="false" placeholder="0" />
@change="onRangeMinChange($event, 'featureAmplitudeMax')" /> <span class="range-sep">&lt;&ensp;&ensp;&ensp;幅值&ensp;&ensp;&ensp;&lt;</span>
<span class="range-sep">&lt;  幅值  &lt;</span> <el-input-number v-model="params.featureAmplitudeMax" class="range-input-item" :min="0" :max="180"
<el-input-number v-model="filterForm.featureAmplitudeMax" class="range-input-item" :min="0" :precision="2" :step="0.01" :controls="false" placeholder="180" />
:max="200" :precision="3" :step="0.01" :controls="false" placeholder="200"
@change="onRangeMaxChange($event, filterForm.featureAmplitudeMin, 'featureAmplitudeMax', '暂态幅值')" />
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="暂态持续时间"> <el-form-item label="暂态持续时间(s)">
<div class="range-inputs"> <div class="range-inputs">
<el-input-number v-model="filterForm.evtParamTmMin" class="range-input-item" :min="0" <el-input-number v-model="params.evtParamTmMin" class="range-input-item" :min="0" :max="60"
:precision="3" :step="0.01" :controls="false" placeholder="X秒" :precision="2" :step="0.01" :controls="false" placeholder="X秒" />
@change="onRangeMinChange($event, 'evtParamTmMax')" /> <span class="range-sep">&lt;&ensp;持续时间&ensp;&lt;</span>
<span class="range-sep">&lt; 持续时间 &lt;</span> <el-input-number v-model="params.evtParamTmMax" class="range-input-item" :min="0" :max="60"
<el-input-number v-model="filterForm.evtParamTmMax" class="range-input-item" :min="0" :precision="2" :step="0.01" :controls="false" placeholder="X秒" />
:precision="3" :step="0.01" :controls="false" placeholder="X毫秒" </div>
@change="onRangeMaxChange($event, filterForm.evtParamTmMin, 'evtParamTmMax', '暂态持续时间')" /> </el-form-item>
<el-form-item label="事件严重度">
<div class="range-inputs">
<el-input-number v-model="params.severityMin" class="range-input-item" :min="0" :max="10"
:precision="2" :step="1" :controls="false" placeholder="0" />
<span class="range-sep">&lt;&emsp;严重度&emsp; &lt;</span>
<el-input-number v-model="params.severityMax" class="range-input-item" :min="0" :max="10"
:precision="2" :step="1" :controls="false" placeholder="10" />
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="触发类型"> <el-form-item label="触发类型">
<el-select v-model="params.showName" clearable placeholder="请选择触发类型" style="width: 100%"> <el-select v-model="params.target" clearable placeholder="请选择触发类型" multiple collapse-tags
style="width: 317px">
<el-option v-for="item in eventTypeList" :key="item.value" :label="item.label" <el-option v-for="item in eventTypeList" :key="item.value" :label="item.label"
:value="item.value" /> :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="是否存在波形"> <el-form-item label="是否存在波形">
<el-select v-model="params.fileFlag" clearable placeholder="请选择是否存在波形" style="width: 100%"> <el-select v-model="params.fileFlag" clearable placeholder="请选择是否存在波形" style="width: 317px">
<el-option v-for="item in fileFlagOptions" :key="item.value" :label="item.label" <el-option v-for="item in fileFlagOptions" :key="item.value" :label="item.label"
:value="item.value" /> :value="item.value" />
</el-select> </el-select>
@@ -46,13 +51,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
const getDefaultFilterParams = () => ({ const getDefaultFilterParams = () => ({
featureAmplitude: '', featureAmplitudeMin: undefined as number | undefined,
evtParamTm: '', featureAmplitudeMax: undefined as number | undefined,
showName: '', evtParamTmMin: undefined as number | undefined,
evtParamTmMax: undefined as number | undefined,
severityMin: undefined as number | undefined,
severityMax: undefined as number | undefined,
target: [] as string[],
fileFlag: '' as string | number fileFlag: '' as string | number
}) })
@@ -67,9 +75,11 @@ const emit = defineEmits<{
}>() }>()
const eventTypeList = [ const eventTypeList = [
{ label: '电压暂降', value: '电压暂降' }, { label: '暂降', value: '暂降' },
{ label: '电压暂升', value: '电压暂升' }, { label: '暂升', value: '暂升' },
{ label: '电压中断', value: '电压中断' } { label: '中断', value: '中断' },
{ label: '瞬态', value: '瞬态' },
{ label: '振荡', value: '振荡' },
] ]
const fileFlagOptions = [ const fileFlagOptions = [
@@ -77,50 +87,6 @@ const fileFlagOptions = [
{ label: '不存在', value: 0 } { label: '不存在', value: 0 }
] ]
type RangeMaxField = 'featureAmplitudeMax' | 'evtParamTmMax'
const filterForm = ref<{
featureAmplitudeMin?: number
featureAmplitudeMax?: number
evtParamTmMin?: number
evtParamTmMax?: number
}>({})
const parseRangeParam = (val: string): [number | undefined, number | undefined] => {
if (!val) return [undefined, undefined]
const [min = '', max = ''] = String(val).split(',')
const minNum = min !== '' ? Number(min) : undefined
const maxNum = max !== '' ? Number(max) : undefined
return [
minNum != null && !Number.isNaN(minNum) ? minNum : undefined,
maxNum != null && !Number.isNaN(maxNum) ? maxNum : undefined
]
}
const joinRangeParam = (min?: number, max?: number) => {
if (min == null && max == null) return ''
return `${min ?? ''},${max ?? ''}`
}
const onRangeMinChange = (min: number | undefined, maxField: RangeMaxField) => {
const max = filterForm.value[maxField]
if (min != null && max != null && max <= min) {
filterForm.value[maxField] = undefined
}
}
const onRangeMaxChange = (
max: number | undefined,
min: number | undefined,
maxField: RangeMaxField,
label: string
) => {
if (min != null && max != null && max <= min) {
ElMessage.warning(`${label}最大值必须大于最小值`)
filterForm.value[maxField] = undefined
}
}
const validateRange = ( const validateRange = (
min: number | undefined, min: number | undefined,
max: number | undefined, max: number | undefined,
@@ -132,15 +98,8 @@ const validateRange = (
if (!hasMin && !hasMax) return true if (!hasMin && !hasMax) return true
if (hasMin !== hasMax) { // 只填最小值或最大值其中一个时,不做范围校验
ElMessage.warning(`${label}请同时填写最小值和最大值`) if (hasMin !== hasMax) return true
return false
}
if (min! <= 0 || max! <= 0) {
ElMessage.warning(`${label}必须大于0`)
return false
}
if (max! <= min!) { if (max! <= min!) {
ElMessage.warning(`${label}最大值必须大于最小值`) ElMessage.warning(`${label}最大值必须大于最小值`)
@@ -155,41 +114,30 @@ const validateRange = (
return true return true
} }
const syncFromParams = () => {
const [faMin, faMax] = parseRangeParam(props.params.featureAmplitude)
const [tmMin, tmMax] = parseRangeParam(props.params.evtParamTm)
filterForm.value.featureAmplitudeMin = faMin
filterForm.value.featureAmplitudeMax = faMax
filterForm.value.evtParamTmMin = tmMin
filterForm.value.evtParamTmMax = tmMax
}
const reset = () => { const reset = () => {
filterForm.value = {}
Object.assign(props.params, getDefaultFilterParams()) Object.assign(props.params, getDefaultFilterParams())
} }
const onConfirm = () => { const onConfirm = () => {
const { featureAmplitudeMin, featureAmplitudeMax, evtParamTmMin, evtParamTmMax } = filterForm.value const { featureAmplitudeMin, featureAmplitudeMax, evtParamTmMin, evtParamTmMax, severityMin, severityMax } = props.params
if (!validateRange(featureAmplitudeMin, featureAmplitudeMax, '暂态幅值', { maxLimit: 200 })) { if (!validateRange(featureAmplitudeMin, featureAmplitudeMax, '暂态幅值', { maxLimit: 180 })) {
return return
} }
if (!validateRange(evtParamTmMin, evtParamTmMax, '暂态持续时间')) { if (!validateRange(evtParamTmMin, evtParamTmMax, '暂态持续时间', { maxLimit: 60 })) {
return
}
if (!validateRange(severityMin, severityMax, '严重度', { maxLimit: 10 })) {
return return
} }
props.params.featureAmplitude = joinRangeParam(featureAmplitudeMin, featureAmplitudeMax)
props.params.evtParamTm = joinRangeParam(evtParamTmMin, evtParamTmMax)
visible.value = false visible.value = false
emit('confirm') emit('confirm')
} }
watch(visible, val => { defineExpose({ reset })
if (val) syncFromParams()
})
defineExpose({ reset, syncFromParams })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -149,15 +149,15 @@ const tableStore = new TableStore({
} }
}, },
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' }, { title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 150 }, { title: '触发类型', field: 'showName', minWidth: 100 },
{ {
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知' return ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ {
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知' return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, }, { title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },

View File

@@ -1,37 +1,37 @@
<template> <template>
<div class="default-main" :style="{ height: pageHeight.height }"> <div class="default-main" :style="{ height: pageHeight.height }">
<el-tabs v-model="activeTab" type="border-card"> <el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="图表" name="chart"> <el-tab-pane label="图表" name="chart">
<chart v-if="activeTab === 'chart'"/> <chart v-if="activeTab === 'chart'"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="报表" name="report"> <el-tab-pane label="报表" name="report">
<report v-if="activeTab === 'report'"/> <report v-if="activeTab === 'report'"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import chart from './components/chart.vue' import chart from './components/chart.vue'
import report from './components/report.vue' import report from './components/report.vue'
defineOptions({ defineOptions({
name: 'govern/analyze/steadyState' name: 'govern/analyze/steadyState'
}) })
const pageHeight = mainHeight(20) const pageHeight = mainHeight(20)
const activeTab = ref('chart') const activeTab = ref('chart')
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
:deep(.el-tabs__content) { :deep(.el-tabs__content) {
padding: 0; padding: 0;
} }
</style> </style>

View File

@@ -144,7 +144,7 @@
<el-form-item class="form-item" label="描述:" <el-form-item class="form-item" label="描述:"
:prop="'projectInfoList[' + index + '].description'" :prop="'projectInfoList[' + index + '].description'"
:rules="[{ required: true, message: '请输入描述', trigger: 'blur' }]"> :rules="[{ required: true, message: '请输入描述', trigger: 'blur' }]">
<el-input maxlength="32" show-word-limit clearable v-model="item.description" placeholder="请输入描述" <el-input maxlength="300" show-word-limit clearable v-model="item.description" placeholder="请输入描述"
:disabled="!( :disabled="!(
(nodeLevel == 2 && pageStatus == 3) || (nodeLevel == 2 && pageStatus == 3) ||
((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) && ((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) &&

View File

@@ -193,14 +193,14 @@
</TableHeader> </TableHeader>
<div class="data_time" :style="{ <div class="data_time" :style="{
alignItems: realTimeFlag ? 'flex-end' : 'center' alignItems: realTimeFlag ? 'flex-end' : 'center'
}" v-if="dataSet.includes('_realtimedata') && sonTab != 2 && trendDataTime && !realTimeFlag"> }" v-if="dataSet.includes('_realtimedata') && sonTab != 2 && !realTimeFlag">
<p class="mb10 mt10"> <p class="mb10 mt10" >
<span>数据时间:{{ trendDataTime || '-' }}</span> <span>数据时间:{{ trendDataTime || '-' }}</span>
</p> </p>
<el-button v-if="!realTimeFlag && dataSet.includes('_realtimedata')" style="float: right !important" <el-button v-if="!realTimeFlag && dataSet.includes('_realtimedata')" style="float: right !important"
:icon="Back" @click="handleReturn" :loading="tableLoading"> :icon="Back" @click="handleReturn" >
返回 返回
</el-button> </el-button>
</div> </div>
@@ -225,7 +225,7 @@
"> ">
<div class="mb10 mt10" v-if="dataSet.indexOf('_history') == -1" <div class="mb10 mt10" v-if="dataSet.indexOf('_history') == -1"
style="font-weight: 700; font-size: 13px; text-align: center"> style="font-weight: 700; font-size: 13px; text-align: center">
最新数据时标:{{ tableData[0]?.time || '-' }} 最新数据时标:{{ latestTime || '-' }}
</div> </div>
<nearRealTimeData ref="nearRealTimeDataRef" v-if="dataSet.indexOf('_history') == -1" <nearRealTimeData ref="nearRealTimeDataRef" v-if="dataSet.indexOf('_history') == -1"
@@ -309,8 +309,7 @@
</div> </div>
<!-- 实时数据 --> <!-- 实时数据 -->
<div :style="`height: calc(100vh - (${sonTab == 1 ? '346px' : sonTab == 2 ? '308px' : '393px'}))`" <div :style="`height: calc(100vh - (${sonTab == 1 ? '346px' : sonTab == 2 ? '308px' : '393px'}))`"
v-if="dataSet.indexOf('_realtimedata') != -1" v-loading="tableLoading" v-if="dataSet.indexOf('_realtimedata') != -1" v-loading="tableLoading">
>
<!-- <div class="view_top_btn" v-if="realTimeFlag"> <!-- <div class="view_top_btn" v-if="realTimeFlag">
<el-button type="primary" :icon="Platform" @click="handleRecordWaves"> <el-button type="primary" :icon="Platform" @click="handleRecordWaves">
实时录波 实时录波
@@ -334,7 +333,7 @@
</div> </div>
<!-- 暂态事件 --> <!-- 暂态事件 -->
<div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_event') != -1"> <div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_event') != -1">
<Event ref="eventRef" :deviceType="deviceType"></Event> <Event ref="eventRef" :deviceType="deviceType" @fileRecall="handleAnalysisList"></Event>
</div> </div>
<!-- 测试项记录 --> <!-- 测试项记录 -->
<div style="height: calc(100vh - 395px)" v-if="dataSet.indexOf('_items') != -1" <div style="height: calc(100vh - 395px)" v-if="dataSet.indexOf('_items') != -1"
@@ -503,7 +502,7 @@ const activeTrendName: any = ref(0)
const trendTimer: any = ref() const trendTimer: any = ref()
const trendDataTime: any = ref() const trendDataTime: any = ref()
const showButton = ref(false) const showButton = ref(false)
const latestTime=ref('')
const decodeMqttPayload = (message: any) => { const decodeMqttPayload = (message: any) => {
try { try {
return JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message)))) return JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
@@ -536,7 +535,7 @@ const handleTrend = async () => {
await getHarmRealData(lineId.value, activeTrendName.value) await getHarmRealData(lineId.value, activeTrendName.value)
.then((res: any) => { .then((res: any) => {
if (res.code == 'A0000') { if (res.code == 'A0000') {
trendDataTime.value = '' // trendDataTime.value = ''
// ElMessage.success('设备应答成功') // ElMessage.success('设备应答成功')
//每隔30s调用一下接口通知后台推送mqtt消息 //每隔30s调用一下接口通知后台推送mqtt消息
trendTimer.value = window.setInterval(() => { trendTimer.value = window.setInterval(() => {
@@ -625,14 +624,14 @@ const handleReturn = async () => {
const handleSearch = () => { const handleSearch = () => {
let queryListName = queryList.value.filter((item: any) => item.id == formInline.targetType) let queryListName = queryList.value.filter((item: any) => item.id == formInline.targetType)
let list = tableData.value.filter((item: any) => { let list = tableData.value.filter((item: any) => {
if (item.otherName.includes(searchValue.value)) { if (item.name.includes(searchValue.value)) {
return item return item
} }
}) })
if (oddAndEvenFlag.value) { if (oddAndEvenFlag.value) {
list = list.filter((item: any) => { list = list.filter((item: any) => {
let str = item.otherName.split('次')[0] let str = item.name.split('次')[0]
queryListName[0].name == '间谐波电压含有率' ? (str = str - 0.5) : ''
if (oddAndEven.value == '1') { if (oddAndEven.value == '1') {
// 奇次 // 奇次
@@ -689,9 +688,16 @@ const dataLevel: any = ref('')
const dataSource = ref([]) const dataSource = ref([])
const engineeringName = ref('') const engineeringName = ref('')
const nodeClick = async (e: anyObj, node?: any) => { const nodeClick = async (e: anyObj, node?: any) => {
console.log("🚀 ~ nodeClick ~ node:", e)
if (e == undefined) { if (e == undefined) {
return (loading.value = false) return (loading.value = false)
} }
if(e.pname?.includes('便携')){
deviceType.value = '1'
}else{
deviceType.value = '2'
}
searchValue.value = '' searchValue.value = ''
deviceId.value = e?.pid deviceId.value = e?.pid
@@ -788,7 +794,7 @@ const nodeClick = async (e: anyObj, node?: any) => {
//治理设备和便携式设备切换判断 //治理设备和便携式设备切换判断
const deviceType = ref('0') const deviceType = ref('0')
const pointTypeChange = (val: any, obj: any) => { const pointTypeChange = (val: any, obj: any) => {
deviceType.value = val
nodeClick(obj) nodeClick(obj)
} }
const realTimeRef: any = ref() const realTimeRef: any = ref()
@@ -1234,21 +1240,23 @@ const handleClick = async (tab?: any) => {
//查询当前指标 //查询当前指标
if (!dataSet.value.includes('_')) { if (!dataSet.value.includes('_')) {
formInline.id = dataSet.value formInline.id = dataSet.value
latestTime.value=''
tableData.value=[]
// await deviceRtData(formInline) // await deviceRtData(formInline)
await realTimeData(formInline) await realTimeData(formInline)
.then((res: any) => { .then((res: any) => {
tableData.value = res.data tableData.value = res.data[0].children
latestTime.value=res.data[0].dataTime
formInline.total = res.data.total formInline.total = res.data.total
let queryListName = queryList.value.filter((item: any) => item.id == formInline.targetType) let queryListName = queryList.value.filter((item: any) => item.id == formInline.targetType)
let list = tableData.value.filter((item: any) => { let list = tableData.value.filter((item: any) => {
if (item.otherName.includes(searchValue.value)) { if (item.name.includes(searchValue.value)) {
return item return item
} }
}) })
if (oddAndEvenFlag.value) { if (oddAndEvenFlag.value) {
list = list.filter((item: any) => { list = list.filter((item: any) => {
let str = item.otherName.split('次')[0] let str = item.name.split('次')[0]
queryListName[0].name == '间谐波电压含有率' ? (str = str - 0.5) : ''
if (oddAndEven.value == '1') { if (oddAndEven.value == '1') {
// 奇次 // 奇次
@@ -1276,6 +1284,7 @@ const handleClick = async (tab?: any) => {
}, 1500) }, 1500)
}) })
.catch(e => { .catch(e => {
loading.value = false
setTimeout(() => { setTimeout(() => {
tableLoading.value = false tableLoading.value = false
}, 1500) }, 1500)
@@ -1340,10 +1349,12 @@ queryByCode('Device_Type').then(res => {
}) })
}) })
const handleTargetTypeChange = () => { const handleTargetTypeChange = () => {
if (queryList.value.filter((item: any) => item.id == formInline.targetType)[0].name != '基本数据') { const targetItem = queryList.value.filter((item: any) => item.id == formInline.targetType)[0];
oddAndEvenFlag.value = true
if (targetItem?.name !== '基本数据' && !targetItem?.name?.includes('间谐波')) {
oddAndEvenFlag.value = true;
} else { } else {
oddAndEvenFlag.value = false oddAndEvenFlag.value = false;
} }
} }
const queryList: any = ref([]) const queryList: any = ref([])

View File

@@ -1,17 +1,18 @@
<template> <template>
<div class="near-realtime-data"> <div class="near-realtime-data">
<div class="view_bot"> <div class="view_bot">
<template v-for="(section, sectionIndex) in tableSections" :key="sectionIndex"> <template v-for="(section, sectionIndex) in tableSections" :key="sectionIndex">
<vxe-table class="near-realtime-table" border height="" width="100%" :data="[section.row]" <vxe-table class="near-realtime-table" border height="" width="100%" :data="[section.row]"
:column-config="tableColumnConfig" :tooltip-config="tableTooltipConfig"> :column-config="tableColumnConfig" :tooltip-config="tableTooltipConfig">
<vxe-colgroup v-for="(item, colIndex) in section.abcItems" :key="`abc-${colIndex}`" align="center" <vxe-colgroup v-for="(item, colIndex) in section.abcItems" :key="`abc-${colIndex}`" align="center"
:title="item.otherName" :width="getMetricWidth(section)"> :title="item.otherName" :width="getMetricWidth(section)">
<vxe-column align="center" :field="`v${colIndex}A`" title="A相" :width="getPhaseWidth(section)" <vxe-column align="center" :field="`v${colIndex}A`" :title="item.phaseLabels[0]"
:formatter="cellFormatter"></vxe-column> :width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}B`" title="B相" :width="getPhaseWidth(section)" <vxe-column align="center" :field="`v${colIndex}B`" :title="item.phaseLabels[1]"
:formatter="cellFormatter"></vxe-column> :width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}C`" title="C相" :width="getPhaseWidth(section)" <vxe-column align="center" :field="`v${colIndex}C`" :title="item.phaseLabels[2]"
:formatter="cellFormatter"></vxe-column> :width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
</vxe-colgroup> </vxe-colgroup>
<vxe-column v-for="(item, colIndex) in section.scalarItems" :key="`scalar-${colIndex}`" <vxe-column v-for="(item, colIndex) in section.scalarItems" :key="`scalar-${colIndex}`"
align="center" :field="`s${colIndex}`" :title="item.otherName" :width="getMetricWidth(section)" align="center" :field="`s${colIndex}`" :title="item.otherName" :width="getMetricWidth(section)"
@@ -34,19 +35,48 @@ import { ref } from 'vue'
const ROW_WITH_ABC = 4 const ROW_WITH_ABC = 4
const ROW_SCALAR_ONLY = 5 const ROW_SCALAR_ONLY = 5
interface DisplayMetric {
otherName: string
valueA?: unknown
valueB?: unknown
valueC?: unknown
valueM?: unknown
phaseLabels: [string, string, string]
sort: number
}
interface MetricItem { interface MetricItem {
type: 'abc' | 'scalar' type: 'abc' | 'scalar'
data: any data: DisplayMetric
} }
interface TableSection { interface TableSection {
abcItems: any[] abcItems: DisplayMetric[]
scalarItems: any[] scalarItems: DisplayMetric[]
row: Record<string, unknown> row: Record<string, unknown>
slotsPerRow: number slotsPerRow: number
emptySlotCount: number emptySlotCount: number
} }
interface RawMetricItem {
targetId?: string
name?: string
unit?: string | null
phase?: string
sort?: number
data?: unknown
otherName?: string
valueA?: unknown
valueB?: unknown
valueC?: unknown
valueM?: unknown
}
const PHASE_GROUPS = [
{ keys: ['A', 'B', 'C'], labels: ['A相', 'B相', 'C相'] as [string, string, string] },
{ keys: ['AB', 'BC', 'CA'], labels: ['AB', 'BC', 'CA'] as [string, string, string] },
]
const height = mainHeight(345) const height = mainHeight(345)
const tableSections = ref<TableSection[]>([]) const tableSections = ref<TableSection[]>([])
@@ -64,10 +94,103 @@ const formatCellValue = (value: unknown): string | number => {
const cellFormatter = ({ cellValue }: { cellValue: unknown }) => formatCellValue(cellValue) const cellFormatter = ({ cellValue }: { cellValue: unknown }) => formatCellValue(cellValue)
const hasAbcValues = (item: any) => const buildOtherName = (name: string, unit?: string | null) => (unit ? `${name}(${unit})` : name)
const isNewFormat = (item: RawMetricItem) => item.targetId != null && item.phase != null && 'data' in item
const hasAbcValues = (item: DisplayMetric | RawMetricItem) =>
item.valueA != null || item.valueB != null || item.valueC != null item.valueA != null || item.valueB != null || item.valueC != null
const buildRow = (abcItems: any[], scalarItems: any[]) => { const normalizeOldItem = (item: RawMetricItem): DisplayMetric => ({
otherName: item.otherName || buildOtherName(item.name || ''),
valueA: item.valueA,
valueB: item.valueB,
valueC: item.valueC,
valueM: item.valueM,
phaseLabels: ['A相', 'B相', 'C相'],
sort: item.sort ?? 0,
})
const getHarmonicOrder = (name: string) => {
const match = name.match(/^(\d+(?:\.\d+)?)次/)
return match ? parseFloat(match[1]) : null
}
const getGroupKey = (item: RawMetricItem) => `${item.targetId}_${item.name}`
const groupNewFormatData = (list: RawMetricItem[]): DisplayMetric[] => {
const groupMap = new Map<string, { name: string; unit: string | null; sort: number; phases: Record<string, unknown> }>()
list.forEach(item => {
const key = getGroupKey(item)
if (!groupMap.has(key)) {
groupMap.set(key, {
name: item.name || '',
unit: item.unit ?? null,
sort: item.sort ?? 0,
phases: {},
})
}
if (item.phase != null) {
groupMap.get(key)!.phases[item.phase] = item.data
}
})
return Array.from(groupMap.values())
.sort((a, b) => {
if (a.sort !== b.sort) return a.sort - b.sort
const orderA = getHarmonicOrder(a.name)
const orderB = getHarmonicOrder(b.name)
if (orderA != null && orderB != null) return orderA - orderB
return a.name.localeCompare(b.name, 'zh-CN')
})
.map(group => {
const otherName = buildOtherName(group.name, group.unit)
const phaseKeys = Object.keys(group.phases)
if (phaseKeys.length === 1 && phaseKeys[0] === 'T') {
return {
otherName,
valueM: group.phases.T,
phaseLabels: ['A相', 'B相', 'C相'],
sort: group.sort,
}
}
for (const { keys, labels } of PHASE_GROUPS) {
if (keys.some(key => group.phases[key] != null)) {
return {
otherName,
valueA: group.phases[keys[0]],
valueB: group.phases[keys[1]],
valueC: group.phases[keys[2]],
phaseLabels: labels,
sort: group.sort,
}
}
}
return {
otherName,
valueM: phaseKeys.length === 1 ? group.phases[phaseKeys[0]] : undefined,
valueA: group.phases.A ?? group.phases.AB,
valueB: group.phases.B ?? group.phases.BC,
valueC: group.phases.C ?? group.phases.CA,
phaseLabels: group.phases.AB != null ? ['AB', 'BC', 'CA'] : ['A相', 'B相', 'C相'],
sort: group.sort,
}
})
}
const normalizeMetrics = (data: RawMetricItem[]): DisplayMetric[] => {
if (!data?.length) return []
if (isNewFormat(data[0])) {
return groupNewFormatData(data)
}
return data.map(normalizeOldItem).sort((a, b) => a.sort - b.sort)
}
const buildRow = (abcItems: DisplayMetric[], scalarItems: DisplayMetric[]) => {
const row: Record<string, unknown> = {} const row: Record<string, unknown> = {}
abcItems.forEach((item, index) => { abcItems.forEach((item, index) => {
row[`v${index}A`] = item.valueA row[`v${index}A`] = item.valueA
@@ -80,10 +203,10 @@ const buildRow = (abcItems: any[], scalarItems: any[]) => {
return row return row
} }
const buildTableSections = (abcList: any[], scalarList: any[]) => { const buildTableSections = (abcList: DisplayMetric[], scalarList: DisplayMetric[]) => {
const unified: MetricItem[] = [ const unified: MetricItem[] = [
...abcList.map((data) => ({ type: 'abc' as const, data })), ...abcList.map(data => ({ type: 'abc' as const, data })),
...scalarList.map((data) => ({ type: 'scalar' as const, data })), ...scalarList.map(data => ({ type: 'scalar' as const, data })),
] ]
const sections: TableSection[] = [] const sections: TableSection[] = []
@@ -95,8 +218,8 @@ const buildTableSections = (abcList: any[], scalarList: any[]) => {
const chunk = unified.slice(index, index + maxCount) const chunk = unified.slice(index, index + maxCount)
index += chunk.length index += chunk.length
const abcItems = chunk.filter((item) => item.type === 'abc').map((item) => item.data) const abcItems = chunk.filter(item => item.type === 'abc').map(item => item.data)
const scalarItems = chunk.filter((item) => item.type === 'scalar').map((item) => item.data) const scalarItems = chunk.filter(item => item.type === 'scalar').map(item => item.data)
const slotsPerRow = abcItems.length > 0 ? ROW_WITH_ABC : ROW_SCALAR_ONLY const slotsPerRow = abcItems.length > 0 ? ROW_WITH_ABC : ROW_SCALAR_ONLY
const metricCount = abcItems.length + scalarItems.length const metricCount = abcItems.length + scalarItems.length
sections.push({ sections.push({
@@ -111,18 +234,11 @@ const buildTableSections = (abcList: any[], scalarList: any[]) => {
return sections return sections
} }
const setData = (data: any, _targetType: any) => { const setData = (data: RawMetricItem[], _targetType?: unknown) => {
const list = JSON.parse(JSON.stringify(data)) const list = JSON.parse(JSON.stringify(data || [])) as RawMetricItem[]
const abcList: any[] = [] const metrics = normalizeMetrics(list)
const scalarList: any[] = [] const abcList = metrics.filter(item => hasAbcValues(item))
const scalarList = metrics.filter(item => !hasAbcValues(item))
list.forEach((item: any) => {
if (hasAbcValues(item)) {
abcList.push(item)
} else {
scalarList.push(item)
}
})
tableSections.value = buildTableSections(abcList, scalarList) tableSections.value = buildTableSections(abcList, scalarList)
} }

View File

@@ -99,6 +99,8 @@ const getMakeUpDataList = (row: any) => {
}) })
dirList.value = res.data dirList.value = res.data
loading.value = false loading.value = false
}).catch(() => {
loading.value = false
}) })
} }
// 进入文件夹 // 进入文件夹

View File

@@ -4,8 +4,8 @@
<div class="harmonic_select" v-if="!loading"> <div class="harmonic_select" v-if="!loading">
<el-form :model="searchForm" id="history_select"> <el-form :model="searchForm" id="history_select">
<el-form-item label="稳态指标"> <el-form-item label="稳态指标">
<el-select multiple collapse-tags collapse-tags-tooltip filterable v-model.trim="searchForm.index" <el-select multiple collapse-tags collapse-tags-tooltip filterable v-model.trim="searchForm.index"
placeholder="请选择统计指标" :multiple-limit="3" value-key="id"> placeholder="请选择统计指标" :multiple-limit="3" value-key="id">
<el-option v-for="(item, index) in indexOptions" :label="item.name" :key="index" <el-option v-for="(item, index) in indexOptions" :label="item.name" :key="index"
:value="item"></el-option> :value="item"></el-option>
</el-select> </el-select>
@@ -145,6 +145,31 @@ const loading = ref(false)
const allDataList: any = ref([]) const allDataList: any = ref([])
const xAixsTimeList: any = ref([]) const xAixsTimeList: any = ref([])
const formatChartValueText = (val: unknown, digits = 2) => {
if (val == null || val === '') return '-'
const num = Number(val)
if (Number.isNaN(num)) return '-'
return num.toFixed(digits)
}
const axisPointerLabelFormatter = (params: any) => {
if (params.axisDimension === 'x') {
return params.value ?? '-'
}
return formatChartValueText(params.value)
}
const tooltipFormatter = (params: any) => {
const list = Array.isArray(params) ? params : [params]
if (!list.length) return ''
let html = `${list[0].axisValueLabel ?? list[0].name ?? ''}`
list.forEach((item: any) => {
const rawValue = Array.isArray(item.value) ? item.value[1] : item.value
html += `<br/>${item.marker}${item.seriesName}: ${formatChartValueText(rawValue)}`
})
return html
}
const resetData = (dataLevel: string) => { const resetData = (dataLevel: string) => {
loading.value = true loading.value = true
indexOptions.value = [ indexOptions.value = [
@@ -259,7 +284,7 @@ const resetData = (dataLevel: string) => {
resetData('Primary') resetData('Primary')
const mqttMessage: any = ref() const mqttMessage: any = ref()
const setHarmonicSpectrumData = (val: any) => { const setHarmonicSpectrumData = (val: any) => {
mqttMessage.value = val mqttMessage.value = val
init() init()
} }
@@ -285,7 +310,7 @@ const init = () => {
// 清空当前时间点的数据(如果存在),防止重复 // 清空当前时间点的数据(如果存在),防止重复
const currentTime = mqttMessage.value.dataTime.split(" ")[1]; const currentTime = mqttMessage.value.dataTime.split(" ")[1];
vv.yMethodList = vv.yMethodList.filter((point: any) => point[0] !== currentTime); vv.yMethodList = vv.yMethodList.filter((point: any) => point[0] !== currentTime);
item.data.push({ item.data.push({
// time: mqttMessage.value.dataTime, // time: mqttMessage.value.dataTime,
value: mqttMessage.value[vv.name], value: mqttMessage.value[vv.name],
@@ -295,7 +320,7 @@ const init = () => {
vv.data.push(mqttMessage.value[vv.name]) vv.data.push(mqttMessage.value[vv.name])
// 更新yMethodList数据确保与xAixsTimeList保持同步 // 更新yMethodList数据确保与xAixsTimeList保持同步
vv.yMethodList.push([currentTime, mqttMessage.value[vv.name] + '', vv.phase]) vv.yMethodList.push([currentTime, mqttMessage.value[vv.name] + '', vv.phase])
// 限制数据点数量,避免过多数据点导致性能问题和显示重复 // 限制数据点数量,避免过多数据点导致性能问题和显示重复
if (vv.yMethodList.length > 50) { if (vv.yMethodList.length > 50) {
vv.yMethodList.shift() vv.yMethodList.shift()
@@ -320,11 +345,14 @@ const init = () => {
// right: '4%' // right: '4%'
// }, // },
tooltip: { tooltip: {
trigger: 'axis',
formatter: tooltipFormatter,
axisPointer: { axisPointer: {
type: 'cross', type: 'cross',
label: { label: {
color: '#fff', color: '#fff',
fontSize: 16 fontSize: 16,
formatter: axisPointerLabelFormatter
} }
}, },
textStyle: { textStyle: {

View File

@@ -1,8 +1,8 @@
<!-- 实时数据 - 谐波频谱页面 --> <!-- 实时数据 - 谐波频谱页面 -->
<template> <template>
<div class="realtrend" v-loading="loading"> <div class="realtrend" v-loading="loading">
<div class="select" v-if="!loading"> <div class="select" v-if="!loading && !isInterharmonicTab">
<div class="mr10">次数</div> <div class="mr10">谐波次数</div>
<el-select v-model.trim="selectValue" style="width: 100px" @change="selectChange"> <el-select v-model.trim="selectValue" style="width: 100px" @change="selectChange">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
@@ -29,17 +29,19 @@
</div> --> </div> -->
<div class="realtrend_table" v-if="Object.keys(tableData).length != 0"> <div class="realtrend_table" v-if="Object.keys(tableData).length != 0">
<div class="thead_left"> <div class="thead_left">
<p style="font-weight: 700; background-color: #f3f6f9">次数()</p> <p style="font-weight: 700; background-color: #f3f6f9">
{{ isInterharmonicTab ? '间谐波次数(次)' : '谐波次数(次)' }}
</p>
<p>{{ item.groupName }}{{ item.unit ? '(' + item.unit + ')' : '' }}</p> <p>{{ item.groupName }}{{ item.unit ? '(' + item.unit + ')' : '' }}</p>
<!-- <p>国标限值{{ item.unit ? '(' + item.unit + ')' : '' }}</p> --> <!-- <p>国标限值{{ item.unit ? '(' + item.unit + ')' : '' }}</p> -->
</div> </div>
<div class="thead_right"> <div class="thead_right">
<div class="right_cell" v-for="(value, key, index) in tableData" :key="index"> <div class="right_cell" v-for="(value, key, index) in tableData" :key="index">
<p v-if="item.groupName.includes('间谐波')" style="background-color: #f3f6f9"> <p v-if="isInterharmonicTab" style="background-color: #f3f6f9">
{{ Number(String(key).replace('data', ' ')) - 0.5 }} {{ getHarmonicOrder(key, true) }}
</p> </p>
<p v-else style="background-color: #f3f6f9"> <p v-else style="background-color: #f3f6f9">
<span>{{ String(key).replace('data', ' ') }}</span> <span>{{ getHarmonicOrder(key) }}</span>
</p> </p>
<p> <p>
<span <span
@@ -78,14 +80,19 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, defineEmits } from 'vue' import { ref, onMounted, computed } from 'vue'
import { VxeGridProps } from 'vxe-table' import { VxeGridProps } from 'vxe-table'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
import MyEchart from '@/components/echarts/MyEchart.vue' import MyEchart from '@/components/echarts/MyEchart.vue'
const activeName = ref(0) const activeName = ref(0)
const emit = defineEmits(['changeTrendType']) const emit = defineEmits(['changeTrendType'])
const tableList: any = [] const tableList: any = []
const selectValue = ref('1') const selectValue = ref('3')
const getHarmonicOrder = (key: string | number, interharmonic = false) => {
const order = Number(String(key).replace('data', ''))
return interharmonic ? order - 0.5 : order
}
const options = [ const options = [
{ {
@@ -142,6 +149,9 @@ const tabsList: any = ref([
unit: '%' unit: '%'
} }
]) ])
const isInterharmonicTab = computed(() => {
return tabsList.value[activeName.value]?.groupName?.includes('间谐波')
})
const loading: any = ref(true) const loading: any = ref(true)
//接收参数 //接收参数
const params = ref({}) const params = ref({})
@@ -208,9 +218,9 @@ const init = () => {
value: limitData.value[key] value: limitData.value[key]
}) })
} }
if (selectValue.value == '1') { if (!isInterharmonicTab.value && selectValue.value == '1') {
gbData.value = gbData.value.filter((_, index: number) => index % 2 !== 0) gbData.value = gbData.value.filter((_, index: number) => index % 2 !== 0)
} else if (selectValue.value == '2') { } else if (!isInterharmonicTab.value && selectValue.value == '2') {
gbData.value = gbData.value.filter((_, index: number) => index % 2 == 0) gbData.value = gbData.value.filter((_, index: number) => index % 2 == 0)
} }
let xAxisList: any = [] let xAxisList: any = []
@@ -229,9 +239,10 @@ const init = () => {
echartsData.value = { echartsData.value = {
color: ['#2E8B57', '#DAA520'], color: ['#2E8B57', '#DAA520'],
xAxis: { xAxis: {
name: '次数', name: isInterharmonicTab.value ? '间谐波次数' : '谐波次数',
data: trendData.map((item: any) => { data: trendData.map((item: any) => {
return (activeName.value == 2 ? item.count - 0.5 : item.count) + '次' const count = isInterharmonicTab.value ? item.count - 0.5 : item.count
return `${count}次`
}) })
}, },
yAxis: { yAxis: {
@@ -278,6 +289,19 @@ const handleClick = (tab: any, event: any) => {
//获取mqtt传送的实时数据 //获取mqtt传送的实时数据
const mqttMessage: any = ref() const mqttMessage: any = ref()
const tableData: any = ref({}) const tableData: any = ref({})
const shouldIncludeHarmonicData = (numberPart: number) => {
if (isInterharmonicTab.value) {
return numberPart < 17
}
if (selectValue.value === '3') {
return true
}
if (selectValue.value === '2') {
return numberPart % 2 === 0
}
return numberPart % 2 !== 0
}
const setRealTrendData = (val: any) => { const setRealTrendData = (val: any) => {
mqttMessage.value = {} mqttMessage.value = {}
if (!val) { if (!val) {
@@ -289,44 +313,14 @@ const setRealTrendData = (val: any) => {
for (let key in val) { for (let key in val) {
if (String(key).includes('data') && String(key) != 'dataLevel' && String(key) != 'dataTime') { if (String(key).includes('data') && String(key) != 'dataLevel' && String(key) != 'dataTime') {
const numberPart = parseInt(key.replace('data', '')) const numberPart = parseInt(key.replace('data', ''))
if (selectValue.value != '3') { if (shouldIncludeHarmonicData(numberPart)) {
if (selectValue.value == '2') { tableData.value[key] = val[key].toFixed(2)
if (activeName.value == 2) {
if (numberPart % 2 !== 0 && numberPart < 17) {
tableData.value[key] = val[key].toFixed(2)
}
} else {
if (numberPart % 2 === 0) {
tableData.value[key] = val[key].toFixed(2)
}
}
} else {
if (activeName.value == 2) {
if (numberPart % 2 === 0 && numberPart < 17) {
tableData.value[key] = val[key].toFixed(2)
}
} else {
if (numberPart % 2 !== 0) {
tableData.value[key] = val[key].toFixed(2)
}
}
}
} else {
if (activeName.value == 2) {
if (numberPart < 17) {
tableData.value[key] = val[key].toFixed(2)
}
} else {
tableData.value[key] = val[key].toFixed(2)
}
} }
} }
} }
if (!tabsList.value[activeName.value].groupName.includes('间谐波')) { if (!isInterharmonicTab.value) {
delete tableData.value.data1 delete tableData.value.data1
} else {
// console.log('不删除')
} }
if (Object.keys(tableData.value).length != 0) { if (Object.keys(tableData.value).length != 0) {
init() init()
@@ -335,11 +329,11 @@ const setRealTrendData = (val: any) => {
emit('changeTrendType', activeName.value) emit('changeTrendType', activeName.value)
} }
} }
const selectChange = (val: any) => { const selectChange = () => {
loading.value = true if (isInterharmonicTab.value) return
// setTimeout(() => { if (mqttMessage.value && Object.keys(mqttMessage.value).length) {
// loading.value=false setRealTrendData(mqttMessage.value)
// },3000) }
} }
//获取国标限值 //获取国标限值
const limitData: any = ref() const limitData: any = ref()

View File

@@ -1,18 +1,25 @@
<template> <template>
<div class="view"> <div class="view">
<div v-show="!isWaveCharts"> <div v-show="!isWaveCharts">
<TableHeader datePicker ref="headerRef" :showReset="false"></TableHeader> <TableHeader datePicker showExport ref="headerRef" @onResetForm="onResetForm">
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
</TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" />
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
@confirm="onFilterConfirm" />
</div> </div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef" <waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" /> @handleHideCharts="isWaveCharts = false" :wp="wp" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, provide, nextTick, defineEmits } from 'vue' import { ref, onMounted, provide, nextTick, } from 'vue'
import { getTabsDataByType } from '@/api/cs-device-boot/EquipmentDelivery' import { getTabsDataByType } from '@/api/cs-device-boot/EquipmentDelivery'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import waveFormAnalysis from './components/waveFormAnalysis.vue' import waveFormAnalysis from './components/waveFormAnalysis.vue'
import { ArrowLeft, Message } from '@element-plus/icons-vue' import { ArrowLeft, Message } from '@element-plus/icons-vue'
@@ -20,6 +27,7 @@ import { ElMessage } from 'element-plus'
import { analyseWave, getFileByEventId } from '@/api/common' import { analyseWave, getFileByEventId } from '@/api/common'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend' import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
const emit=defineEmits(['fileRecall'])
const tableParams: any = ref({}) const tableParams: any = ref({})
const refheader = ref() const refheader = ref()
const view = ref(true) const view = ref(true)
@@ -31,7 +39,7 @@ const boxoList: any = ref([])
const wp = ref({}) const wp = ref({})
const value = ref(1) const value = ref(1)
const waveFormAnalysisRef = ref() const waveFormAnalysisRef = ref()
const headerRef = ref() const multiConditionRef = ref()
const props = defineProps({ const props = defineProps({
deviceType: { deviceType: {
@@ -46,6 +54,7 @@ const tableStore: any = new TableStore({
url: '/cs-device-boot/csGroup/deviceDataByType', url: '/cs-device-boot/csGroup/deviceDataByType',
publicHeight: 215, publicHeight: 215,
method: 'POST', method: 'POST',
exportName: '暂态事件',
column: [ column: [
// { width: '60', type: 'checkbox', fixed: 'left' }, // { width: '60', type: 'checkbox', fixed: 'left' },
{ {
@@ -91,15 +100,20 @@ const tableStore: any = new TableStore({
return row.cellValue || '-' return row.cellValue || '-'
} }
}, },
{ field: 'showName', title: '触发类型', minWidth: 120 }, {
title: '严重度', field: 'severity', minWidth: 80, align: 'center', sortable: true, formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/'
}
},
{ field: 'showName', title: '触发类型', minWidth: 100 },
{ {
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知' return ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ {
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知' return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, }, { title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
@@ -161,16 +175,16 @@ const tableStore: any = new TableStore({
}) })
} }
}, },
{ // {
name: 'edit', // name: 'edit',
text: '暂无波形', // text: '暂无波形',
type: 'info', // type: 'info',
icon: 'el-icon-DataLine', // icon: 'el-icon-DataLine',
render: 'basicButton', // render: 'basicButton',
disabled: row => { // disabled: row => {
return row.wavePath // return row.wavePath
} // }
}, // },
{ {
name: 'edit', name: 'edit',
title: '波形下载', title: '波形下载',
@@ -207,14 +221,24 @@ const tableStore: any = new TableStore({
type: 'primary', type: 'primary',
icon: 'el-icon-Check', icon: 'el-icon-Check',
render: 'basicButton', render: 'basicButton',
loading: 'loading3',
disabled: row => { disabled: row => {
return !(props.deviceType == '2' && row.wavePath == null) return !(row.wavePath == null)
}, },
click: row => { click: row => {
getFileByEventId(row.id).then(res => { if (props.deviceType == '2') {
ElMessage.success(res.message) row.loading3 = true
tableStore.index() getFileByEventId(row.id).then(res => {
}) ElMessage.success(res.message)
tableStore.index()
row.loading3 = false
}).catch(() => {
row.loading3 = false
})
}else if(props.deviceType == '1'){
emit('fileRecall', row.id)
}
} }
} }
] ]
@@ -229,6 +253,27 @@ const tableStore: any = new TableStore({
}, },
loadCallback: () => { } loadCallback: () => { }
}) })
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
// 重置
const onResetForm = () => {
filterVisible.value = false
tableStore.table.params.cascader = ''
tableStore.table.params.deviceTypeId = ''
tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = ''
tableStore.table.params.deviceId = ''
tableStore.table.params.deviceTypeName = ''
multiConditionRef.value?.reset()
}
const filterVisible = ref(false)
provide('tableStore', tableStore) provide('tableStore', tableStore)
const isWaveCharts = ref(false) const isWaveCharts = ref(false)
//获取请求参数 //获取请求参数
@@ -237,17 +282,18 @@ const getTableParams = (val: any) => {
isWaveCharts.value = false isWaveCharts.value = false
tableStore.index() tableStore.index()
} }
//返回 const onFilterConfirm = () => {
const handleBack = async () => { tableStore.onTableAction('search', {})
isWaveCharts.value = false
emit('activeTabsType', '')
await tableStore.index()
} }
const openFilterDialog = () => {
filterVisible.value = true
}
defineExpose({ getTableParams }) defineExpose({ getTableParams })
onMounted(() => { onMounted(() => {
console.log('🚀 ~ props.deviceType:', props.deviceType)
tableStore.index()
// tableStore.index()
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -1,7 +1,16 @@
<template> <template>
<div> <div>
<div v-show="!isWaveCharts"> <div v-show="!isWaveCharts">
<TableHeader showExport ref="headerRef" @onResetForm="onResetForm">
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
</TableHeader>
<Table ref="tableRef" /> <Table ref="tableRef" />
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
@confirm="onFilterConfirm" />
<!-- 补召日志 -->
<analysisList ref="analysisListRef"></analysisList>
</div> </div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef" <waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" /> @handleHideCharts="isWaveCharts = false" :wp="wp" />
@@ -20,10 +29,14 @@ import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue' import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'; import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue';
import { analyseWave } from '@/api/common' import { analyseWave, getFileByEventId } from '@/api/common'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend' import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree'
import analysisList from '@/views/govern/device/control/analysisList/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
const props = defineProps({ const props = defineProps({
activeName: String, activeName: String,
@@ -33,10 +46,14 @@ const loading = ref(false)
const dictData = useDictData() const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason') const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type') const EventTypeList: any = dictData.getBasicData('Event_Type')
const DeviceType = ref([])
const waveFormAnalysisRef = ref() const waveFormAnalysisRef = ref()
const isWaveCharts = ref(false) const isWaveCharts = ref(false)
const boxoList: any = ref([]) const boxoList: any = ref([])
const analysisListRef = ref()
const wp = ref({}) const wp = ref({})
const filterVisible = ref(false)
const multiConditionRef = ref()
const tableStore = new TableStore({ const tableStore = new TableStore({
url: '/cs-harmonic-boot/data/getEventByItem', url: '/cs-harmonic-boot/data/getEventByItem',
method: 'POST', method: 'POST',
@@ -72,6 +89,11 @@ const tableStore = new TableStore({
return row.cellValue return row.cellValue
}, sortable: true }, sortable: true
}, },
{
title: '严重度', field: 'severity', minWidth: 80, align: 'center', sortable: true, formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/'
}
},
{ {
field: 'phaseType', field: 'phaseType',
title: '相别', title: '相别',
@@ -83,15 +105,15 @@ const tableStore = new TableStore({
}, },
{ field: 'showName', title: '触发类型', minWidth: 170 }, { field: 'showName', title: '触发类型', minWidth: 100 },
{ {
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知' return ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ {
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => { title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知' return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
} }
}, },
{ title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' }, { title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
@@ -150,16 +172,16 @@ const tableStore = new TableStore({
}) })
} }
}, },
{ // {
name: 'edit', // name: 'edit',
text: '暂无波形', // text: '暂无波形',
type: 'info', // type: 'info',
icon: 'el-icon-DataLine', // icon: 'el-icon-DataLine',
render: 'basicButton', // render: 'basicButton',
disabled: row => { // disabled: row => {
return row.wavePath // return row.wavePath
} // }
}, // },
{ {
name: 'edit', name: 'edit',
title: '波形下载', title: '波形下载',
@@ -190,7 +212,56 @@ const tableStore = new TableStore({
}) })
} }
} },
{
name: 'edit',
text: '暂不支持补召',
type: 'info',
icon: 'el-icon-DataLine',
render: 'basicButton',
disabled: row => {
let code = DeviceType.value.filter((item: any) => item.id == row.devType)[0]?.code
return !(code == 'Direct_Connected_Device' || code == 'Gateway')
},
},
{
name: 'edit',
text: '波形补召',
type: 'primary',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading2',
disabled: row => {
return row.wavePath
},
click: row => {
let code = DeviceType.value.filter((item: any) => item.id == row.devType)[0]?.code
if (code == 'Portable') {
// 便携式设备
analysisListRef.value &&
analysisListRef.value.open({
lineId: row.lineId,
deviceData: row,
deviceId: row.deviceId
})
} else if (code == 'DEV_CLD') {
row.loading2 = true
// 监测设备
getFileByEventId(row.id).then(res => {
ElMessage.success(res.message)
tableStore.index()
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
}
},
] ]
} }
], ],
@@ -199,6 +270,15 @@ const tableStore = new TableStore({
} }
}) })
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
const setHeight = () => { const setHeight = () => {
if (props.activeColName == '0') { if (props.activeColName == '0') {
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(350, 485) waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(350, 485)
@@ -209,6 +289,23 @@ const setHeight = () => {
tableStore.table.height = mainHeight(240).height tableStore.table.height = mainHeight(240).height
} }
} }
queryByCode('Device_Type').then(res => {
queryCsDictTree(res.data.id).then((list: any) => {
DeviceType.value = list.data
})
})
const onResetForm = () => {
filterVisible.value = false
tableStore.table.params.cascader = ''
tableStore.table.params.deviceTypeId = ''
tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = ''
tableStore.table.params.deviceId = ''
tableStore.table.params.deviceTypeName = ''
multiConditionRef.value?.reset()
}
provide('tableStore', tableStore) provide('tableStore', tableStore)
const init = () => { const init = () => {
tableStore.table.params.id = props.activeName tableStore.table.params.id = props.activeName
@@ -217,6 +314,12 @@ const init = () => {
// }) // })
tableStore.index() tableStore.index()
} }
const onFilterConfirm = () => {
tableStore.onTableAction('search', {})
}
const openFilterDialog = () => {
filterVisible.value = true
}
onMounted(() => { onMounted(() => {
}) })

View File

@@ -59,7 +59,7 @@
<el-input maxlength="32" show-word-limit v-else v-model="form.area" clearable placeholder="请输入区域" /> <el-input maxlength="32" show-word-limit v-else v-model="form.area" clearable placeholder="请输入区域" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="description"> <el-form-item label="备注" prop="description">
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" /> <el-input maxlength="300" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>

View File

@@ -18,7 +18,7 @@
<el-input maxlength="32" show-word-limit v-model="form.area" clearable placeholder="请输入区域" /> <el-input maxlength="32" show-word-limit v-model="form.area" clearable placeholder="请输入区域" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="description"> <el-form-item label="备注" prop="description">
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" /> <el-input maxlength="300" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" /> <el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />

View File

@@ -16,7 +16,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="description"> <el-form-item label="备注" prop="description">
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" /> <el-input maxlength="300" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" /> <el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />

View File

@@ -32,10 +32,10 @@
height="auto" height="auto"
style="width: 100%" style="width: 100%"
> >
<vxe-column field="projectName" title="项目名称"></vxe-column> <vxe-column field="projectName" title="项目名称" minWidth="140px"></vxe-column>
<vxe-column field="projectArea" title="地址"></vxe-column> <vxe-column field="projectArea" title="地址" minWidth="100px"></vxe-column>
<vxe-column field="projectRemark" title="备注"></vxe-column> <vxe-column field="projectRemark" title="备注" minWidth="100px"></vxe-column>
<vxe-column title="拓扑图" width="100px"> <vxe-column title="拓扑图" width="100px">
<template #default="{ row }"> <template #default="{ row }">
<el-image <el-image
@@ -49,7 +49,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="projectSort" title="排序" width="80px"></vxe-column> <vxe-column field="projectSort" title="排序" width="80px"></vxe-column>
<vxe-column title="操作" width="180px"> <vxe-column title="操作" width="130px">
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
style="margin-left: 4px" style="margin-left: 4px"
@@ -137,7 +137,7 @@ const tableStore: any = new TableStore({
{ {
title: '操作', title: '操作',
fixed: 'right', fixed: 'right',
width: '150', width: '130',
render: 'buttons', render: 'buttons',
buttons: [ buttons: [
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
</el-form-item> </el-form-item>
<el-form-item label="描述:"> <el-form-item label="描述:">
<el-input maxlength="32" show-word-limit v-model.trim="versionDesc" disabled type="textarea" :rows="3"></el-input> <el-input maxlength="300" show-word-limit v-model.trim="versionDesc" disabled type="textarea" :rows="3"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@@ -3,12 +3,8 @@
<TableHeader> <TableHeader>
<template v-slot:select> <template v-slot:select>
<el-form-item label="项目名称"> <el-form-item label="项目名称">
<el-input maxlength="32" show-word-limit <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" clearable
placeholder="请输入项目名称"></el-input>
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入项目名称"
></el-input>
</el-form-item> </el-form-item>
</template> </template>
<template v-slot:operation> <template v-slot:operation>
@@ -16,11 +12,8 @@
</template> </template>
</TableHeader> </TableHeader>
<!-- <Table ref="tableRef" /> --> <!-- <Table ref="tableRef" /> -->
<div <div v-if="tableStore.table.data.length != 0" style="overflow-x: hidden; overflow-y: scroll; padding: 0 10px"
style="overflow-x: hidden; overflow-y: scroll; padding: 0 10px" v-loading="tableStore.table.loading" :style="{ height: tableStore.table.height }">
v-loading="tableStore.table.loading"
:style="{ height: tableStore.table.height }"
>
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="6" v-for="item in tableStore.table.data" :key="item.id" class="mt10"> <el-col :span="6" v-for="item in tableStore.table.data" :key="item.id" class="mt10">
<el-card class="box-card" @click="querdata(item)" shadow="hover"> <el-card class="box-card" @click="querdata(item)" shadow="hover">
@@ -28,25 +21,15 @@
<span style="display: flex; align-items: center"> <span style="display: flex; align-items: center">
{{ item.name }} {{ item.name }}
<el-tooltip <el-tooltip class="item" effect="dark" content="修改项目" placement="top"
class="item" v-if="hasAdmin || item.createBy == adminInfo.id">
effect="dark" <Edit style="margin-left: 5px; width: 16px" class="xiaoshou color"
content="修改项目" @click="editd(item)" />
placement="top"
v-if="hasAdmin || item.createBy == adminInfo.id"
>
<Edit
style="margin-left: 5px; width: 16px"
class="xiaoshou color"
@click="editd(item)"
/>
</el-tooltip> </el-tooltip>
</span> </span>
<div style="height: 32px"> <div style="height: 32px">
<div <div style="display: flex; justify-content: end"
style="display: flex; justify-content: end" v-if="hasAdmin || item.createBy == adminInfo.id">
v-if="hasAdmin || item.createBy == adminInfo.id"
>
<!-- <el-button <!-- <el-button
class="color" class="color"
icon="el-icon-Promotion" icon="el-icon-Promotion"
@@ -57,54 +40,36 @@
> >
绑定 绑定
</el-button> --> </el-button> -->
<el-button <el-button class="color" icon="el-icon-Share" style="padding: 3px 0" type="text"
class="color" @click="Aclick(item)">
icon="el-icon-Share"
style="padding: 3px 0"
type="text"
@click="Aclick(item)"
>
设计 设计
</el-button> </el-button>
<!-- <el-button icon="el-icon-share" style="padding: 3px 0; color: green" <!-- <el-button icon="el-icon-share" style="padding: 3px 0; color: green"
type="text" @click="shejid(item)">设计</el-button> --> type="text" @click="shejid(item)">设计</el-button> -->
<!-- <el-button icon="el-icon-edit" style="padding: 3px 0; color: blue" type="text" <!-- <el-button icon="el-icon-edit" style="padding: 3px 0; color: blue" type="text"
@click="shejid(item)">编辑</el-button> --> @click="shejid(item)">编辑</el-button> -->
<el-button <el-button icon="el-icon-Delete" style="padding: 3px 0; color: red" type="text"
icon="el-icon-Delete" @click="deleted(item)">
style="padding: 3px 0; color: red"
type="text"
@click="deleted(item)"
>
删除 删除
</el-button> </el-button>
</div> </div>
</div> </div>
</div> </div>
<img <img v-if="item.fileContent" :src="item.fileContent" class="image xiaoshou"
v-if="item.fileContent" @click="imgData(item)" />
:src="item.fileContent"
class="image xiaoshou"
@click="imgData(item)"
/>
<el-empty v-else description="暂无设计" style="height: 220px" /> <el-empty v-else description="暂无设计" style="height: 220px" />
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<div class="table-pagination"> <el-empty v-else description="暂无数据" :style="{ height: tableStore.table.height }" />
<el-pagination
:currentPage="tableStore.table.params!.pageNum"
:page-size="tableStore.table.params!.pageSize"
:page-sizes="[10, 20, 50, 100]"
background
:layout="'sizes,total, ->, prev, pager, next, jumper'"
:total="tableStore.table.total"
@size-change="onTableSizeChange"
@current-change="onTableCurrentChange"
></el-pagination>
</div>
<div class="table-pagination">
<el-pagination :currentPage="tableStore.table.params!.pageNum"
:page-size="tableStore.table.params!.pageSize" :page-sizes="[10, 20, 50, 100]" background
:layout="'sizes,total, ->, prev, pager, next, jumper'" :total="tableStore.table.total"
@size-change="onTableSizeChange" @current-change="onTableCurrentChange"></el-pagination>
</div>
<popup ref="popupRef" @submit="tableStore.index()" /> <popup ref="popupRef" @submit="tableStore.index()" />
</div> </div>
</template> </template>
@@ -142,7 +107,7 @@ const tableStore = new TableStore({
method: 'POST', method: 'POST',
publicHeight: 60, publicHeight: 60,
column: [], column: [],
loadCallback: () => {} loadCallback: () => { }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.searchValue = '' tableStore.table.params.searchValue = ''
@@ -179,14 +144,14 @@ const activate = (e: any) => {
} }
}) })
}) })
.catch(() => {}) .catch(() => { })
} }
const getBindId = () => { const getBindId = () => {
getByUserId({ userId: adminInfo.id }).then(res => { getByUserId({ userId: adminInfo.id }).then(res => {
bindId.value = res.data.pageId bindId.value = res.data.pageId
}) })
} }
const querdata = (e: any) => {} const querdata = (e: any) => { }
const editd = (e: any) => { const editd = (e: any) => {
popupRef.value.open({ popupRef.value.open({
title: '修改项目', title: '修改项目',
@@ -198,7 +163,7 @@ const Aclick = (e: any) => {
//window.open(window.location.origin + `/zutai/?id=${e.id}&&name=${e.name}&&preview=false&&graphicDisplay=zl`) //window.open(window.location.origin + `/zutai/?id=${e.id}&&name=${e.name}&&preview=false&&graphicDisplay=zl`)
window.open( window.open(
window.location.origin + window.location.origin +
`/zutai/?id=${e.id}&&name=${e.name}&&preview=false&&graphicDisplay=${VITE_FLAG ? 'ypt' : 'zl'}` `/zutai/?id=${e.id}&&name=${e.name}&&preview=false&&graphicDisplay=${VITE_FLAG ? 'ypt' : 'zl'}`
) )
} }
// 删除 // 删除
@@ -235,9 +200,8 @@ const deleted = (e: any) => {
const imgData = (e: any) => { const imgData = (e: any) => {
window.open( window.open(
window.location.origin + window.location.origin +
`/zutai/?id=${e.id}&&name=${e.name}&&preview=true&&graphicDisplay=${VITE_FLAG ? 'ypt' : 'zl'}#/preview_${ `/zutai/?id=${e.id}&&name=${e.name}&&preview=true&&graphicDisplay=${VITE_FLAG ? 'ypt' : 'zl'}#/preview_${VITE_FLAG ? 'YPT' : 'ZL'
VITE_FLAG ? 'YPT' : 'ZL' }`
}`
) )
// window.open('http://192.168.1.128:4001' + `/zutai/?id=${e.id}&&name=${e.name}&&preview=true&&graphicDisplay=zl#/preview_ZL`) // window.open('http://192.168.1.128:4001' + `/zutai/?id=${e.id}&&name=${e.name}&&preview=true&&graphicDisplay=zl#/preview_ZL`)
} }
@@ -311,7 +275,7 @@ span {
// 不可全选样式 // 不可全选样式
.el-tree-node { .el-tree-node {
.is-leaf + .el-checkbox .el-checkbox__inner { .is-leaf+.el-checkbox .el-checkbox__inner {
display: inline-block; display: inline-block;
} }

View File

@@ -1,223 +1,223 @@
<template> <template>
<div class="default-main report-zl-page" :style="height"> <div class="default-main report-zl-page" :style="height">
<div class="report-zl-sidebar"> <div class="report-zl-sidebar">
<!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick" <!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil"> @Policy="stencil">
</pointTreeWx> --> </pointTreeWx> -->
<CloudDeviceEntryTree ref="TerminalRef" template @Policy="stencil" @node-click="handleNodeClick" <CloudDeviceEntryTree ref="TerminalRef" template @Policy="stencil" @node-click="handleNodeClick"
@init="handleNodeClick"></CloudDeviceEntryTree> @init="handleNodeClick"></CloudDeviceEntryTree>
</div> </div>
<div class="report-zl-main"> <div class="report-zl-main">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false"> <TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select> <template v-slot:select>
<!-- <el-form-item label="时间:"> <!-- <el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker> <DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> --> </el-form-item> -->
<el-form-item label="模板策略"> <el-form-item label="模板策略">
<el-select v-model.trim="Template" @change="changetype" placeholder="请选择模版" value-key="id"> <el-select v-model.trim="Template" @change="changetype" placeholder="请选择模版" value-key="id">
<el-option v-for="item in templatePolicy" :key="item.id" :label="item.excelName" <el-option v-for="item in templatePolicy" :key="item.id" :label="item.excelName"
:value="item"></el-option> :value="item"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- <el-form-item label="监测对象"> <!-- <el-form-item label="监测对象">
<el-select <el-select
filterable filterable
v-model="tableStore.table.params.sensitiveUserId" v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象" placeholder="请选择监测对象"
clearable clearable
> >
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
</template> </template>
<template #operation> <template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">报表生成</el-button> <el-button icon="el-icon-Download" type="primary" @click="exportEvent">报表生成</el-button>
</template> </template>
</TableHeader> </TableHeader>
<div class="box report-zl-box"> <div class="box report-zl-box">
<div class="report-zl-sheet-wrap"> <div class="report-zl-sheet-wrap">
<div <div
id="luckysheet" id="luckysheet"
class="report-zl-sheet" class="report-zl-sheet"
v-loading="tableStore.table.loading" v-loading="tableStore.table.loading"
element-loading-background="rgba(255, 255, 255, 0.85)" element-loading-background="rgba(255, 255, 255, 0.85)"
></div> ></div>
<el-empty <el-empty
v-show="!tableStore.table.loading && tableStore.table.data.length === 0" v-show="!tableStore.table.loading && tableStore.table.data.length === 0"
class="report-zl-empty" class="report-zl-empty"
description="暂无数据" description="暂无数据"
/> />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted, ref, provide } from 'vue' import { onMounted, onUnmounted, ref, provide } from 'vue'
import TableStore from '@/utils/tableStore' import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue' import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue' import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData' import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper' import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils' import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import DatePicker from '@/components/form/datePicker/time.vue' import DatePicker from '@/components/form/datePicker/time.vue'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue' import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit' import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
// import data from './123.json' // import data from './123.json'
defineOptions({ defineOptions({
name: 'govern/reportCore/statisticsWx/indexZL' name: 'govern/reportCore/statisticsWx/indexZL'
}) })
const height = mainHeight(20) const height = mainHeight(20)
const dictData = useDictData() const dictData = useDictData()
const TableHeaderRef = ref() const TableHeaderRef = ref()
const dotList: any = ref({}) const dotList: any = ref({})
const Template: any = ref({}) const Template: any = ref({})
const reportForm: any = ref('') const reportForm: any = ref('')
const datePickerRef = ref() const datePickerRef = ref()
const templatePolicy: any = ref([]) const templatePolicy: any = ref([])
const name = ref('') const name = ref('')
const tableStore = new TableStore({ const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getSensitiveUserReport', url: '/cs-harmonic-boot/customReport/getSensitiveUserReport',
method: 'POST', method: 'POST',
column: [], column: [],
showPage: false, showPage: false,
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id tableStore.table.params.lineId = dotList.value.id
tableStore.table.params.sensitiveUserId = dotList.value.id tableStore.table.params.sensitiveUserId = dotList.value.id
// ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]), // ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]),
// (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]), // (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]),
if (!tableStore.table.params.tempId) { if (!tableStore.table.params.tempId) {
ElMessage.warning('请选择模板') ElMessage.warning('请选择模板')
return false return false
} }
if (!dotList.value?.id) { if (!dotList.value?.id) {
ElMessage.warning('请选择监测点') ElMessage.warning('请选择监测点')
return false return false
} }
destroyLuckysheet() destroyLuckysheet()
delete tableStore.table.params.searchBeginTime delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag delete tableStore.table.params.timeFlag
}, },
loadCallback: () => { loadCallback: () => {
name.value = dotList.value.name name.value = dotList.value.name
if (tableStore.table.data.length > 0) { if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false }) renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
} else { } else {
destroyLuckysheet() destroyLuckysheet()
} }
} }
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1 tableStore.table.params.resourceType = 1
tableStore.table.params.customType = 1 tableStore.table.params.customType = 1
const flag = ref(true) const flag = ref(true)
onMounted(() => { onMounted(() => {
initListByIds() initListByIds()
}) })
onUnmounted(() => { onUnmounted(() => {
destroyLuckysheet() destroyLuckysheet()
}) })
const idList = ref([]) const idList = ref([])
// 监测对象 // 监测对象
const initListByIds = () => { const initListByIds = () => {
getListByIds({}).then((res: any) => { getListByIds({}).then((res: any) => {
if (res.data.length > 0) { if (res.data.length > 0) {
idList.value = res.data idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) { if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id tableStore.table.params.sensitiveUserId = idList.value[0].id
} }
// templateListData() // templateListData()
} }
}) })
} }
const stencil = (val: any) => { const stencil = (val: any) => {
templatePolicy.value = val.filter((item: any) => item.excelType == '4') templatePolicy.value = val.filter((item: any) => item.excelType == '4')
Template.value = templatePolicy.value[0] Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType reportForm.value = templatePolicy.value[0]?.excelType
} }
const changetype = (val: any) => { const changetype = (val: any) => {
reportForm.value = val.excelType reportForm.value = val.excelType
} }
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
if (isLineTreeLeaf(data) || data?.level == 3) { if (isLineTreeLeaf(data) || data?.level == 3) {
dotList.value = data dotList.value = data
setTimeout(() => { setTimeout(() => {
tableStore.index() tableStore.index()
}, 500) }, 500)
} else { } else {
tableStore.table.loading = false tableStore.table.loading = false
} }
} }
const exportEvent = () => { const exportEvent = () => {
const now = new Date() const now = new Date()
const year = now.getFullYear() // 4位年份 const year = now.getFullYear() // 4位年份
const month = now.getMonth() + 1 // 月份0-11需+1 const month = now.getMonth() + 1 // 月份0-11需+1
const day = now.getDate() // 日期1-31 const day = now.getDate() // 日期1-31
// 格式化YYYY - MM - DD补零 // 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}` const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0) exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.report-zl-page { .report-zl-page {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
} }
.report-zl-sidebar { .report-zl-sidebar {
width: 280px; width: 280px;
flex-shrink: 0; flex-shrink: 0;
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
} }
.report-zl-main { .report-zl-main {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
background: #fff; background: #fff;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
} }
.box { .box {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
padding: 10px 0; padding: 10px 0;
overflow: hidden; overflow: hidden;
} }
.report-zl-box { .report-zl-box {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.report-zl-sheet-wrap { .report-zl-sheet-wrap {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
position: relative; position: relative;
} }
.report-zl-sheet { .report-zl-sheet {
height: 100%; height: 100%;
} }
.report-zl-empty { .report-zl-empty {
position: absolute; position: absolute;
inset: 0; inset: 0;
height: 100%; height: 100%;
background: #fff; background: #fff;
} }
</style> </style>

View File

@@ -163,7 +163,7 @@
<el-input maxlength="32" show-word-limit v-model="formData.sort" placeholder="请输入排序"></el-input> <el-input maxlength="32" show-word-limit v-model="formData.sort" placeholder="请输入排序"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="描述:" prop="remark" class="top"> <el-form-item label="描述:" prop="remark" class="top">
<el-input maxlength="32" show-word-limit <el-input maxlength="300" show-word-limit
v-model="formData.remark" v-model="formData.remark"
:autosize="{ minRows: 2, maxRows: 4 }" :autosize="{ minRows: 2, maxRows: 4 }"
type="textarea" type="textarea"

View File

@@ -13,7 +13,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
<el-input maxlength="32" show-word-limit v-model.trim="form.remark" placeholder="请输入备注" /> <el-input maxlength="300" show-word-limit v-model.trim="form.remark" placeholder="请输入备注" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">

View File

@@ -61,13 +61,14 @@
v-if="(item as LayoutItem).component" v-if="(item as LayoutItem).component"
:key="(item as LayoutItem).i" :key="(item as LayoutItem).i"
class="pd10" class="pd10"
:timeValue="datePickerRef?.timeValue || 3" :timeValue="datePickerRef?.timeValue"
:height="rowHeight * item.h - seRowHeight(item.h) + 'px'" :height="rowHeight * item.h - seRowHeight(item.h) + 'px'"
:width="rowWidth * item.w - 30 + 'px'" :width="rowWidth * item.w - 30 + 'px'"
:timeKey="(item as LayoutItem).timeKeys" :timeKey="(item as LayoutItem).timeKeys"
:interval="datePickerRef?.interval" :interval="datePickerRef?.interval"
:w="item.w" :w="item.w"
:h="item.h" :h="item.h"
:flag="flag"
/> />
<div v-else class="pd10">组件加载失败...</div> <div v-else class="pd10">组件加载失败...</div>
</div> </div>

View File

@@ -28,10 +28,10 @@
<IconSelector v-model="form.icon" placeholder="请选择图标" /> <IconSelector v-model="form.icon" placeholder="请选择图标" />
</el-form-item> </el-form-item>
<el-form-item class="top" label="组件标识" prop="code"> <el-form-item class="top" label="组件标识" prop="code">
<el-input maxlength="32" show-word-limit v-model="form.code" placeholder="请输入组件菜单选取"></el-input> <el-input v-model="form.code" placeholder="请输入组件菜单选取"></el-input>
</el-form-item> </el-form-item>
<el-form-item class="top" label="组件路径" prop="path"> <el-form-item class="top" label="组件路径" prop="path">
<el-input maxlength="32" show-word-limit v-model="form.path" placeholder="请输入组件路径"></el-input> <el-input v-model="form.path" placeholder="请输入组件路径"></el-input>
</el-form-item> </el-form-item>
<el-form-item class="top" label="组件查询时间" prop="timeKeys"> <el-form-item class="top" label="组件查询时间" prop="timeKeys">
<el-checkbox-group v-model="form.timeKeys"> <el-checkbox-group v-model="form.timeKeys">
@@ -44,7 +44,8 @@
</el-form-item> </el-form-item>
<el-form-item class="top" label="组件排序" prop="sort"> <el-form-item class="top" label="组件排序" prop="sort">
<el-input maxlength="32" show-word-limit v-model.number="form.sort" placeholder="请输入组件排序"></el-input> <!-- <el-input maxlength="32" show-word-limit v-model.number="form.sort" placeholder="请输入组件排序"></el-input> -->
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="width: 600px; height: 390px; overflow: hidden"> <div style="width: 600px; height: 390px; overflow: hidden">

View File

@@ -28,7 +28,7 @@
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="描述:" class="top"> <el-form-item label="描述:" class="top">
<el-input maxlength="32" show-word-limit <el-input maxlength="300" show-word-limit
:autosize="{ minRows: 2, maxRows: 4 }" :autosize="{ minRows: 2, maxRows: 4 }"
type="textarea" type="textarea"

View File

@@ -19,7 +19,7 @@
<el-input maxlength="32" show-word-limit style="width: 100%" v-model.number.trim="form.sort" placeholder="请输入排序" /> <el-input maxlength="32" show-word-limit style="width: 100%" v-model.number.trim="form.sort" placeholder="请输入排序" />
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
<el-input maxlength="32" show-word-limit v-model.trim="form.remark" clearable :rows="2" type="textarea" placeholder="请输入备注" /> <el-input maxlength="300" show-word-limit v-model.trim="form.remark" clearable :rows="2" type="textarea" placeholder="请输入备注" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>

File diff suppressed because one or more lines are too long