修改测试问题

This commit is contained in:
guanj
2026-06-11 20:27:37 +08:00
parent bda7373133
commit 1a09c31669
61 changed files with 3393 additions and 2406 deletions

View File

@@ -25,7 +25,6 @@ export function objTree() {
})
}
//云设备录入树
export function getCldTree() {
return createAxios({
@@ -40,4 +39,11 @@ export function lineTree() {
method: 'POST'
})
}
//APF报表树
export function getUserDevTree(data) {
return createAxios({
url: '/cs-harmonic-boot/pqSensitiveUser/getUserDevTree',
method: 'POST',
params: data
})
}

View File

@@ -35,7 +35,7 @@ export const editEquipmentDelivery = (data: any) => {
return createAxios({
url: '/cs-device-boot/EquipmentDelivery/updateEquipmentDelivery',
method: 'POST',
data: data
data
})
}

View File

@@ -106,9 +106,9 @@ const tableStore: any = new TableStore({
text: `F47曲线`
},
legend: {
data: ['可容忍事件', '不可容忍事件'],
itemWidth: 10,
itemHeight: 10,
data: ['分割线', '可容忍事件', '不可容忍事件'],
// itemWidth: 10,
// itemHeight: 10,
itemGap: 15
},
tooltip: {
@@ -132,9 +132,14 @@ const tableStore: any = new TableStore({
formatter: function (a: any) {
var relVal = `<strong>${a.seriesName}</strong><br/>`
relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + Math.floor(a.value[1] * 100) / 100 + '%</font>'
relVal += "<font style='color:" + "'>发生时间" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + Math.floor(a.value[1] * 100) / 100 + '%</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>监测点名称:" + (a.value[4] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>设备名称:" + (a.value[5] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>项目名称:" + (a.value[6] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>工程名称:" + (a.value[7] || '/') + '</font>'
return relVal
}
},
@@ -171,7 +176,7 @@ const tableStore: any = new TableStore({
dataZoom: null,
series: [
{
name: '边界线',
name: '分割线',
type: 'line',
data: [
[0.05, 0],
@@ -184,6 +189,7 @@ const tableStore: any = new TableStore({
[1000, 80]
],
showSymbol: false,
tooltips: {
show: false
}
@@ -268,8 +274,11 @@ function gongfunction(arr: any) {
let time = arr[i].time
let eventId = arr[i].eventId
let lineName = arr[i].lineName
let equipmentName = arr[i].equipmentName
let projectName = arr[i].projectName
let engineeringName = arr[i].engineeringName
// let index =arr[i].eventDetailIndex;
point = [xx, yy, time, eventId, lineName]
point = [xx, yy, time, eventId, lineName, equipmentName, projectName, engineeringName]
if (xx <= 0.003) {
let line = 0

View File

@@ -0,0 +1,439 @@
<template>
<div>
<!--ITIC曲线 -->
<TableHeader ref="TableHeaderRef" :showReset="false" :timeKeyList="prop.timeKey" @selectChange="selectChange"
datePicker v-if="fullscreen"></TableHeader>
<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.gs }}</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>
<my-echart v-loading="tableStore.table.loading" ref="chartRef" class="tall" :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%">
<waveFormAnalysis v-loading="loading" ref="waveFormAnalysisRef" @handleHideCharts="isWaveCharts = false"
:wp="wp" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, computed, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import MyEchart from '@/components/echarts/MyEchart.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import TableHeader from '@/components/table/header/index.vue'
import { analyseWave } from '@/api/common'
import { ElMessage } from 'element-plus'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
})
const TableHeaderRef = ref()
const headerHeight = ref(57)
const dialogTitle = ref('波形分析')
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height
if (datePickerValue && datePickerValue.timeValue) {
// 更新时间参数
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
}
}
// 计算是否全屏展示
const fullscreen = computed(() => {
const w = Number(prop.w)
const h = Number(prop.h)
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
// 执行相应逻辑
return true
} else {
return false
}
})
const echartList = ref()
const chartRef = ref()
// 波形
const isWaveCharts = ref(false)
const loading = ref(false)
const wp = ref({})
const boxoList: any = ref({})
const waveFormAnalysisRef: any = ref(null)
const data = reactive({
name: '事件个数',
gs: 0,
krr: 0,
bkrr: 0
})
const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/csevent/f47Curve',
method: 'POST',
showPage: false,
column: [],
beforeSearchFun: () => {
setTime()
},
loadCallback: () => {
const gongData = gongfunction(tableStore.table.data)
data.gs = tableStore.table.data.length
data.krr = gongData.pointI.length
data.bkrr = gongData.pointIun.length
echartList.value = {
title: {
text: `ITIC曲线`
},
legend: {
data: ['上限', '下限', '可容忍事件', '不可容忍事件'],
// itemWidth: 10,
// itemHeight: 10,
itemGap: 15
},
tooltip: {
trigger: 'item',
show: true,
axisPointer: {
type: 'shadow',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter: function (a: any) {
var relVal = `<strong>${a.seriesName}</strong><br/>`
relVal += "<font style='color:" + "'>发生时间:" + a.value[2] + '</font><br/>'
relVal += "<font style='color:" + "'>特征幅值:" + Math.floor(a.value[1] * 100) / 100 + '%</font><br/>'
relVal += "<font style='color:" + "'>持续时间:" + a.value[0] + 's</font><br/>'
relVal += "<font style='color:" + "'>监测点名称:" + (a.value[4] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>设备名称:" + (a.value[5] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>项目名称:" + (a.value[6] || '/') + '</font><br/>'
relVal += "<font style='color:" + "'>工程名称:" + (a.value[7] || '/') + '</font>'
return relVal
}
},
xAxis: [
{
type: 'log',
min: 0.001,
max: 1000,
splitLine: {
show: false
},
name: 's'
}
],
yAxis: [
{
type: 'value',
splitNumber: 10,
minInterval: 3,
name: '%'
}
],
color: ['#FF8C00', '#00BFFF', 'green', 'red'],
options: {
dataZoom: null,
series: [
{
name: '上限',
type: 'line',
data: [
[0.001, 200],
[0.003, 140],
[0.003, 120],
[0.5, 120],
[0.5, 110],
[10, 110],
[1000, 110]
],
showSymbol: false,
tooltips: {
show: false
}
},
{
name: '下限',
type: 'line',
data: [
[0.02, 0],
[0.02, 70],
[0.5, 70],
[0.5, 80],
[10, 80],
[10, 90],
[1000, 90]
],
showSymbol: false,
tooltips: {
show: false
}
},
{
name: '可容忍事件',
type: 'scatter',
symbol: 'circle',
symbolSize: 8,
data: gongData.pointI,
legendSymbol: 'circle'
},
{
name: '不可容忍事件',
type: 'scatter',
symbol: 'circle',
symbolSize: 8,
data: gongData.pointIun,
legendSymbol: 'rect'
}
]
}
}
}
})
const tableRef = ref()
provide('tableRef', tableRef)
provide('tableStore', tableStore)
const setTime = () => {
const time = getTime(
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
prop.timeKey,
fullscreen.value
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
: prop.timeValue
)
if (Array.isArray(time)) {
tableStore.table.params.searchBeginTime = time[0]
tableStore.table.params.searchEndTime = time[1]
TableHeaderRef.value?.setInterval(time[2] - 0)
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
} else {
console.warn('获取时间失败time 不是一个有效数组')
}
}
function gongfunction(arr: any) {
let standI = 0
let unstandI = 0
let pointIun: any[] = []
let pointI: any[] = []
const total = arr.length
if (total > 0) {
for (let i = 0; i < arr.length; i++) {
const xx = arr[i].persistTime
const yy = arr[i].eventValue
const time = arr[i].time
const eventId = arr[i].eventId
const lineName = arr[i].lineName
let equipmentName = arr[i].equipmentName
let projectName = arr[i].projectName
let engineeringName = arr[i].engineeringName
// let index =arr[i].eventDetailIndex;
const point = [xx, yy, time, eventId, lineName, equipmentName, projectName, engineeringName]
if (xx <= 0.003) {
const line = 230 - 30000 * xx
if (yy > line) {
unstandI++
pointIun.push({
value: point,
itemStyle: { normal: { color: 'red' } }
})
} else {
standI++
pointI.push({
value: point,
itemStyle: { normal: { color: 'green' } }
})
}
} else if (xx <= 0.02) {
if (yy > 120) {
unstandI++
pointIun.push({
value: point,
itemStyle: { normal: { color: 'red' } }
})
} else {
standI++
pointI.push({
value: point,
itemStyle: { normal: { color: 'green' } }
})
}
} else if (xx <= 0.5) {
if (yy > 120 || yy < 70) {
unstandI++
pointIun.push({
value: point,
itemStyle: { normal: { color: 'red' } }
})
} else {
standI++
pointI.push({
value: point,
itemStyle: { normal: { color: 'green' } }
})
}
} else if (xx <= 10) {
if (yy > 110 || yy < 80) {
unstandI++
pointIun.push({
value: point,
itemStyle: { normal: { color: 'red' } }
})
} else {
standI++
pointI.push({
value: point,
itemStyle: { normal: { color: 'green' } }
})
}
} else {
if (yy > 110 || yy < 90) {
unstandI++
pointIun.push({
value: point,
itemStyle: { normal: { color: 'red' } }
})
} else {
standI++
pointI.push({
value: point,
itemStyle: { normal: { color: 'green' } }
})
}
}
}
}
return {
standI,
unstandI,
pointI,
pointIun
}
}
onMounted(() => {
setTimeout(() => {
tableStore.index()
}, 100)
})
// 点击事件处理函数
const handleChartClick = (params: any) => {
if (params.seriesName === '可容忍事件') {
// 处理可容忍事件点击
dialogTitle.value = '可容忍事件波形分析'
handleTolerableEventClick(params)
} else if (params.seriesName === '不可容忍事件') {
dialogTitle.value = '不可容忍事件波形分析'
// 处理不可容忍事件点击
// ElMessage.info(`点击了不可容忍事件: 持续时间${params.value[0]}s, 幅值${params.value[1].toFixed(2)}%`)
handleTolerableEventClick(params)
}
}
// 可容忍事件点击处理函数
const handleTolerableEventClick = async (row: any) => {
loading.value = true
nextTick(() => {
if (waveFormAnalysisRef.value) {
//waveFormAnalysisRef.value.setHeight(false, 360)
// waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
}
})
const messageInstance = ElMessage.info(`正在加载,请稍等...`)
await analyseWave(row.value[3]) //eventId
.then(res => {
row.loading1 = false
if (res != undefined) {
boxoList.value = {
persistTime: row.value[0], //持续时间
featureAmplitude: (row.value[1] / 100), //残余电压
startTime: row.value[2], //时间
lineName: row.value[4] //监测点名称
}
boxoList.value.systemType = 'YPT'
wp.value = res.data
}
isWaveCharts.value = true
loading.value = false
})
.catch(() => {
messageInstance.close()
row.loading1 = false
loading.value = false
})
nextTick(() => {
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
waveFormAnalysisRef.value && waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
})
}
// 不可容忍事件点击处理函数
const handleIntolerableEventClick = (params: any) => {
console.log('不可容忍事件详情:', params)
}
watch(
() => prop.timeKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
(newVal, oldVal) => {
// 当外部时间值变化时,更新表格的时间参数
if (newVal && (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) {
tableStore.table.params.searchBeginTime = newVal[0]
tableStore.table.params.searchEndTime = newVal[1]
tableStore.index()
}
},
{
deep: true
}
)
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>

View File

@@ -1,20 +1,10 @@
<template>
<div>
<!--暂降方向统计 -->
<TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
datePicker
:timeKeyList="prop.timeKey"
v-if="fullscreen"
></TableHeader>
<my-echart
v-loading="tableStore.table.loading"
class="tall"
:options="echartList"
:style="{ width: prop.width, height: `calc(${prop.height} )` }"
/>
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker
:timeKeyList="prop.timeKey" v-if="fullscreen"></TableHeader>
<my-echart v-loading="tableStore.table.loading" class="tall" :options="echartList"
:style="{ width: prop.width, height: `calc(${prop.height} )` }" />
</div>
</template>
<script setup lang="ts">
@@ -126,7 +116,11 @@ const tableStore: any = new TableStore({
title: [
{
text: '暂降方向统计',
left: 'center'
left: 'center',
textStyle: {
color: '#000',
fontSize: '15'
},
},
{
text: total + '次',
@@ -205,6 +199,6 @@ watch(
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>

View File

@@ -81,51 +81,68 @@ const tableStore: any = new TableStore({
{
title: '指标名称',
field: 'name',
minWidth: '90'
minWidth: 120
},
{
title: '越限最高监测点',
field: 'lineName',
minWidth: 120,
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '设备名称', field: 'devName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '项目名称', field: 'projectName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '越限最大值',
field: 'maxValue',
minWidth: '70',
minWidth: 100,
render: 'customTemplate',
customTemplate: (row: any) => {
const extentValue =
row.maxValue !== null && row.maxValue !== undefined && row.maxValue !== ''
? Math.floor(row.maxValue * 100) / 100
: '/'
return `<span style='cursor: pointer;text-decoration: underline;'>${extentValue}</span>`
return extentValue=='/' ? '/' : `<span style='cursor: pointer;text-decoration: underline;'>${extentValue}</span>`
}
},
{
title: '国标限值',
field: 'internationalValue',
minWidth: '60'
minWidth: 100, formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '越限程度(%)',
field: 'extent',
minWidth: '70',
minWidth: 100,
formatter: (row: any) => {
return Math.floor(row.cellValue * 100) / 100
return row.cellValue? Math.floor(row.cellValue * 100) / 100 : '/'
}
},
{
title: '越限时间',
field: 'time',
minWidth: '60',
minWidth: 100,
formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '越限最高监测点',
field: 'lineName',
minWidth: '90',
formatter: (row: any) => {
return row.cellValue || '/'
}
}
],
beforeSearchFun: () => {
setTime()

View File

@@ -1,6 +1,11 @@
<template>
<div>
<div class="device-control">
<!--治理效果报表 -->
<div v-show="fullscreen">
<!-- <PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree> -->
<APFTree :height="flag ? 126 : 70" @node-click="handleNodeClick" template @init="handleNodeClick"></APFTree>
</div>
<div>
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" datePicker
@selectChange="selectChange" v-if="fullscreen">
<template v-slot:select>
@@ -10,25 +15,31 @@
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="监测对象">
<!-- <el-form-item label="监测对象">
<el-select filterable v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象"
clearable>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form-item> -->
</template>
<template v-slot:operation>
<el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出</el-button>
</template>
</TableHeader>
<div style="display: flex" >
<div style="display: flex">
<div id="luckysheet" :style="{
width: `calc(${prop.width} )`,
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - 57px + ${fullscreen ? 0 : 56}px)`
}"></div>
}" v-if="tableStore.table.data.length"></div>
<el-empty description="暂无报表" v-else style="flex: 1" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - 57px + ${fullscreen ? 0 : 56}px)`
}"></el-empty>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, provide, reactive, watch, h, computed, nextTick } from 'vue'
@@ -40,6 +51,7 @@ import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
import { ElMessage } from 'element-plus'
import APFTree from '@/components/tree/govern/APFTree.vue'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
@@ -47,39 +59,38 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const TableHeaderRef = ref()
// 报表模板列表
const templateList = ref()
const templateList = ref([])
// 监测对象
const idList = ref()
// 监测对象
const initListByIds = () => {
getListByIds({}).then((res: any) => {
if (res.data?.length > 0) {
idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id
}
templateListData()
} else {
querySysExcel({}).then(res => {
const handleNodeClick = async (data: any) => {
if (templateList.value.length == 0) {
await querySysExcel({}).then(res => {
templateList.value = res.data.filter(item => item.excelType == 4)
if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
tableStore.table.params.tempId = templateList.value[0].id
}
})
}
if (data?.level == 3 || data?.level == 2) {
tableStore.table.params.sensitiveUserId = data.id
await tableStore.index()
} else {
tableStore.table.loading = false
}
})
}
const templateListData = () => {
querySysExcel({}).then(res => {
templateList.value = res.data.filter(item => item.excelType == 4)
@@ -98,7 +109,7 @@ const downloadExcel = () => {
onMounted(() => {
initListByIds()
// initListByIds()
})
onUnmounted(() => {
destroyLuckysheet()
@@ -199,4 +210,11 @@ watch(
// :deep(.el-select) {
// min-width: 80px;
// }
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog draggable title="趋势图" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="dialogTitle" v-model="dialogVisible" append-to-body width="70%">
<!-- 总体指标占比详情谐波含有率 -->
<div>
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
@@ -8,22 +8,10 @@
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item>
<el-form-item label="统计指标" label-width="80px">
<el-select
multiple
:multiple-limit="2"
collapse-tags
collapse-tags-tooltip
v-model="searchForm.index"
placeholder="请选择统计指标"
@change="onIndexChange($event)"
filterable
>
<el-option
v-for="item in indexOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-select multiple :multiple-limit="2" collapse-tags collapse-tags-tooltip
v-model="searchForm.index" placeholder="请选择统计指标" @change="onIndexChange($event)" filterable>
<el-option v-for="item in indexOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
@@ -33,12 +21,8 @@
</el-radio-group>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 90px !important"
placeholder="请选择"
v-model="searchForm.valueType"
filterable
>
<el-select style="min-width: 90px !important" placeholder="请选择" v-model="searchForm.valueType"
filterable>
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
@@ -46,29 +30,15 @@
</el-select>
</el-form-item>
<el-form-item>
<div
class="history_count"
v-for="(item, index) in countData"
:key="index"
v-show="item.countOptions.length != 0"
>
<div class="history_count" v-for="(item, index) in countData" :key="index"
v-show="item.countOptions.length != 0">
<span class="mr12">
{{ item.name.includes('次数') ? item.name : item.name + '谐波次数' }}
</span>
<el-select
v-model="item.count"
@change="onCountChange($event, index)"
placeholder="请选择谐波次数"
style="width: 100px"
class="mr20"
filterable
>
<el-option
v-for="vv in item.countOptions"
:key="vv"
:label="item.name.includes('间谐波') ? vv - 0.5 : vv"
:value="vv"
></el-option>
<el-select v-model="item.count" @change="onCountChange($event, index)" placeholder="请选择谐波次数"
style="width: 100px" class="mr20" filterable>
<el-option v-for="vv in item.countOptions" :key="vv"
:label="item.name.includes('间谐波') ? vv - 0.5 : vv" :value="vv"></el-option>
</el-select>
</div>
</el-form-item>
@@ -109,6 +79,7 @@ const props = defineProps({
type: Array
}
})
const dialogTitle = ref('趋势图')
const dialogVisible: any = ref(false)
// console.log("🚀 ~ props:", props.TrendList)
@@ -644,7 +615,7 @@ const onIndexChange = (val: any) => {
}
watch(
() => searchForm.value.index,
(val: any, oldval: any) => {},
(val: any, oldval: any) => { },
{
deep: true,
immediate: true
@@ -652,6 +623,7 @@ watch(
)
const openDialog = async (row: any, field: any, title: any) => {
dialogTitle.value = row?.lineName + '_趋势图'
dialogVisible.value = true
trendRequestData.value = row

View File

@@ -1,10 +1,10 @@
<template>
<div>
<!-- 指标越限详情 -->
<el-dialog draggable title="指标越限详情" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="title" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef">
<template v-slot:select>
<el-form-item label="监测点">
<!-- <el-form-item label="监测点">
<el-select
v-model="tableStore.table.params.lineId"
placeholder="请选择监测点"
@@ -18,7 +18,7 @@
:value="item.lineId"
/>
</el-select>
</el-form-item>
</el-form-item> -->
</template>
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
@@ -38,7 +38,7 @@ import { cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const title = ref('指标越限详情')
const options = ref()
const height = mainHeight(0, 2).height as any
const tableHeaderRef = ref()
@@ -76,13 +76,13 @@ const tableStore: any = new TableStore({
{
title: '日期',
field: 'time',
width: '150',
width: '120',
sortable: true
},
{
title: '名称',
field: 'lineName',
width: '150'
width: '120'
},
{
title: '长时闪变越限(%)',
@@ -143,6 +143,7 @@ tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any,interval:any,list:any) => {
dialogVisible.value = true
title.value = row.lineName + '_指标越限详情'
options.value = list
// initCSlineList()
tableStore.table.params.lineId = row.lineId
@@ -161,7 +162,7 @@ const open = async (row: any,searchBeginTime:any,searchEndTime:any,interval:any,
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name' && column.field != 'time') {
if (column.field != 'lineName' &&column.field != 'name' && column.field != 'time') {
dialogFlag.value = true
dialogVisible.value = false
nextTick(() => {

View File

@@ -1,28 +1,14 @@
<template>
<div>
<!--电网侧指标越限统计 -->
<TableHeader
:showReset="false"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
:timeKeyList="prop.timeKey"
v-if="fullscreen"
></TableHeader>
<my-echart
class="tall"
:options="echartList"
:style="{
<TableHeader :showReset="false" ref="TableHeaderRef" @selectChange="selectChange" datePicker
:timeKeyList="prop.timeKey" v-if="fullscreen"></TableHeader>
<my-echart class="tall" :options="echartList" :style="{
width: prop.width,
height: `calc(${prop.height} / 2 )`
}"
/>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} / 2 - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`"
isGroup
></Table>
}" />
<Table ref="tableRef" @cell-click="cellClickEvent"
:height="`calc(${prop.height} / 2 - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`" isGroup></Table>
<!-- 指标越限详情 -->
<OverLimitDetails ref="OverLimitDetailsRef" />
</div>
@@ -141,17 +127,20 @@ const tableStore: any = new TableStore({
}
},
{
title: '名称',
title: '监测点名称',
field: 'lineName',
minWidth: '90'
minWidth: 120
},
{ title: '设备名称', field: 'devName', minWidth: 130, align: 'center', },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '越限占比(%)',
children: [
{
title: '长时闪变',
field: 'flicker',
minWidth: '70',
minWidth: '80',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flicker}</span>`
@@ -261,6 +250,6 @@ watch(
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>

View File

@@ -1,46 +1,31 @@
<template>
<div>
<div class="device-control">
<!--指标越限时间分布
-->
<TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
>
<template v-slot:select>
<div v-show="fullscreen">
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
</div>
<div>
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
@selectChange="selectChange" datePicker v-if="fullscreen">
<!-- <template v-slot:select>
<el-form-item label="监测点">
<el-select size="small" filterable v-model="tableStore.table.params.lineId">
<el-option
v-for="item in lineList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
<el-option v-for="item in lineList" :key="item.lineId" :label="item.name"
:value="item.lineId" />
</el-select>
</el-form-item>
</template>
</template> -->
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart
class="tall"
v-if="lineShow"
:options="echartList1"
:style="{
width: prop.width,
<my-echart class="tall" v-if="lineShow" :options="echartList1" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
}" />
<el-empty v-else description="暂无监测点" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
}" />
<!-- <my-echart
class="mt10"
:options="echartList1"
@@ -51,6 +36,7 @@
/> -->
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h } from 'vue'
@@ -59,7 +45,7 @@ import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
import PointTree from '@/components/tree/govern/pointTree.vue'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
@@ -67,7 +53,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
@@ -106,17 +93,28 @@ const echartList1 = ref()
const probabilityData = ref()
const initLineList = async () => {
cslineList({}).then(res => {
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
// const initLineList = async () => {
// cslineList({}).then(res => {
// if (res.data.length == 0) {
// lineShow.value = false
// return (tableStore.table.loading = false)
// }
// lineShow.value = true
// lineList.value = res.data
// tableStore.table.params.lineId = lineList.value[0].lineId
// tableStore.index()
// })
// }
const nodeClick = (e: any) => {
if (e == undefined) {
}
lineShow.value = true
lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId
if (e.level == 3) {
tableStore.table.params.lineId = e.id
tableStore.index()
})
}
}
const pointTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
// 越限程度概率分布
@@ -438,7 +436,7 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore)
onMounted(() => {
initLineList()
// initLineList()
})
const setTime = () => {
@@ -475,6 +473,14 @@ watch(
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -1,45 +1,30 @@
<template>
<div>
<div class="device-control">
<!--指标越限概率分布 -->
<TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
>
<template v-slot:select>
<div v-show="fullscreen">
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
</div>
<div>
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
@selectChange="selectChange" datePicker v-if="fullscreen">
<!-- <template v-slot:select>
<el-form-item label="监测点">
<el-select size="small" filterable v-model="tableStore.table.params.lineId">
<el-option
v-for="item in lineList"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
<el-option v-for="item in lineList" :key="item.lineId" :label="item.name"
:value="item.lineId" />
</el-select>
</el-form-item>
</template>
</template> -->
</TableHeader>
<div v-loading="tableStore.table.loading">
<my-echart
v-if="lineShow"
class="tall"
:options="echartList"
:style="{
width: prop.width,
<my-echart v-if="lineShow" class="tall" :options="echartList" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
<el-empty
v-else
description="暂无监测点"
:style="{
width: prop.width,
}" />
<el-empty v-else description="暂无监测点" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
}" />
<!-- <my-echart
class="mt10"
:options="echartList1"
@@ -50,6 +35,8 @@
/> -->
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, reactive, watch, h } from 'vue'
@@ -58,7 +45,7 @@ import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
import PointTree from '@/components/tree/govern/pointTree.vue'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
@@ -66,7 +53,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const lineShow = ref(true)
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
@@ -105,18 +93,31 @@ const echartList1 = ref()
const probabilityData = ref()
const initLineList = async () => {
cslineList({}).then(res => {
if (res.data.length == 0) {
lineShow.value = false
return (tableStore.table.loading = false)
// const initLineList = async () => {
// cslineList({}).then(res => {
// if (res.data.length == 0) {
// lineShow.value = false
// return (tableStore.table.loading = false)
// }
// lineShow.value = true
// lineList.value = res.data
// tableStore.table.params.lineId = lineList.value[0].lineId
// tableStore.index()
// })
// }
const nodeClick = (e: any) => {
if (e == undefined) {
}
lineShow.value = true
lineList.value = res.data
tableStore.table.params.lineId = lineList.value[0].lineId
if (e.level == 3) {
tableStore.table.params.lineId = e.id
tableStore.index()
})
}
}
const pointTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
// 越限程度概率分布
const initProbabilityData = () => {
@@ -437,7 +438,7 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore)
onMounted(() => {
initLineList()
// initLineList()
})
const setTime = () => {
@@ -474,6 +475,13 @@ watch(
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog draggable title="趋势图" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="dialogTitle" v-model="dialogVisible" append-to-body width="70%">
<!-- 总体指标占比详情谐波含有率 -->
<div>
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
@@ -8,22 +8,10 @@
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item>
<el-form-item label="统计指标" label-width="80px">
<el-select
multiple
:multiple-limit="2"
collapse-tags
collapse-tags-tooltip
v-model="searchForm.index"
placeholder="请选择统计指标"
@change="onIndexChange($event)"
filterable
>
<el-option
v-for="item in indexOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-select multiple :multiple-limit="2" collapse-tags collapse-tags-tooltip
v-model="searchForm.index" placeholder="请选择统计指标" @change="onIndexChange($event)" filterable>
<el-option v-for="item in indexOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
@@ -33,12 +21,8 @@
</el-radio-group>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 120px !important"
placeholder="请选择"
v-model="searchForm.valueType"
filterable
>
<el-select style="min-width: 120px !important" placeholder="请选择" v-model="searchForm.valueType"
filterable>
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
@@ -46,29 +30,15 @@
</el-select>
</el-form-item>
<el-form-item>
<div
class="history_count"
v-for="(item, index) in countData"
:key="index"
v-show="item.countOptions.length != 0"
>
<div class="history_count" v-for="(item, index) in countData" :key="index"
v-show="item.countOptions.length != 0">
<span class="mr12">
{{ item.name.includes('次数') ? item.name : item.name + '谐波次数' }}
</span>
<el-select
v-model="item.count"
@change="onCountChange($event, index)"
placeholder="请选择谐波次数"
style="width: 100px"
class="mr20"
filterable
>
<el-option
v-for="vv in item.countOptions"
:key="vv"
:label="item.name.includes('间谐波') ? vv - 0.5 : vv"
:value="vv"
></el-option>
<el-select v-model="item.count" @change="onCountChange($event, index)" placeholder="请选择谐波次数"
style="width: 100px" class="mr20" filterable>
<el-option v-for="vv in item.countOptions" :key="vv"
:label="item.name.includes('间谐波') ? vv - 0.5 : vv" :value="vv"></el-option>
</el-select>
</div>
</el-form-item>
@@ -109,6 +79,7 @@ const props = defineProps({
type: Array
}
})
const dialogTitle = ref('趋势图')
const dialogVisible: any = ref(false)
// console.log("🚀 ~ props:", props.TrendList)
@@ -184,7 +155,7 @@ const initCode = (field: string, title: string) => {
? '谐波电压'
: field.includes('iharm')
? '谐波电流'
: field.includes('voltageDevOvertime')
: field.includes('uaberrance')
? '电压偏差'
: field.includes('ubalanceOvertime')
? '不平衡'
@@ -643,7 +614,7 @@ const onIndexChange = (val: any) => {
}
watch(
() => searchForm.value.index,
(val: any, oldval: any) => {},
(val: any, oldval: any) => { },
{
deep: true,
immediate: true
@@ -652,6 +623,8 @@ watch(
const openDialog = async (row: any, field: any, title: any) => {
dialogVisible.value = true
dialogTitle.value = row?.lineName + '_趋势图'
trendRequestData.value = row
nextTick(() => {

View File

@@ -1,9 +1,9 @@
<template>
<div>
<!-- 指标越限详情 -->
<el-dialog draggable title="指标越限详情" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="title" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef">
<template v-slot:select>
<!-- <template v-slot:select>
<el-form-item label="监测点">
<el-select
v-model="tableStore.table.params.lineId"
@@ -19,7 +19,7 @@
/>
</el-select>
</el-form-item>
</template>
</template> -->
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
</el-dialog>
@@ -28,7 +28,7 @@
</div>
</template>
<script setup lang="ts">
import { ref, provide,nextTick } from 'vue'
import { ref, provide, nextTick } from 'vue'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore'
@@ -40,7 +40,7 @@ const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const dialogFlag = ref(false)
const title = ref('指标越限详情')
const options = ref()
const height = mainHeight(0, 2).height as any
const tableHeaderRef = ref()
@@ -77,18 +77,18 @@ const tableStore: any = new TableStore({
{
title: '日期',
field: 'time',
width: '150',
width: '120',
sortable: true
},
{
title: '名称',
title: '监测点名称',
field: 'lineName',
width: '150'
width: '120'
},
{
title: '越限(分钟)',
title: '长时闪变越限(分钟)',
field: 'flickerOvertime',
width: '90',
width: '100',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
@@ -140,15 +140,17 @@ const tableStore: any = new TableStore({
provide('tableStore', tableStore)
tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any,data:any=[]) => {
const open = async (row: any, searchBeginTime: any, searchEndTime: any, data: any = []) => {
console.log("🚀 ~ open ~ row:", row)
dialogVisible.value = true
// initCSlineList()
options.value = data
title.value = row.lineName + '_指标越限详情'
tableStore.table.params.lineId = row.lineId
nextTick(() => {
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime])
tableStore.table.params.searchBeginTime =searchBeginTime
tableStore.table.params.searchBeginTime = searchBeginTime
tableStore.table.params.searchEndTime = searchEndTime
tableStore.index()
})
@@ -157,11 +159,11 @@ const open = async (row: any,searchBeginTime:any,searchEndTime:any,data:any=[])
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name' && column.field != 'time') {
if (column.field != 'lineName' && column.field != 'name' && column.field != 'time') {
dialogFlag.value = true
dialogVisible.value = false
nextTick(() => {
harmonicRatioRef.value.openDialog(row,column.field,column.title.replace(/次/g, ""))
harmonicRatioRef.value.openDialog(row, column.field, column.title.replace(/次/g, ""))
})
}

View File

@@ -1,25 +1,17 @@
<template>
<div>
<!--主要监测点列表 -->
<TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
@selectChange="selectChange"
v-if="fullscreen"
ref="TableHeaderRef"
>
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" @selectChange="selectChange" v-if="fullscreen"
ref="TableHeaderRef">
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit v-model="tableStore.table.params.keywords" clearable placeholder="请输入监测点名称" />
<el-input maxlength="32" show-word-limit v-model="tableStore.table.params.keywords" clearable
placeholder="请输入监测点名称" />
</el-form-item>
</template>
</TableHeader>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"
></Table>
<Table ref="tableRef" @cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"></Table>
<!-- 指标越限详情 -->
<OverLimitDetails ref="OverLimitDetailsRef" />
</div>
@@ -41,7 +33,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const OverLimitDetailsRef = ref()
const headerHeight = ref(57)
@@ -90,17 +83,17 @@ const tableStore: any = new TableStore({
{
title: '监测点名称',
field: 'lineName',
minWidth: '90',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.lineName}</span>`
}
},
minWidth: 120,
},
{ title: '设备名称', field: 'devName', minWidth: 130, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '监测对象类型',
field: 'objType',
minWidth: '90',
minWidth: '100',
formatter: (row: any) => {
return row.cellValue || '/'
}
@@ -114,7 +107,11 @@ const tableStore: any = new TableStore({
}
},
{ title: '主要存在的电能质量问题', field: 'problems', minWidth: '150', showOverflow: true }
{
title: '主要存在的电能质量问题', field: 'problems', minWidth: '250', render: 'customTemplate', customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.problems}</span>`
}
}
],
beforeSearchFun: () => {
setTime()
@@ -131,7 +128,7 @@ provide('tableStore', tableStore)
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field == 'lineName') {
if (column.field == 'problems') {
let time = getTimeOfTheMonth('3');
OverLimitDetailsRef.value.open(
@@ -176,7 +173,13 @@ watch(
deep: true
}
)
watch(
() => prop.flag,
val => {
tableStore.showPage = fullscreen.value ? true : false
}
)
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped></style>

View File

@@ -220,16 +220,33 @@ const setEchart = () => {
}
: {}
],
grid: {
left: '10px',
right: '20px'
},
// grid: {
// left: '10px',
// right: '30px',
// },
options: {
// dataZoom: [
// {
// type: 'inside',
// start: 0,
// end: 100,
// filterMode: 'filter'
// },
// {
// type: 'slider',
// start: 0,
// end: 100,
// height: 13,
// bottom: '20px',
// filterMode: 'filter'
// }
// ],
series: [
{
type: 'bar',
name: powerName, // 动态设置功率名称
data: [],
clip: true,
itemStyle: {
normal: {
color: function (params: any) {
@@ -248,7 +265,7 @@ const setEchart = () => {
type: 'line',
step: 'end',
showSymbol: false,
// smooth: true,
clip: true,
data: [],
yAxisIndex: 1
}

View File

@@ -1,10 +1,10 @@
<template>
<div>
<!-- 指标越限详情 -->
<el-dialog draggable title="指标越限详情" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="title" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef">
<template v-slot:select>
<el-form-item label="监测点">
<!-- <el-form-item label="监测点">
<el-select
v-model="tableStore.table.params.lineId"
placeholder="请选择监测点"
@@ -18,7 +18,7 @@
:value="item.lineId"
/>
</el-select>
</el-form-item>
</el-form-item> -->
</template>
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
@@ -38,7 +38,7 @@ import { cslineList ,governLineList} from '@/api/harmonic-boot/cockpit/cockpit'
const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const title = ref('指标越限详情')
const options = ref()
const height = mainHeight(0, 2).height as any
const tableHeaderRef = ref()
@@ -76,13 +76,13 @@ const tableStore: any = new TableStore({
{
title: '日期',
field: 'time',
width: '150',
width: '120',
sortable: true
},
{
title: '名称',
field: 'lineName',
width: '150'
width: '120'
},
{
title: '长时闪变越限(%)',
@@ -144,6 +144,7 @@ tableStore.table.params.orderBy = ''
const time:any=ref([])
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
dialogVisible.value = true
title.value = row.lineName + '_指标越限详情'
time.value=[searchBeginTime,searchEndTime]
initCSlineList()
tableStore.table.params.lineId = row.lineId
@@ -159,7 +160,7 @@ const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name' && column.field != 'time') {
if (column.field != 'lineName' && column.field != 'name' && column.field != 'time') {
dialogFlag.value = true
dialogVisible.value = false
nextTick(() => {

View File

@@ -1,55 +1,26 @@
<template>
<div>
<!-- 监测点列表 -->
<TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
:timeKeyList="prop.timeKey"
>
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" v-if="fullscreen"
:timeKeyList="prop.timeKey">
<template #select>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
placeholder="请输入监测点名称"
/>
<el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点名称" />
</el-form-item>
</template>
</TableHeader>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"
></Table>
<Table ref="tableRef" @cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"></Table>
<!-- 指标越限详情 -->
<OverLimitDetails ref="OverLimitDetailsRef" />
<!-- 上传对话框 -->
<el-dialog
v-model="uploadDialogVisible"
title="上传报告"
append-to-body
width="500px"
@closed="handleDialogClosed"
>
<el-upload
ref="uploadRef"
class="upload-demo"
action=""
accept=".doc,.docx,.PDF"
:on-change="handleChange"
:before-upload="beforeUpload"
:limit="1"
:auto-upload="false"
:on-exceed="handleExceed"
:on-remove="handleRemove"
:file-list="fileList"
>
<el-dialog v-model="uploadDialogVisible" title="上传报告" append-to-body width="500px" @closed="handleDialogClosed">
<el-upload ref="uploadRef" class="upload-demo" action="" accept=".doc,.docx,.PDF" :on-change="handleChange"
:before-upload="beforeUpload" :limit="1" :auto-upload="false" :on-exceed="handleExceed"
:on-remove="handleRemove" :file-list="fileList">
<el-button type="primary">点击上传</el-button>
<template #tip>
<div class="el-upload__tip">请上传Word或PDF文件</div>
@@ -73,7 +44,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import OverLimitDetails from '@/components/cockpit/monitoringPointList/components/overLimitDetails.vue'
import TableHeader from '@/components/table/header/index.vue'
import { uploadReport, getReportUrl } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
const prop = defineProps({
w: { type: [String, Number] },
@@ -82,7 +53,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const headerHeight = ref(57)
@@ -135,16 +107,19 @@ const tableStore: any = new TableStore({
{
title: '监测点名称',
field: 'lineName',
minWidth: '120',
minWidth: 120,
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.lineName}</span>`
}
},
{ title: '设备名称', field: 'devName', minWidth: 130, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '监测类型',
field: 'position',
minWidth: '80',
minWidth: '100',
formatter: (row: any) => {
return row.cellValue || '/'
}
@@ -162,28 +137,22 @@ const tableStore: any = new TableStore({
title: '监测点状态',
field: 'runStatus',
render: 'tag',
width: 100,
width: 90,
custom: {
停运: 'danger',
退运: 'danger',
运行: 'success',
在线: 'success',
中断: 'warning',
离线: 'danger',
检修: 'warning',
调试: 'warning',
// 0运行1检修2停运3调试4退运
0: 'success',
1: 'warning',
2: 'danger',
3: 'warning',
4: 'info',
null: 'info'
},
replaceValue: {
运行: '运行',
在线: '在线',
退运: '退运',
停运: '停运',
中断: '中断',
检修: '检修',
离线: '离线',
调试: '调试',
0: '运行',
1: '检修',
2: '运',
3: '调试',
4: '退运',
null: '/'
}
},
@@ -198,7 +167,7 @@ const tableStore: any = new TableStore({
{
title: '治理对象',
field: 'sensitiveUser',
minWidth: '90',
minWidth: '100',
formatter: (row: any) => {
return row.cellValue || '/'
}
@@ -206,7 +175,7 @@ const tableStore: any = new TableStore({
{
title: '是否治理',
title: '治理方案',
field: 'govern',
minWidth: '80',
formatter: (row: any) => {
@@ -241,7 +210,7 @@ const tableStore: any = new TableStore({
// }
// },
{
title: '报告',
title: '治理前报告',
field: 'reportFilePath',
minWidth: '150',
formatter: (row: any) => {
@@ -347,7 +316,7 @@ const downloadTheReport = (lineId: string, name: string) => {
forceDownloadPdf(res.data, name.split('/').pop() || '')
})
}
const forceDownloadPdf = async (pdfUrl:any, fileName = '文件.pdf') => {
const forceDownloadPdf = async (pdfUrl: any, fileName = '文件.pdf') => {
try {
// 1. 请求 PDF 并转为 Blob关键绕开浏览器直接解析
const response = await fetch(pdfUrl, {
@@ -456,6 +425,12 @@ const handleUpload = async () => {
onMounted(() => {
tableStore.index()
})
watch(
() => prop.flag,
val => {
tableStore.showPage = fullscreen.value ? true : false
}
)
watch(
() => prop.timeKey,
val => {

View File

@@ -1,10 +1,10 @@
<template>
<div>
<!-- 指标越限详情 -->
<el-dialog draggable title="指标越限详情" v-model="dialogVisible" append-to-body width="70%">
<el-dialog draggable :title="title" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef">
<template v-slot:select>
<el-form-item label="监测点">
<!-- <el-form-item label="监测点">
<el-select
v-model="tableStore.table.params.lineId"
placeholder="请选择监测点"
@@ -18,7 +18,7 @@
:value="item.lineId"
/>
</el-select>
</el-form-item>
</el-form-item> -->
</template>
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
@@ -38,7 +38,7 @@ import { cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
const dialogVisible: any = ref(false)
const harmonicRatioRef: any = ref(null)
const title = ref('指标越限详情')
const options = ref()
const height = mainHeight(0, 2).height as any
const tableHeaderRef = ref()
@@ -76,13 +76,13 @@ const tableStore: any = new TableStore({
{
title: '日期',
field: 'time',
width: '150',
width: '120',
sortable: true
},
{
title: '名称',
field: 'lineName',
width: '150'
width: '120'
},
{
title: '长时闪变越限(%)',
@@ -144,6 +144,7 @@ tableStore.table.params.orderBy = ''
const open = async (row: any,searchBeginTime:any,searchEndTime:any,data: any) => {
dialogVisible.value = true
// initCSlineList()
title.value = row.lineName + '_指标越限详情'
options.value = data
tableStore.table.params.lineId = row.lineId
@@ -158,7 +159,7 @@ const open = async (row: any,searchBeginTime:any,searchEndTime:any,data: any) =>
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name' && column.field != 'time') {
if (column.field != 'lineName' && column.field != 'name' && column.field != 'time') {
dialogFlag.value = true
dialogVisible.value = false
nextTick(() => {

View File

@@ -142,17 +142,20 @@ const tableStore: any = new TableStore({
}
},
{
title: '名称',
title: '监测点名称',
field: 'lineName',
minWidth: '90'
minWidth: 120
},
{ title: '设备名称', field: 'devName', minWidth: 130, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '越限占比(%)',
children: [
{
title: '长时闪变',
field: 'flicker',
minWidth: '70',
minWidth: '80',
render: 'customTemplate',
customTemplate: (row: any) => {
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flicker}</span>`

View File

@@ -1,32 +1,17 @@
<template>
<div>
<!--敏感负荷列表 -->
<TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
:timeKeyList="prop.timeKey"
>
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" v-if="fullscreen"
:timeKeyList="prop.timeKey">
<template #select>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
placeholder="请输入敏感负荷名称"
/>
<el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入敏感用户名称" />
</el-form-item>
</template>
</TableHeader>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`"
isGroup
></Table>
<Table ref="tableRef" @cell-click="cellClickEvent"
:height="`calc(${prop.height} - ${headerHeight}px + ${fullscreen ? -58 : 56}px )`" isGroup></Table>
</div>
</template>
<script setup lang="ts">
@@ -44,7 +29,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const headerHeight = ref(57)
@@ -91,13 +77,13 @@ const tableStore: any = new TableStore({
}
},
{
title: '敏感负荷名称',
title: '敏感用户名称',
field: 'name',
minWidth: '90'
},
{
title: '敏感负荷类型',
title: '敏感用户类型',
field: 'loadType',
minWidth: '70',
formatter: row => {
@@ -125,7 +111,7 @@ const tableStore: any = new TableStore({
setTime()
},
loadCallback: () => {}
loadCallback: () => { }
})
tableStore.table.params.searchValue = ''
const tableRef = ref()
@@ -179,5 +165,11 @@ watch(
deep: true
}
)
watch(
() => prop.flag,
val => {
tableStore.showPage = fullscreen.value ? true : false
}
)
</script>
<style lang="scss" scoped></style>

View File

@@ -1,203 +1,155 @@
<template>
<div>
<!-- 暂态事件详情 -->
<el-dialog draggable title="暂态事件详情 " v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select>
<el-form-item label="监测点" v-if="props.showLine">
<el-select v-model="tableStore.table.params.lineId" filterable placeholder="请选择监测点名称">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="暂态类型">
<el-select
v-model="tableStore.table.params.eventType"
style="min-width: 150px"
clearable
placeholder="请选择暂态类型"
>
<el-option
v-for="item in eventList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-dialog draggable title="暂态事件" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport ref="tableHeaderRef" @selectChange="selectChange"
@onResetForm="onResetForm">
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
</TableHeader>
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
@confirm="onFilterConfirm" />
<Table ref="tableRef" isGroup :height="heightRef"></Table>
</el-dialog>
<!-- 查看波形 -->
<el-dialog
v-model="isWaveCharts"
draggable
title="波形分析"
append-to-body
v-if="isWaveCharts"
width="70%"
@close="handleHideCharts"
>
<waveFormAnalysis
v-loading="loading"
ref="waveFormAnalysisRef"
@handleHideCharts="handleHideCharts"
:wp="wp"
/>
<el-dialog v-model="isWaveCharts" draggable title="波形分析" append-to-body width="70%" @close="handleHideCharts">
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="handleHideCharts" :wp="wp" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import { ref, provide, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout'
import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import { analyseWave } from '@/api/common'
import { getSimpleLine } from '@/api/harmonic-boot/cockpit/cockpit'
interface Props {
showLine?: boolean
}
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData'
const props = withDefaults(defineProps<Props>(), {
showLine: true
})
const dialogVisible: any = ref(false)
const waveFormAnalysisRef: any = ref(null)
// 波形
const dialogVisible = ref(false)
const waveFormAnalysisRef = ref()
const isWaveCharts = ref(false)
const loading = ref(false)
const wp = ref({})
const boxoList: any = ref({})
const tableHeaderRef = ref()
const filterVisible = ref(false)
const multiConditionRef = ref<InstanceType<typeof MultiCondition>>()
const currentOpenTime = ref('')
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const options = ref()
const heightRef = ref(mainHeight(168, 2.1).height)
const selectChange = (flag: boolean, h: any) => {
const selectChange = (_flag: boolean, h: number) => {
heightRef.value = mainHeight(h, 2.1).height
}
const eventList = [
{ label: '电压暂降', value: '1' },
{ label: '电压中断', value: '2' },
{ label: '电压暂升', value: '3' }
]
const getSimpleLineList = async () => {
const res = await getSimpleLine()
options.value = res.data
}
const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/event/pageEvent',
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
method: 'POST',
showPage: true,
exportName: '暂态事件详情',
exportName: '暂态事件',
column: [
{
field: 'index',
title: '序号',
width: '80',
width: 80,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{
title: '暂态时间',
field: 'startTime',
minWidth: '180'
},
{
title: '测点名称',
field: 'lineName',
minWidth: '150'
},
{
title: '暂态类型',
field: 'tag',
minWidth: '100'
},
{
title: '特征幅值(%)',
field: 'amplitude',
minWidth: '100'
},
{
title: '暂降深度(%)',
field: 'depth',
minWidth: '100',
formatter: (row: any) => {
// 当暂态类型不是电压暂升时,计算暂降深度 = 100 - 特征幅值
if (row.row.tag !== '电压暂升') {
const amplitude = parseFloat(row.row.amplitude)
if (!isNaN(amplitude)) {
return 100 - amplitude
}
return '-'
} else {
// 电压暂升时不显示暂降深度
return '/'
}
}
},
{
title: '持续时间(S)',
field: 'persistTime',
minWidth: '100'
},
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '暂降幅值(%)', minWidth: 120, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true },
{
title: '严重度',
field: 'severity',
minWidth: '80'
minWidth: 80,
align: 'center',
sortable: true,
formatter: (row: any) => row.cellValue ? row.cellValue : '/'
},
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 100, align: 'center' },
{
title: '暂降原因',
field: 'advanceReason',
minWidth: 100,
align: 'center',
formatter: (row: any) => ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
},
{
title: '波形',
minWidth: '100',
title: '暂降类型',
field: 'advanceType',
minWidth: 100,
align: 'center',
formatter: (row: any) => EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
},
{ title: '监测点名称', field: 'lineName', minWidth: 130, align: 'center' },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true },
{ title: '设备名称', field: 'equipmentName', minWidth: 130, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '发生位置',
field: 'sagSource',
minWidth: 120,
align: 'center',
formatter: (row: any) => row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
},
{
title: '操作',
fixed: 'right',
align: 'center',
width: 150,
render: 'buttons',
buttons: [
{
name: 'edit',
text: '波形分析',
text: '暂无波形',
type: 'info',
icon: 'el-icon-DataLine',
render: 'basicButton',
disabled: row => {
return row.wavePath
},
},
{
name: 'edit',
title: '波形分析',
type: 'primary',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading1',
disabled: row => {
return !row.wavePath
},
click: async row => {
disabled: (row: any) => !row.wavePath,
click: async (row: any) => {
row.loading1 = true
dialogVisible.value = false
// 在打开弹窗时立即设置高度
nextTick(() => {
if (waveFormAnalysisRef.value) {
// waveFormAnalysisRef.value.setHeight(false, 360)
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
}
})
await analyseWave(row.id)
.then(res => {
isWaveCharts.value = true
loading.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row
// boxoList.value = {
// ...row,
// duration: row.persistTime // 将 persistTime 值赋给 duration
// }
boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
// row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
boxoList.value.systemType = 'YPT'
loading.value = true
isWaveCharts.value = true
boxoList.value = {
...row,
engineeringName: row.projectName,
persistTime: row.evtParamTm,
featureAmplitude:
row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null,
systemType: 'YPT',
}
wp.value = res.data
}
loading.value = false
@@ -208,40 +160,142 @@ const tableStore: any = new TableStore({
})
nextTick(() => {
waveFormAnalysisRef.value &&
if (waveFormAnalysisRef.value) {
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
}
})
}
},
{
name: 'edit',
text: '暂无波形',
type: 'info',
icon: 'el-icon-DataLine',
title: '波形下载',
type: 'primary',
icon: 'el-icon-Check',
loading: 'loading2',
render: 'basicButton',
disabled: row => {
return row.wavePath
disabled: (row: any) => !row.wavePath,
click: (row: any) => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
const blob = new Blob([res as unknown as BlobPart], { type: 'application/zip' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = row.wavePath.split('/')[2] || '波形文件'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
}
]
}
],
beforeSearchFun: () => {},
loadCallback: () => {}
beforeSearchFun: () => { },
resetCallback: () => {
restoreOpenDayTime()
},
loadCallback: () => {
tableStore.table.data.forEach((item: any) => {
item.loading = false
item.evtParamTm =
item.evtParamTm.split('s')[0] != '-' ? (item.evtParamTm.split('s')[0] - 0).toFixed(2) : '-'
item.evtParamVVaDepth =
item.evtParamVVaDepth.split('%')[0] != '-'
? (item.evtParamVVaDepth.split('%')[0] - 0).toFixed(2)
: '-'
})
}
})
tableStore.table.params.eventType = ''
tableStore.table.params.type = 0
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
provide('tableStore', tableStore)
const open = async (time: any) => {
tableStore.table.params.eventType = ''
dialogVisible.value = true
getSimpleLineList()
tableStore.table.params.lineId = ''
nextTick(() => {
const resetFilterParams = () => {
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
}
const restoreOpenDayTime = () => {
if (!currentOpenTime.value || !tableHeaderRef.value) return
tableHeaderRef.value.setInterval(5)
tableHeaderRef.value.setTimeInterval([time, time])
tableStore.table.params.searchBeginTime = time
tableStore.table.params.searchEndTime = time
tableHeaderRef.value.setTimeInterval([currentOpenTime.value, currentOpenTime.value])
tableStore.table.params.searchBeginTime = currentOpenTime.value
tableStore.table.params.searchEndTime = currentOpenTime.value
}
const syncInitDataForReset = () => {
if (!tableStore.initData) return
Object.assign(tableStore.initData, {
searchBeginTime: currentOpenTime.value,
searchEndTime: currentOpenTime.value,
startTime: currentOpenTime.value,
endTime: currentOpenTime.value,
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
}
const openFilterDialog = () => {
filterVisible.value = true
}
const onFilterConfirm = () => {
tableStore.onTableAction('search', {})
}
const onResetForm = () => {
filterVisible.value = false
resetFilterParams()
multiConditionRef.value?.reset()
syncInitDataForReset()
nextTick(() => {
restoreOpenDayTime()
})
}
const open = (time: string) => {
currentOpenTime.value = time
dialogVisible.value = true
filterVisible.value = false
resetFilterParams()
multiConditionRef.value?.reset()
tableStore.table.params.pageNum = 1
nextTick(() => {
restoreOpenDayTime()
tableStore.index()
nextTick(() => {
syncInitDataForReset()
})
})
}
@@ -252,4 +306,5 @@ const handleHideCharts = () => {
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@@ -58,7 +58,7 @@
</template>
</el-calendar>
<!-- 暂态事件列表 -->
<TransientList ref="transientListRef" :showLine="false" />
<TransientList ref="transientListRef" />
</div>
</template>
<script setup lang="ts">

View File

@@ -1,22 +1,12 @@
<template>
<div>
<!--暂态事件概率分布 -->
<TableHeader
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
<my-echart
class="tall"
:options="echartList"
:style="{
<TableHeader ref="TableHeaderRef" :timeKeyList="prop.timeKey" :showReset="false" @selectChange="selectChange"
datePicker v-if="fullscreen"></TableHeader>
<my-echart class="tall" :options="echartList" :style="{
width: prop.width,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
}" />
<!-- <my-echart
class="mt10"
:options="echartList1"
@@ -170,7 +160,11 @@ const tableStore: any = new TableStore({
},
title: {
text: '暂态事件概率分布',
x: 'center'
x: 'center',
textStyle: {
color: '#000',
fontSize: '15'
},
},
visualMap: {
max: 500,
@@ -242,7 +236,7 @@ const tableStore: any = new TableStore({
echartList1.value = {
title: {
text: '越限时间概率分布'
text: '暂态事件概率分布',
},
xAxis: {
type: 'category',

View File

@@ -1,197 +1,178 @@
<template>
<div>
<!-- 暂态事件详情 -->
<el-dialog draggable title="暂态事件详情 " v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select>
<el-form-item label="监测点">
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称">
<el-option
v-for="item in options"
:key="item.lineId"
:label="item.name"
:value="item.lineId"
/>
</el-select>
</el-form-item>
<el-form-item label="暂态类型">
<el-select
v-model="tableStore.table.params.eventType"
style="min-width: 150px"
clearable
placeholder="请选择暂态类型"
>
<el-option
v-for="item in eventList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-dialog draggable :title="title" v-model="dialogVisible" append-to-body width="70%">
<TableHeader datePicker showExport ref="tableHeaderRef" @selectChange="selectChange"
@onResetForm="onResetForm">
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
</TableHeader>
<MultiCondition
v-model:visible="filterVisible"
:fixed-target="filterTarget"
:params="tableStore.table.params"
ref="multiConditionRef"
@confirm="onFilterConfirm"
/>
<Table ref="tableRef" isGroup :height="heightRef"></Table>
</el-dialog>
<!-- 查看波形 -->
<el-dialog
v-model="isWaveCharts"
draggable
title="波形分析"
append-to-body
width="70%"
@close="handleHideCharts"
>
<waveFormAnalysis
v-loading="loading"
v-if="isWaveCharts"
ref="waveFormAnalysisRef"
@handleHideCharts="handleHideCharts"
:wp="wp"
/>
<el-dialog v-model="isWaveCharts" draggable title="波形分析" append-to-body width="70%" @close="handleHideCharts">
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="handleHideCharts" :wp="wp" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import { ref, provide, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout'
import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import { analyseWave } from '@/api/common'
import { getSimpleLine } from '@/api/harmonic-boot/cockpit/cockpit'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData'
const dialogVisible: any = ref(false)
const waveFormAnalysisRef: any = ref(null)
// 波形
const dialogVisible = ref(false)
const waveFormAnalysisRef = ref()
const isWaveCharts = ref(false)
const loading = ref(false)
const wp = ref({})
const boxoList: any = ref({})
const title = ref('暂态事件')
const tableHeaderRef = ref()
const filterVisible = ref(false)
const filterTarget = ref('')
const multiConditionRef = ref<InstanceType<typeof MultiCondition>>()
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const options = ref()
const heightRef = ref(mainHeight(168, 2.2).height)
const selectChange = (flag: boolean, h: any) => {
const tableParams = ref<any>({})
const selectChange = (_flag: boolean, h: number) => {
heightRef.value = mainHeight(h, 2.2).height
}
const eventList = [
{ label: '电压暂降', value: '1' },
{ label: '电压中断', value: '2' },
{ label: '电压暂升', value: '3' }
]
const getSimpleLineList = async () => {
const res = await getSimpleLine()
options.value = res.data
}
const tableStore: any = new TableStore({
url: '/cs-harmonic-boot/event/pageEvent',
url: '/cs-device-boot/csGroup/deviceDataByType',
method: 'POST',
showPage: true,
exportName: '主要监测点列表',
exportName: '暂态事件',
column: [
{
field: 'index',
title: '序号',
width: '80',
width: 80,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'startTime', title: '发生时刻', minWidth: 180, sortable: true },
{
title: '暂态时间',
field: 'startTime',
minWidth: '180'
},
{
title: '测点名称',
field: 'lineName',
minWidth: '150'
},
{
title: '暂态类型',
field: 'tag',
minWidth: '100'
},
{
title: '特征幅值(%)',
field: 'amplitude',
minWidth: '100'
},
{
title: '暂降深度(%)',
field: 'depth',
minWidth: '100',
field: 'featureAmplitude',
title: '暂降幅值(%)',
minWidth: 120,
sortable: true,
formatter: (row: any) => {
// 当暂态类型不是电压暂升时,计算暂降深度 = 100 - 特征幅值
if (row.row.tag !== '电压暂升') {
const amplitude = parseFloat(row.row.amplitude)
if (!isNaN(amplitude)) {
return 100 - amplitude
}
return '-'
} else {
// 电压暂升时不显示暂降深度
return '/'
row.cellValue = row.cellValue != null ? Number(row.cellValue).toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
return row.cellValue
}
},
{
title: '持续时间(S)',
field: 'persistTime',
minWidth: '100'
title: '持续时间(s)',
minWidth: 110,
formatter: (row: any) => row.cellValue ? (row.cellValue - 0).toFixed(2) : '/',
sortable: true
},
{
field: 'phaseType',
title: '相别',
minWidth: 80,
formatter: (row: any) => row.cellValue || '/'
},
{
title: '严重度',
field: 'severity',
minWidth: '80'
minWidth: 80,
align: 'center',
sortable: true,
formatter: (row: any) => row.cellValue ? row.cellValue : '/'
},
{ field: 'showName', title: '触发类型', minWidth: 100 },
{
title: '暂降原因',
field: 'advanceReason',
minWidth: 100,
align: 'center',
formatter: (row: any) => ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
},
{
title: '波形',
width: '90',
title: '暂降类型',
field: 'advanceType',
minWidth: 100,
align: 'center',
formatter: (row: any) => EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
},
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true },
{
title: '发生位置',
field: 'sagSource',
minWidth: 120,
align: 'center',
formatter: (row: any) => row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
},
{
title: '操作',
fixed: 'right',
width: 150,
render: 'buttons',
buttons: [
{
name: 'edit',
text: '波形分析',
type: 'primary',
text: '暂无波形',
type: 'info',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading1',
disabled: row => {
return !row.wavePath
},
click: async row => {
row.loading1 = true
// 在打开弹窗时立即设置高度
nextTick(() => {
if (waveFormAnalysisRef.value) {
// waveFormAnalysisRef.value.setHeight(false, 360)
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
return row.wavePath
}
})
},
{
name: 'edit',
title: '波形分析',
type: 'primary',
icon: 'el-icon-Check',
render: 'basicButton',
loading: 'loading1',
disabled: (row: any) => !row.wavePath,
click: async (row: any) => {
row.loading1 = true
await analyseWave(row.id)
.then(res => {
row.loading1 = false
if (res != undefined) {
loading.value = true
isWaveCharts.value = true
dialogVisible.value = false
row.loading1 = false
if (res != undefined) {
boxoList.value = row
// boxoList.value = {
// ...row,
// duration: row.persistTime // 将 persistTime 值赋给 duration
// }
// boxoList.value.featureAmplitude =
// row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
boxoList.value.systemType = 'YPT'
boxoList.value = {
...row,
systemType: 'YPT',
engineeringName: tableParams.value.projectName,
featureAmplitude:
row.featureAmplitude != null ? Number(row.featureAmplitude / 100) : '-',
persistTime: row.persistTime ? row.persistTime.toFixed(2) : '-',
}
wp.value = res.data
}
loading.value = false
@@ -202,34 +183,108 @@ const tableStore: any = new TableStore({
})
nextTick(() => {
waveFormAnalysisRef.value &&
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
if (waveFormAnalysisRef.value) {
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value)
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
}
})
}
},
{
name: 'edit',
text: '暂无波形',
type: 'info',
icon: 'el-icon-DataLine',
title: '波形下载',
type: 'primary',
loading: 'loading2',
icon: 'el-icon-Check',
render: 'basicButton',
disabled: row => {
return row.wavePath
disabled: (row: any) => !row.wavePath,
click: (row: any) => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
const blob = new Blob([res as unknown as BlobPart], { type: 'application/zip' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = row.wavePath.split('/')[2] || '波形文件'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
}
]
}
],
beforeSearchFun: () => {},
loadCallback: () => {}
beforeSearchFun: () => {
tableStore.table.params.devId = tableParams.value.devId
tableStore.table.params.lineId = tableParams.value.lineId
tableStore.table.params.list = tableParams.value.list
tableStore.table.params.type = 3
},
loadCallback: () => { }
})
tableStore.table.params.eventType = ''
tableStore.table.params.type = 3
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
provide('tableStore', tableStore)
const open = async (row: any, searchBeginTime: any, searchEndTime: any) => {
tableStore.table.params.eventType = ''
const resetFilterParams = () => {
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
}
const openFilterDialog = () => {
filterVisible.value = true
}
const onFilterConfirm = () => {
tableStore.onTableAction('search', {})
}
const onResetForm = () => {
filterVisible.value = false
resetFilterParams()
multiConditionRef.value?.reset()
tableStore.index()
}
const open = (row: any, searchBeginTime: any, searchEndTime: any, type?: string) => {
filterTarget.value = type || ''
title.value = row.name + '_暂态事件'
dialogVisible.value = true
getSimpleLineList()
tableStore.table.params.lineId = row.id
filterVisible.value = false
resetFilterParams()
multiConditionRef.value?.reset()
const lineId = row.id || row.lineId
tableParams.value = {
lineId,
devId: row.devId,
engineeringName: row.engineeringName,
list: row.devId ? [{ lineId, devId: row.devId }] : undefined,
}
tableStore.table.params.pageNum = 1
nextTick(() => {
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime])
tableStore.table.params.searchBeginTime = searchBeginTime
@@ -245,4 +300,5 @@ const handleHideCharts = () => {
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@@ -1,27 +1,14 @@
<template>
<div>
<!--暂态事件统计 -->
<TableHeader
ref="TableHeaderRef"
:showReset="false"
@selectChange="selectChange"
datePicker
v-if="fullscreen" :timeKeyList="prop.timeKey"
></TableHeader>
<my-echart
class="tall"
:options="echartList"
:style="{
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"
:timeKeyList="prop.timeKey"></TableHeader>
<my-echart class="tall" :options="echartList" :style="{
width: prop.width,
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
}"
/>
<Table
ref="tableRef"
@cell-click="cellClickEvent"
:height="`calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`"
isGroup
></Table>
}" />
<Table ref="tableRef" @cell-click="cellClickEvent"
:height="`calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`" isGroup></Table>
<TransientStatisticsDetail ref="transientStatisticsDetailRef"></TransientStatisticsDetail>
</div>
</template>
@@ -129,7 +116,11 @@ const eventEcharts = () => {
title: [
{
text: '暂态事件统计',
left: 'center'
left: 'center',
textStyle: {
color: '#000',
fontSize: '15'
},
},
{
text: rawData.eventOff + rawData.eventDown + rawData.eventUp + '次',
@@ -173,15 +164,30 @@ const tableStore: any = new TableStore({
}
},
{
title: '名称',
title: '监测点名称',
field: 'name',
minWidth: '90'
minWidth: 120
},
{
title: '设备名称', field: 'devName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '项目名称', field: 'projectName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center', formatter: (row: any) => {
return row.cellValue || '/'
}
},
{
title: '电压中断(次)',
field: 'eventOff',
minWidth: '70',
minWidth: '90',
sortable: true,
render: 'customTemplate',
customTemplate: (row: any) => {
@@ -191,7 +197,7 @@ const tableStore: any = new TableStore({
{
title: '电压暂降(次)',
field: 'eventDown',
minWidth: '80',
minWidth: '90',
sortable: true,
render: 'customTemplate',
customTemplate: (row: any) => {
@@ -201,7 +207,7 @@ const tableStore: any = new TableStore({
{
title: '电压暂升(次)',
field: 'eventUp',
minWidth: '80',
minWidth: '90',
sortable: true,
render: 'customTemplate',
customTemplate: (row: any) => {
@@ -224,11 +230,13 @@ provide('tableStore', tableStore)
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field != 'name') {
if (column.field != 'name' && column.field != 'devName' && column.field != 'projectName' && column.field != 'engineeringName') {
transientStatisticsDetailRef.value.open(
row,
tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
tableStore.table.params.searchEndTime || prop.timeValue?.[1]
tableStore.table.params.searchEndTime || prop.timeValue?.[1],
column.field=='eventOff'?'中断':column.field=='eventDown'?'暂降':column.field=='eventUp'?'暂升':''
)
}
}

View File

@@ -1,25 +1,21 @@
<template>
<div>
<div class="device-control">
<!--趋势对比 -->
<TableHeader
datePicker
ref="TableHeaderRef"
:timeKeyList="prop.timeKey"
:showReset="false"
@selectChange="selectChange"
v-if="fullscreen"
>
<div v-show="fullscreen">
<!-- <PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree> -->
<APFTree :height="flag ? 126 : 70" @node-click="handleNodeClick" @init="handleNodeClick"></APFTree>
</div>
<div>
<TableHeader datePicker ref="TableHeaderRef" :timeKeyList="prop.timeKey" :showReset="false"
@selectChange="selectChange" v-if="fullscreen">
<template v-slot:select>
<el-form-item label="监测对象">
<el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<!-- <el-form-item label="监测对象">
<el-select filterable v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象"
clearable>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form-item> -->
<!-- <el-form-item label="监测点名称">
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称" clearable>
<el-option
@@ -32,21 +28,19 @@
</el-form-item> -->
<el-form-item label="电能质量指标">
<el-select 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-option v-for="item in indicatorList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-radio-group v-model="tableStore.table.params.dataLevel">
<el-radio-group v-model="tableStore.table.params.dataLevel" @change="tableStore.index()">
<el-radio-button label="一次值" value="Primary" />
<el-radio-button label="二次值" value="Secondary" />
</el-radio-group>
</el-form-item>
<el-form-item label="统计类型">
<el-select
style="min-width: 120px !important"
placeholder="请选择"
v-model="tableStore.table.params.valueType"
>
<el-select style="min-width: 120px !important" placeholder="请选择"
v-model="tableStore.table.params.valueType">
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
@@ -54,33 +48,23 @@
</el-select>
</el-form-item>
<el-form-item>
<div v-if="shouldShowHarmonicCount()" style="display: flex; color: var(--el-text-color-regular)">
<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"
>
<el-option
v-for="num in harmonicCountOptions"
:key="num"
:label="num"
:value="num"
></el-option>
<el-select v-model="tableStore.table.params.harmonicCount" placeholder="请选择谐波次数"
style="min-width: 80px !important">
<el-option v-for="num in harmonicCountOptions" :key="num" :label="num"
:value="num"></el-option>
</el-select>
</div>
</el-form-item>
</template>
</TableHeader>
<my-echart
v-loading="tableStore.table.loading"
class="tall"
:options="echartList"
:style="{
width: prop.width,
<my-echart v-loading="tableStore.table.loading" class="tall" :options="echartList" :style="{
width: `calc(${prop.width} - ${fullscreen ? 290 : 0}px)`,
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
}"
/>
}" />
</div>
<!-- <el-empty description="暂无数据" /> -->
</div>
</template>
@@ -94,7 +78,7 @@ import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { getTime } from '@/utils/formatTime'
import { yMethod, exportCSV } from '@/utils/echartMethod'
import { max } from 'lodash'
import APFTree from '@/components/tree/govern/APFTree.vue'
const prop = defineProps({
w: { type: [String, Number] },
h: { type: [String, Number] },
@@ -102,7 +86,8 @@ const prop = defineProps({
height: { type: [String, Number] },
timeKey: { type: Array as () => string[] },
timeValue: { type: Object },
interval: { type: Number }
interval: { type: Number },
flag: { type: Boolean }
})
const TableHeaderRef = ref()
@@ -136,12 +121,26 @@ const initListByIds = () => {
getListByIds({}).then((res: any) => {
if (res.data?.length > 0) {
idList.value = res.data
initCode()
} else {
tableStore.index()
}
})
}
const handleNodeClick = async (data: any) => {
if (data?.level == 3 || data?.level == 2) {
tableStore.table.params.sensitiveUserId = data.id
await tableStore.index()
} else {
tableStore.table.loading = false
}
}
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
headerHeight.value = height
@@ -158,7 +157,7 @@ const initCode = () => {
indicatorList.value = item.data
tableStore.table.params.indicator = indicatorList.value[0].id
nextTick(() => {
tableStore.index()
// tableStore.index()
})
})
})
@@ -192,7 +191,7 @@ const setEchart = () => {
if (!afterGroupedByPhase[phase]) {
afterGroupedByPhase[phase] = []
}
afterGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'dotted'])
afterGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'dashed'])
})
// 构建系列数据
@@ -224,7 +223,6 @@ const setEchart = () => {
},
lineStyle: {
type: 'solid', // 实线
width: 2 // 线条宽度
},
yAxisIndex: 0
})
@@ -249,7 +247,6 @@ const setEchart = () => {
},
lineStyle: {
type: 'dashed', // 虚线
width: 2 // 线条宽度
},
yAxisIndex: 0
})
@@ -285,9 +282,9 @@ const setEchart = () => {
return {
name: item.name,
icon: isBefore
? 'rect'
: 'path://M0,2 L8,2 L8,6 L0,6 Z M12,2 L20,2 L20,6 L12,6 Z M24,2 L32,2 L32,6 L24,6 Z M36,2 L44,2 L44,6 L36,6 Z', // 矩形组成的粗虚线
// icon: isBefore
// ? 'rect'
// : 'path://M0,2 L8,2 L8,6 L0,6 Z M12,2 L20,2 L20,6 L12,6 Z M24,2 L32,2 L32,6 L24,6 Z M36,2 L44,2 L44,6 L36,6 Z', // 矩形组成的粗虚线
itemStyle: {
color: color // 明确指定图例图标的颜色
},
@@ -326,11 +323,16 @@ const setEchart = () => {
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`//`<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
str += `${marker}${el.seriesName.split('(')[0]}${
el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == null ? '' : el.value[2]) : '-'
str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == null ? '' : el.value[2]) : '-'
}<br>`
})
return str
@@ -338,9 +340,11 @@ const setEchart = () => {
},
legend: {
data: legendData,
icon: 'rect',
// icon: 'rect',
itemWidth: 18,
itemHeight: 3,
type: 'scroll',
itemStyle: {
borderWidth: 0
},
@@ -379,9 +383,9 @@ const tableStore: any = new TableStore({
column: [],
beforeSearchFun: () => {
setTime()
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id
}
// if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
// tableStore.table.params.sensitiveUserId = idList.value[0].id
// }
let lists: any = []
// 处理电能质量指标
const selectedIndicator = indicatorList.value?.find(
@@ -420,7 +424,8 @@ tableStore.table.params.valueType = 'avg'
provide('tableStore', tableStore)
onMounted(() => {
initListByIds()
initCode()
// initListByIds()
})
const setTime = () => {
@@ -504,4 +509,11 @@ watch(
// :deep(.el-select) {
// min-width: 80px;
// }
.device-control {
display: flex;
}
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -45,7 +45,7 @@ const initChart = () => {
color: '#000',
textStyle: {
color: '#000',
fontSize: '18'
fontSize: '15'
},
// },
...(props.options?.title || null)
@@ -108,14 +108,14 @@ const initChart = () => {
bottom: '20px',
end: 100,
filterMode: 'none'
filterMode: 'filter'
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100,
filterMode: 'none'
filterMode: 'filter'
}
// {
// show: true,

View File

@@ -684,7 +684,7 @@ const initWave = (
align: 'left'
},
textStyle: {
fontSize: '16px',
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
}
},
@@ -809,13 +809,15 @@ const initWave = (
height: 13,
start: 0,
bottom: '20px',
end: 100
end: 100,
filterMode: 'filter'
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100
end: 100,
filterMode: 'filter'
}
],
series: [
@@ -1027,7 +1029,7 @@ const drawPics = (
left: 'center',
text: '', //titlename || title,
textStyle: {
fontSize: '16px',
fontSize: '15px',
color: props.DColor ? '#000' : echartsColor.WordColor
}
},

View File

@@ -448,7 +448,7 @@ const initWave = (
left: 'center',
text: titleText,
textStyle: {
fontSize: '16px',
fontSize: '14px',
color: props.DColor ? '#000' : echartsColor.WordColor
}
},
@@ -565,13 +565,15 @@ const initWave = (
height: 13,
start: 0,
bottom: '20px',
end: 100
end: 100,
filterMode: 'filter'
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100
end: 100,
filterMode: 'filter'
}
],
series: [

View File

@@ -31,7 +31,7 @@
</el-button>
<el-button
@click="onComSearch"
v-if="showSearch"
v-if="showSearch &&showQuery"
:loading="tableStore.table.loading"
type="primary"
:icon="Search"
@@ -102,6 +102,7 @@ interface Props {
showSearch?: boolean
nextFlag?: boolean //控制时间是否可以往后推
theCurrentTime?: boolean //控制时间前3天展示上个月时间
showQuery?: boolean //是否显示查詢
showReset?: boolean //是否显示重置
showExport?: boolean //导出控制
timeCacheFlag?: boolean //是否取缓存时间
@@ -115,6 +116,7 @@ const props = withDefaults(defineProps<Props>(), {
nextFlag: false,
theCurrentTime: true,
showReset: true,
showQuery: true,
showExport: false,
timeCacheFlag: true,
timeKeyList: () => ['1', '2', '3', '4', '5'] // 修改为箭头函数返回空数组

View File

@@ -0,0 +1,168 @@
<template>
<div class="apf-tree">
<div class="cn-tree" :style="{ height: `calc(100vh - 125px - ${height}px)` }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
</el-input>
</div>
<el-tree style="flex: 1; overflow: auto" :props="defaultProps" highlight-current
:filter-node-method="filterNode" node-key="id" default-expand-all :data="tree" ref="treRef"
@node-click="clickNode" :expand-on-click-node="false">
<template #default="{ node, data: nodeData }">
<span class="custom-tree-node">
<Icon :name="nodeData.icon" style="font-size: 16px" :style="{ color: nodeData.color }"
v-if="nodeData.icon" />
<span style="margin-left: 5px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick } from 'vue'
import { ElTree } from 'element-plus'
import { getUserDevTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config'
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
import { useDictData } from '@/stores/dictData'
import { createLineTreeDecorators } from './lineTreeUtils'
import { bootstrapWithTemplate } from './treeCommonUtils'
import { createTreeFilterNode } from './treeFilterUtils'
interface Props {
template?: boolean
type?: string
height?: number
}
const props = withDefaults(defineProps<Props>(), { template: false, type: 'apf', height: 0 })
defineOptions({ name: 'govern/APFTree', inheritAttrs: false })
const emit = defineEmits(['init', 'node-click', 'deviceTypeChange', 'Policy'])
const config = useConfig()
const dictData = useDictData()
const tree = ref<any[]>([])
const treRef = ref<InstanceType<typeof ElTree>>()
const filterText = ref('')
const defaultProps = { children: 'children', label: 'name', value: 'id' }
const decorators = createLineTreeDecorators(() => config.getColorVal('elementUiPrimary'))
const filterNode = createTreeFilterNode()
watch(filterText, val => treRef.value?.filter(val))
/** 将 { 用户名: 设备[] | null } 转为两级树 */
function transformUserDevTree(data: Record<string, any[] | null>) {
const nodes: any[] = []
const devices: any[] = []
const { primary, statusColor, applyMeta } = decorators
if (!data || typeof data !== 'object') {
return { nodes, devices }
}
Object.entries(data).forEach(([userName, deviceList]) => {
const hasDevices = Array.isArray(deviceList) && deviceList.length > 0
const userId = hasDevices ? deviceList[0]?.monitorUser || userName : `apf-user-${userName}`
const children = hasDevices
? deviceList.map((device: any) => {
const node = {
...device,
level: 2,
pid: userId,
pname: userName
}
applyMeta(node, {
icon: 'el-icon-Platform',
color: statusColor(device.runStatus)
})
devices.push(node)
return node
})
: undefined
const userNode: any = {
id: userId,
name: userName,
level: 1,
...(children ? { children } : {})
}
applyMeta(userNode, { icon: 'el-icon-User', color: primary(), disabled: true })
nodes.push(userNode)
})
return { nodes, devices }
}
async function selectFirstDevice(devices: any[]) {
const node = devices[0]
if (!node) {
emit('init', { ...node })
return
}
await nextTick()
treRef.value?.setCurrentKey(node.id)
emit('init', { level: 2, ...node })
}
async function loadTree() {
tree.value = []
const res = await getUserDevTree({ type: props.type })
const { nodes, devices } = transformUserDevTree(res.data)
tree.value = nodes
await selectFirstDevice(devices)
}
const clickNode = (node: any) => {
if (node?.children?.length) return
emit('node-click', node)
}
bootstrapWithTemplate(
props.template,
loadTree,
() => querySysExcel({ id: dictData.state.area[0]?.id }),
data => emit('Policy', data)
)
watch(() => props.type, () => {
loadTree()
})
</script>
<style lang="scss" scoped>
.apf-tree {
width: 280px;
flex-shrink: 0;
}
.cn-tree {
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 10px;
overflow-y: auto;
:deep(.el-tree) {
border: 1px solid var(--el-border-color);
}
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: var(--el-color-primary-light-7);
}
.custom-tree-node {
display: flex;
}
}
</style>

View File

@@ -77,7 +77,11 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
config.url == '/cs-harmonic-boot/limitRateDetailD/limitProbabilityData' ||
config.url == '/system-boot/dictTree/queryByCode' ||
config.url == '/system-boot/dictTree/queryByid' ||
config.url == '/system-boot/dictTree/query'
config.url == '/system-boot/dictTree/query'||
config.url == '/cs-harmonic-boot/csevent/f47Curve'||
config.url == '/cs-harmonic-boot/sysExcel/querySysExcel'||
config.url == '/cs-device-boot/csLedger/lineTree'||
config.url == '/cs-harmonic-boot/pqSensitiveUser/getUserDevTree'
)
)
removePending(config)

View File

@@ -60,16 +60,16 @@ const tabsList = ref([
])
const rankOptions = ref([
{
value: '1,7',
label: '1级(ERROR)'
value: '1',
label: '1级'
},
{
value: '2,6',
label: '2级(WARN)'
value: '2',
label: '2级'
},
{
value: '3,4,5',
label: '3级(DEBUG,NORMAL)'
value: '3',
label: '3级'
},
// {
// value: '4',

View File

@@ -37,16 +37,16 @@ const props = defineProps(['deviceTree'])
const refheader = ref()
const rankOptions = ref([
{
value: '1,7',
label: '1级(ERROR)'
value: '1',
label: '1级'
},
{
value: '2,6',
label: '2级(WARN)'
value: '2',
label: '2级'
},
{
value: '3,4,5',
label: '3级(DEBUG,NORMAL)'
value: '3',
label: '3级'
},
// {
// value: '4',
@@ -151,12 +151,12 @@ const tableStore = new TableStore({
provide('tableStore', tableStore)
tableStore.table.params.searchValue = ''
tableStore.table.params.level = ''
const deviceTreeOptions = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => {
if (item?.children.length == 0) {
deviceTreeOptions.value.splice(index, 1)
}
})
// const deviceTreeOptions = ref<any>(props.deviceTree)
// deviceTreeOptions.value.map((item: any, index: any) => {
// if (item?.children.length == 0) {
// deviceTreeOptions.value.splice(index, 1)
// }
// })
onMounted(() => {
tableStore.index()

View File

@@ -50,12 +50,8 @@ const props = defineProps(['deviceTree'])
const refheader = ref()
const waveFormAnalysisRef = ref()
const view = ref(true)
const isWaveCharts = ref(false)
const view2 = ref(false)
const showBoxi = ref(true)
const loading = ref(false)
const bxactiveName = ref('ssbx')
const boxoList: any = ref({})
const analysisListRef = ref()
const wp = ref({})
@@ -103,9 +99,6 @@ const tableStore = new TableStore({
return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
}
},
{ title: '监测点名称', field: 'lineName', minWidth: 130, align: 'center' },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
{ title: '设备名称', field: 'equipmentName', minWidth: 130, align: 'center' },
@@ -116,8 +109,6 @@ const tableStore = new TableStore({
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
}
},
{
title: '操作',
fixed: 'right',
@@ -144,12 +135,15 @@ const tableStore = new TableStore({
if (res != undefined) {
loading.value = true
isWaveCharts.value = true
boxoList.value = row
boxoList.value.engineeringName = row.projectName
boxoList.value.persistTime = row.evtParamTm
boxoList.value.featureAmplitude =
row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null
boxoList.value.systemType = 'YPT'
boxoList.value = {
...row,
engineeringName: row.projectName,
persistTime: row.evtParamTm,
featureAmplitude:
row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null,
systemType: 'YPT',
}
wp.value = res.data
}
loading.value = false
@@ -164,27 +158,7 @@ const tableStore = new TableStore({
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
// waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(200, 190)
})
// row.loading1 = true
// view.value = false
// view2.value = true
// loading.value = true
// boxoList.value = row
// boxoList.value.systemType = 'WX'
// boxoList.value.persistTime = row.evtParamTm != '-' ? row.evtParamTm - 0 : null
// await analyseWave(row.id)
// .then(res => {
// row.loading1 = false
// if (res != undefined) {
// wp.value = res.data
// }
// loading.value = false
// })
// .catch(() => {
// row.loading1 = false
// loading.value = false
// })
}
},
{

View File

@@ -15,9 +15,7 @@
</el-tab-pane>
<el-tab-pane label="异常事件" name="5">
<Abnormal v-if="activeName == '5'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
</el-tabs>
</div>
</template>
@@ -26,7 +24,6 @@ import { ref, onMounted, provide } from 'vue'
import Steady from './Steady.vue'
import Transient from './Transient.vue'
import Device from './Device.vue'
import Abnormal from './Abnormal.vue'
import Front from './Front.vue'
import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
defineOptions({

View File

@@ -29,8 +29,8 @@
</div>
</el-form-item>
<el-form-item label="触发类型">
<el-select v-model="params.target" clearable placeholder="请选择触发类型" multiple collapse-tags
style="width: 317px">
<el-select v-model="params.target" :clearable="!hasFixedTarget" placeholder="请选择触发类型" multiple
collapse-tags style="width: 317px">
<el-option v-for="item in eventTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
@@ -51,16 +51,36 @@
</template>
<script setup lang="ts">
import { computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
const getDefaultFilterParams = () => ({
const ALL_EVENT_TYPES = [
{ label: '暂降', value: '暂降' },
{ label: '暂升', value: '暂升' },
{ label: '中断', value: '中断' },
{ label: '瞬态', value: '瞬态' },
{ label: '振荡', value: '振荡' },
]
const parseFixedTargets = (fixedTarget?: string | string[]) => {
if (!fixedTarget) return []
const values = Array.isArray(fixedTarget) ? fixedTarget : fixedTarget.split(/[,]/).map(item => item.trim())
return values.filter(Boolean)
}
const getInitialTarget = (fixedTarget?: string | string[]) => {
const fixed = parseFixedTargets(fixedTarget)
return fixed.length ? [...fixed] : []
}
const getDefaultFilterParams = (fixedTarget?: string | string[]) => ({
featureAmplitudeMin: undefined as number | undefined,
featureAmplitudeMax: undefined as number | undefined,
evtParamTmMin: undefined as number | undefined,
evtParamTmMax: undefined as number | undefined,
severityMin: undefined as number | undefined,
severityMax: undefined as number | undefined,
target: [] as string[],
target: getInitialTarget(fixedTarget),
fileFlag: '' as string | number
})
@@ -68,19 +88,33 @@ const visible = defineModel<boolean>('visible', { default: false })
const props = defineProps<{
params: Record<string, any>
fixedTarget?: string | string[]
}>()
const emit = defineEmits<{
confirm: []
}>()
const eventTypeList = [
{ label: '暂降', value: '暂降' },
{ label: '暂升', value: '暂升' },
{ label: '中断', value: '中断' },
{ label: '瞬态', value: '瞬态' },
{ label: '振荡', value: '振荡' },
]
const hasFixedTarget = computed(() => parseFixedTargets(props.fixedTarget).length > 0)
const eventTypeList = computed(() => {
const fixed = parseFixedTargets(props.fixedTarget)
if (!fixed.length) return ALL_EVENT_TYPES
return fixed.map(value => {
const matched = ALL_EVENT_TYPES.find(item => item.value === value)
return matched || { label: value, value }
})
})
const syncFixedTarget = () => {
const fixed = parseFixedTargets(props.fixedTarget)
if (fixed.length) {
props.params.target = [...fixed]
}
}
watch(() => props.fixedTarget, syncFixedTarget, { immediate: true })
const fileFlagOptions = [
{ label: '存在', value: 1 },
@@ -115,7 +149,8 @@ const validateRange = (
}
const reset = () => {
Object.assign(props.params, getDefaultFilterParams())
Object.assign(props.params, getDefaultFilterParams(props.fixedTarget))
syncFixedTarget()
}
const onConfirm = () => {

View File

@@ -0,0 +1,448 @@
<template>
<div class=" analyze-apf" :style="{ height: pageHeight.height }" v-loading="loading">
<APFTree @node-click="nodeClick" :height="60" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></APFTree>
<div class="analyze-apf-right" v-if="formInline.devId">
<div ref="headerRef">
<TableHeader :showSearch="false" ref="tableHeaderRef" @selectChange="selectChange"
>
<template v-slot:select>
<el-form-item label="时间:">
<DatePicker ref="datePickerRef" :timeKeyList="['3', '4', '5']"></DatePicker>
</el-form-item>
<el-form-item label="统计指标:">
<el-select style="width: 200px" v-model.trim="formInline.statisticalId" filterable
@change="frequencyFlag" placeholder="请选择">
<el-option v-for="item in zblist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="谐波次数:" v-show="frequencyShow">
<el-select v-model.trim="formInline.frequency" filterable placeholder="请选择"
style="width: 100px">
<el-option v-for="item in 49" :key="item + 1" :label="item + 1"
:value="item + 1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="值类型:">
<el-select v-model.trim="formInline.valueType" filterable placeholder="请选择">
<el-option v-for="item in typelist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
<template v-slot:operation>
<el-button type="primary" @click="search" icon="el-icon-Search">查询</el-button>
<el-button :type="timeControl ? 'primary' : ''" icon="el-icon-Sort" @click="setTimeControl">
缺失数据
</el-button>
</template>
</TableHeader>
</div>
<el-empty description="暂无数据" v-if="!echartsData" style="flex: 1"></el-empty>
<template v-else>
<div :style="echartHeight">
<MyEchart :options="echartsData" />
</div>
</template>
</div>
<el-empty v-else :style="echartHeight" description="请选择设备" class="analyze-apf-right" />
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { mainHeight } from '@/utils/layout'
// import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import APFTree from '@/components/tree/govern/APFTree.vue'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { getDevCapacity } from '@/api/cs-device-boot/capacity'
import { queryCommonStatisticalByTime } from '@/api/cs-harmonic-boot/stable'
import DatePicker from '@/components/form/datePicker/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { yMethod, completeTimeSeries } from '@/utils/echartMethod'
import TableHeader from '@/components/table/header/index.vue'
const timeControl = ref(false)
const tableHeaderRef = ref()
const headerRef = ref()
const pageHeight = mainHeight(80)
const echartHeight = ref(mainHeight(130))
const loading = ref(false)
const echartsData = ref<any>(null)
const datePickerRef = ref()
const formInline = reactive({
statisticalId: '',
valueType: '',
startTime: '',
endTime: '',
devId: '',
frequency: ''
})
const dataLists = ref<any[]>([])
const timeFlag = ref(true)
const frequencyShow = ref(false)
const devCapacity = ref(0)
const flag = ref(false)
const typelist = [
{
label: '平均值',
value: 'avg'
},
{
label: '最大值',
value: 'max'
},
{
label: '最小值',
value: 'min'
},
{
label: 'CP95值',
value: 'cp95'
}
]
const zblist = ref<any[]>([])
const init = () => {
return new Promise((resolve, reject) => {
queryByCode('Web_Apf').then(res => {
queryCsDictTree(res.data?.id).then(res => {
zblist.value = res.data.map((item: any) => {
return {
value: item.id,
label: item.name,
...item
}
})
formInline.statisticalId = zblist.value[0]?.value
formInline.valueType = typelist[0].value
resolve(null)
})
})
})
}
const deviceTypeChange = (val: any, obj: any) => {
flag.value = true
nodeClick(obj)
}
const nodeClick = async (e: anyObj) => {
if ((e.level == 2 || e.level == 3)) {
formInline.devId = e.id
loading.value = true
if (zblist.value.length === 0) {
await init()
}
getDevCapacity(formInline.devId)
.then(res => {
devCapacity.value = res.data
search()
})
.catch(() => {
loading.value = false
})
}
}
const lineStyle = [
{ type: 'solid', width: 3 },
{ type: 'dotted', width: 3 },
{ type: 'dashed', width: 3 }
]
const search = () => {
if (timeFlag.value) {
datePickerRef.value && datePickerRef.value.setInterval(5)
timeFlag.value = false
}
loading.value = true
formInline.startTime = datePickerRef.value.timeValue[0]
formInline.endTime = datePickerRef.value.timeValue[1]
if (!frequencyShow.value) {
formInline.frequency = ''
}
queryCommonStatisticalByTime(formInline)
.then(({ data }: { data: any[] }) => {
dataLists.value = data
setEchart()
loading.value = false
})
.catch(() => {
loading.value = false
})
}
const setEchart = () => {
loading.value = true
let data = JSON.parse(JSON.stringify(dataLists.value))
if (data.length) {
let list = processingOfData(data, 'unit')
echartsData.value = {}
let legend: any[] = []
let xAxis: any[] = []
let yAxis: any[] = []
let series: any[] = []
let color: any[] = []
let title = ''
data.forEach(item => {
if (!xAxis.includes(item.time)) {
xAxis.push(item.time)
}
// if (!legend.includes(item.anotherName)) {
// legend.push(item.anotherName)
// }
})
let units = Object.keys(list)
// console.log('🚀 ~ .then ~ units:', units)
for (let unit in list) {
console.log('🚀 ~ .then ~ unit:', unit)
let [min, max] = yMethod(list[unit].map((item: any) => item.statisticalData))
yAxis.push({
name: unit == 'null' ? '' : unit,
type: 'value',
// max: 10,
min: min,
max: max,
// splitNumber: 5,
// minInterval: 1,
axisLine: {
show: true,
//symbol: ["none", "arrow"],
lineStyle: {
color: '#333'
}
}
})
// processingOfData(list[unit], 'anotherName')
let anotherList = processingOfData(list[unit], 'anotherName')
for (let k in anotherList) {
title = k
let lineName = lineStyle[Object.keys(anotherList).indexOf(k)]
let phaseList = processingOfData(anotherList[k], 'phase')
for (let j in phaseList) {
color.push(j == 'A' ? '#DAA520' : j == 'B' ? '#2E8B57' : j == 'C' ? '#A52a2a' : '#0000CC')
legend.push(
j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j
)
series.push({
name: j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j,
symbol: 'none',
smooth: true,
type: 'line',
// data: phaseList[j].map(item => [
// item.time,
// Math.floor(item.statisticalData * 100) / 100,
// unit,
// lineName.type
// ]),
data: timeControl.value
? completeTimeSeries(
phaseList[j].map(item => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
])
)
: phaseList[j].map(item => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
]),
lineStyle: lineName,
yAxisIndex: unit.indexOf(units)
})
}
}
}
echartsData.value = {
title: {
text: zblist.value.filter(item => item.id == formInline.statisticalId)[0].name
},
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == 'null' ? '' : el.value[2]) : '-'
}<br>`
})
return str
}
},
legend: {
itemWidth: 20,
itemHeight: 20,
itemStyle: { opacity: 0 }, //去圆点
type: 'scroll', // 开启滚动分页
top: 25
// data: legend
},
grid: {
left: '20px',
right: '40px',
bottom: '50px',
top: '80px',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
color: color,
xAxis: {
name: '',
type: 'time',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
// boundaryGap: false,
// data: xAxis,
// axisLabel: {
// formatter: function (value: string) {
// return value.split(' ').join('\n')
// }
// },
// axisLine: {
// show: true,
// // symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
},
yAxis: yAxis,
// [
// {
// name: '畸变率:(%)',
// type: 'value',
// // max: 10,
// min: min1,
// max: max1,
// splitNumber: 5,
// minInterval: 1,
// axisLine: {
// show: true,
// //symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
// },
// {
// name: '电流:(A)',
// type: 'value',
// min: min,
// max: max,
// splitNumber: 5,
// minInterval: 1,
// splitLine: {
// show: false
// },
// axisLine: {
// show: true,
// //symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
// }
// ],
options: {
series: series
}
}
} else {
echartsData.value = null
}
loading.value = false
}
const setTimeControl = () => {
timeControl.value = !timeControl.value
setEchart()
}
const processingOfData = (data: any, type: string) => {
let groupedData: any = {}
data.forEach(item => {
if (!groupedData[item[type]]) {
groupedData[item[type]] = []
}
groupedData[item[type]].push(item)
})
return groupedData
}
const frequencyFlag = () => {
let name = zblist.value.filter(item => item.id == formInline.statisticalId)[0].name
if (name.includes('含有率') || name.includes('幅值')) {
frequencyShow.value = true
formInline.frequency = 2
} else {
frequencyShow.value = false
}
tableHeaderRef.value && tableHeaderRef.value?.computedSearchRow()
}
const selectChange = (flag: boolean) => {
setTimeout(() => {
echartHeight.value = mainHeight(23 + headerRef.value.offsetHeight)
}, 100)
}
</script>
<style lang="scss">
.analyze-apf {
display: flex;
&-right {
height: 100%;
overflow: hidden;
flex: 1;
// padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
}
}
</style>
<style lang="scss" scoped>
.el-select {
min-width: 100px;
}
</style>

View File

@@ -1,449 +1,28 @@
<template>
<div class="default-main analyze-apf" :style="{ height: pageHeight.height }" v-loading="loading">
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
<div class="analyze-apf-right" v-if="formInline.devId">
<div ref="headerRef">
<TableHeader :showSearch="false" ref="tableHeaderRef" @selectChange="selectChange"
>
<template v-slot:select>
<el-form-item label="时间:">
<DatePicker ref="datePickerRef" :timeKeyList="['3', '4', '5']"></DatePicker>
</el-form-item>
<el-form-item label="统计指标:">
<el-select style="width: 200px" v-model.trim="formInline.statisticalId" filterable
@change="frequencyFlag" placeholder="请选择">
<el-option v-for="item in zblist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="谐波次数:" v-show="frequencyShow">
<el-select v-model.trim="formInline.frequency" filterable placeholder="请选择"
style="width: 100px">
<el-option v-for="item in 49" :key="item + 1" :label="item + 1"
:value="item + 1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="值类型:">
<el-select v-model.trim="formInline.valueType" filterable placeholder="请选择">
<el-option v-for="item in typelist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
<template v-slot:operation>
<el-button type="primary" @click="search" icon="el-icon-Search">查询</el-button>
<el-button :type="timeControl ? 'primary' : ''" icon="el-icon-Sort" @click="setTimeControl">
缺失数据
</el-button>
</template>
</TableHeader>
</div>
<el-empty description="暂无数据" v-if="!echartsData" style="flex: 1"></el-empty>
<template v-else>
<div :style="echartHeight">
<MyEchart :options="echartsData" />
</div>
</template>
</div>
<el-empty v-else description="请选择设备" class="analyze-apf-right" />
<div class="default-main apf">
<el-tabs type="border-card" v-model="activeName">
<el-tab-pane label="APF" name="1">
<APF v-if="activeName == '1'" />
</el-tab-pane>
<el-tab-pane label="DVR" name="2">
<DVR v-if="activeName == '2'" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { mainHeight } from '@/utils/layout'
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { getDevCapacity } from '@/api/cs-device-boot/capacity'
import { queryCommonStatisticalByTime } from '@/api/cs-harmonic-boot/stable'
import DatePicker from '@/components/form/datePicker/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { yMethod, completeTimeSeries } from '@/utils/echartMethod'
import TableHeader from '@/components/table/header/index.vue'
const timeControl = ref(false)
import APF from './APF.vue'
import DVR from '../DVR/index.vue'
defineOptions({
name: 'govern/analyze/APF'
})
const activeName = ref('1')
const tableHeaderRef = ref()
const headerRef = ref()
const pageHeight = mainHeight(20)
const echartHeight = ref(mainHeight(80))
const loading = ref(false)
const echartsData = ref<any>(null)
const datePickerRef = ref()
const formInline = reactive({
statisticalId: '',
valueType: '',
startTime: '',
endTime: '',
devId: '',
frequency: ''
})
const dataLists = ref<any[]>([])
const timeFlag = ref(true)
const frequencyShow = ref(false)
const devCapacity = ref(0)
const flag = ref(false)
const typelist = [
{
label: '平均值',
value: 'avg'
},
{
label: '最大值',
value: 'max'
},
{
label: '最小值',
value: 'min'
},
{
label: 'CP95值',
value: 'cp95'
}
]
const zblist = ref<any[]>([])
const init = () => {
return new Promise((resolve, reject) => {
queryByCode('Web_Apf').then(res => {
queryCsDictTree(res.data?.id).then(res => {
zblist.value = res.data.map((item: any) => {
return {
value: item.id,
label: item.name,
...item
}
})
formInline.statisticalId = zblist.value[0]?.value
formInline.valueType = typelist[0].value
resolve(null)
})
})
})
}
const deviceTypeChange = (val: any, obj: any) => {
flag.value = true
nodeClick(obj)
}
const nodeClick = async (e: anyObj) => {
if ((e.level == 2 || e.level == 3)) {
formInline.devId = e.id
loading.value = true
if (zblist.value.length === 0) {
await init()
}
getDevCapacity(formInline.devId)
.then(res => {
devCapacity.value = res.data
search()
})
.catch(() => {
loading.value = false
})
}
}
const lineStyle = [
{ type: 'solid', width: 3 },
{ type: 'dotted', width: 3 },
{ type: 'dashed', width: 3 }
]
const search = () => {
if (timeFlag.value) {
datePickerRef.value && datePickerRef.value.setInterval(5)
timeFlag.value = false
}
loading.value = true
formInline.startTime = datePickerRef.value.timeValue[0]
formInline.endTime = datePickerRef.value.timeValue[1]
if (!frequencyShow.value) {
formInline.frequency = ''
}
queryCommonStatisticalByTime(formInline)
.then(({ data }: { data: any[] }) => {
dataLists.value = data
setEchart()
loading.value = false
})
.catch(() => {
loading.value = false
})
}
const setEchart = () => {
loading.value = true
let data = JSON.parse(JSON.stringify(dataLists.value))
if (data.length) {
let list = processingOfData(data, 'unit')
echartsData.value = {}
let legend: any[] = []
let xAxis: any[] = []
let yAxis: any[] = []
let series: any[] = []
let color: any[] = []
let title = ''
data.forEach(item => {
if (!xAxis.includes(item.time)) {
xAxis.push(item.time)
}
// if (!legend.includes(item.anotherName)) {
// legend.push(item.anotherName)
// }
})
let units = Object.keys(list)
// console.log('🚀 ~ .then ~ units:', units)
for (let unit in list) {
console.log('🚀 ~ .then ~ unit:', unit)
let [min, max] = yMethod(list[unit].map((item: any) => item.statisticalData))
yAxis.push({
name: unit == 'null' ? '' : unit,
type: 'value',
// max: 10,
min: min,
max: max,
// splitNumber: 5,
// minInterval: 1,
axisLine: {
show: true,
//symbol: ["none", "arrow"],
lineStyle: {
color: '#333'
}
}
})
// processingOfData(list[unit], 'anotherName')
let anotherList = processingOfData(list[unit], 'anotherName')
for (let k in anotherList) {
title = k
let lineName = lineStyle[Object.keys(anotherList).indexOf(k)]
let phaseList = processingOfData(anotherList[k], 'phase')
for (let j in phaseList) {
color.push(j == 'A' ? '#DAA520' : j == 'B' ? '#2E8B57' : j == 'C' ? '#A52a2a' : '#0000CC')
legend.push(
j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j
)
series.push({
name: j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j,
symbol: 'none',
smooth: true,
type: 'line',
// data: phaseList[j].map(item => [
// item.time,
// Math.floor(item.statisticalData * 100) / 100,
// unit,
// lineName.type
// ]),
data: timeControl.value
? completeTimeSeries(
phaseList[j].map(item => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
])
)
: phaseList[j].map(item => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
]),
lineStyle: lineName,
yAxisIndex: unit.indexOf(units)
})
}
}
}
echartsData.value = {
title: {
text: zblist.value.filter(item => item.id == formInline.statisticalId)[0].name
},
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == 'null' ? '' : el.value[2]) : '-'
}<br>`
})
return str
}
},
legend: {
itemWidth: 20,
itemHeight: 20,
itemStyle: { opacity: 0 }, //去圆点
type: 'scroll', // 开启滚动分页
top: 25
// data: legend
},
grid: {
left: '20px',
right: '40px',
bottom: '50px',
top: '80px',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
color: color,
xAxis: {
name: '',
type: 'time',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
// boundaryGap: false,
// data: xAxis,
// axisLabel: {
// formatter: function (value: string) {
// return value.split(' ').join('\n')
// }
// },
// axisLine: {
// show: true,
// // symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
},
yAxis: yAxis,
// [
// {
// name: '畸变率:(%)',
// type: 'value',
// // max: 10,
// min: min1,
// max: max1,
// splitNumber: 5,
// minInterval: 1,
// axisLine: {
// show: true,
// //symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
// },
// {
// name: '电流:(A)',
// type: 'value',
// min: min,
// max: max,
// splitNumber: 5,
// minInterval: 1,
// splitLine: {
// show: false
// },
// axisLine: {
// show: true,
// //symbol: ["none", "arrow"],
// lineStyle: {
// color: '#333'
// }
// }
// }
// ],
options: {
series: series
}
}
} else {
echartsData.value = null
}
loading.value = false
}
const setTimeControl = () => {
timeControl.value = !timeControl.value
setEchart()
}
const processingOfData = (data: any, type: string) => {
let groupedData: any = {}
data.forEach(item => {
if (!groupedData[item[type]]) {
groupedData[item[type]] = []
}
groupedData[item[type]].push(item)
})
return groupedData
}
const frequencyFlag = () => {
let name = zblist.value.filter(item => item.id == formInline.statisticalId)[0].name
if (name.includes('含有率') || name.includes('幅值')) {
frequencyShow.value = true
formInline.frequency = 2
} else {
frequencyShow.value = false
}
tableHeaderRef.value && tableHeaderRef.value?.computedSearchRow()
}
const selectChange = (flag: boolean) => {
setTimeout(() => {
echartHeight.value = mainHeight(23 + headerRef.value.offsetHeight)
}, 100)
}
</script>
<style lang="scss">
.analyze-apf {
display: flex;
&-right {
height: 100%;
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
}
}
</style>
<style lang="scss" scoped>
.el-select {
min-width: 100px;
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -1,16 +1,22 @@
<template>
<div class="default-main">
<div>
<div class="analyze-dvr" v-show="!isWaveCharts" :style="{ height: pageHeight.height }" v-loading="loading">
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
<!-- <DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree> -->
<APFTree @node-click="nodeClick" :height="60" :type="'dvr'" @init="nodeClick"
@deviceTypeChange="deviceTypeChange"></APFTree>
<div class="analyze-dvr-right" v-if="tableStore.table.params.deviceId">
<TableHeader datePicker showExport>
<TableHeader datePicker showExport @onResetForm="onResetForm">
<template v-slot:select>
<el-form-item label="触发类型">
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="请输入监测点名称" />
</el-form-item>
<!-- <el-form-item label="触发类型">
<el-select v-model.trim="tableStore.table.params.eventType" clearable placeholder="请选择事件类型">
<el-option v-for="item in eventList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-form-item> -->
<!-- <el-form-item label="位置">
<el-select v-model.trim="tableStore.table.params.location" clearable placeholder="请选择位置">
<el-option v-for="item in locationList" :key="item.value" :label="item.label"
@@ -18,45 +24,24 @@
</el-select>
</el-form-item> -->
</template>
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
</TableHeader>
<Table v-if="view" ref="tableRef"></Table>
</div>
<el-empty v-else description="请选择设备" class="analyze-dvr-right" />
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
@confirm="onFilterConfirm" />
<!-- 补召日志 -->
<analysisList ref="analysisListRef"></analysisList>
</div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" style="padding: 10px" />
<!-- <div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
<el-row>
<el-col :span="12">
<div v-if="view2" style="display: flex">
<el-radio-group v-model.trim="value" @change="changeView">
<el-radio-button label="一次值" :value="1" />
<el-radio-button label="二次值" :value="2" />
@handleHideCharts="isWaveCharts = false" :wp="wp" />
</el-radio-group>
</div>
</el-col>
<el-col :span="12">
<el-button v-if="view2" @click="backbxlb" class="el-icon-refresh-right" icon="el-icon-Back"
style="float: right">
返回
</el-button>
</el-col>
</el-row>
<el-tabs v-if="view2" class="default-main" v-model.trim="bxactiveName" @tab-click="bxhandleClick">
<el-tab-pane label="瞬时波形" name="ssbx" class="boxbx pt10 pb10"
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
<shushiboxi v-if="bxactiveName == 'ssbx' && showBoxi" :value="value" :boxoList="boxoList" :wp="wp">
</shushiboxi>
</el-tab-pane>
<el-tab-pane label="RMS波形" class="boxbx pt10 pb10" name="rmsbx"
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
<rmsboxi v-if="bxactiveName == 'rmsbx' && showBoxi" :value="value" :boxoList="boxoList" :wp="wp">
</rmsboxi>
</el-tab-pane>
</el-tabs>
</div> -->
</div>
</template>
@@ -65,91 +50,58 @@ import { ref, nextTick, provide, onMounted } from 'vue'
import { mainHeight } from '@/utils/layout'
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import TableStore from '@/utils/tableStore'
import APFTree from '@/components/tree/govern/APFTree.vue'
import Table from '@/components/table/index.vue'
import analysisList from '@/views/govern/device/control/analysisList/index.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import { analyseWave } from '@/api/common'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import MultiCondition from '@/views/govern/alarm/multiCondition.vue'
import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage } from 'element-plus'
defineOptions({
name: 'govern/analyze/DVR/index'
})
const pageHeight = mainHeight(20)
const DeviceType = ref([])
const pageHeight = mainHeight(80)
const loading = ref(false)
const view = ref(true)
const view2 = ref(false)
const showBoxi = ref(true)
const isWaveCharts = ref(false)
const bxactiveName = ref('ssbx')
const analysisListRef = ref()
const boxoList: any = ref({})
const wp = ref({})
const eventList = ref([
{
value: 'Evt_Sys_DipStr',
label: '电压暂降'
},
{
value: 'Evt_Sys_SwlStr',
label: '电压暂升'
},
{
value: 'Evt_Sys_IntrStr',
label: '电压中断'
}
])
const locationList = ref([
{
value: 'grid',
label: '电网侧'
},
{
value: 'load',
label: '负载侧'
}
])
const filterVisible = ref(false)
const waveFormAnalysisRef = ref()
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const multiConditionRef = ref<InstanceType<typeof MultiCondition>>()
const tableStore = new TableStore({
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
method: 'POST',
publicHeight: 60,
column: [
{
field: 'index',
title: '序号',
width: '80',
width: 80,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '暂降幅值(%)', minWidth: 120, field: 'evtParamVVaDepth', align: 'center', sortable: true, },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true },
{
title: '暂降幅值(%)',
field: 'evtParamVVaDepth',
minWidth: 120,
formatter: (row: any) => {
let a = row.cellValue.split('%')[0] - 0
return a ? a.toFixed(2) : '-'
},
sortable: true
},
{
title: '持续时间(s)',
field: 'evtParamTm',
sortable: true,
minWidth: 110,
formatter: (row: any) => {
const val = row.cellValue
if (val === null || val === undefined || val === '' ) return '-'
const num = Number(val)
if (Number.isNaN(num)) return '-'
return Math.floor(num * 10000) / 100
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: 'showName', minWidth: 100 },
{ title: '触发类型', field: 'showName', minWidth: 100, align: 'center' },
{
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.id == row.cellValue)?.name || '未知'
@@ -160,7 +112,14 @@ const tableStore = new TableStore({
return EventTypeList.find((item: any) => item.id == row.cellValue)?.name || '未知'
}
},
{ title: '监测点名称', field: 'lineName', minWidth: 130, align: 'center' },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
{ title: '设备名称', field: 'equipmentName', minWidth: 130, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 130, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 130, align: 'center' },
{
title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => {
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
@@ -168,43 +127,40 @@ const tableStore = new TableStore({
},
{
title: '操作',
fixed: 'right',
align: 'center',
width: '180',
render: 'buttons',
buttons: [
{
name: 'edit',
text: '波形分析',
title: '波形分析',
type: 'primary',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading1',
disabled: row => {
return !row.wavePath && row.evtParamTm < 20
return !row.wavePath
},
click: async row => {
row.loading1 = true
await analyseWave(row.id)
.then(res => {
isWaveCharts.value = true
loading.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row
boxoList.value.featureAmplitude =
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth.split('%')[0] / 100 : null
boxoList.value.persistTime =
row.evtParamTm != '-' ? Math.floor(row.evtParamTm * 10000) / 100 : null
// boxoList.value.systemType = 'WX'
boxoList.value.systemType = 'YPT'
loading.value = true
isWaveCharts.value = true
boxoList.value = {
...row,
engineeringName: row.projectName,
persistTime: row.evtParamTm,
featureAmplitude:
row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null,
systemType: 'YPT',
}
wp.value = res.data
}
loading.value = false
@@ -217,18 +173,9 @@ const tableStore = new TableStore({
nextTick(() => {
waveFormAnalysisRef.value &&
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(false, 150)
// waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(200, 190)
})
}
},
{
name: 'edit',
text: '暂无波形',
type: 'info',
icon: 'el-icon-DataLine',
render: 'basicButton',
disabled: row => {
return !(!row.wavePath && row.evtParamTm < 20)
}
},
{
@@ -239,7 +186,6 @@ const tableStore = new TableStore({
loading: 'loading2',
render: 'basicButton',
disabled: row => {
// && row.evtParamTm < 20
return !row.wavePath
},
click: row => {
@@ -260,17 +206,78 @@ const tableStore = new TableStore({
row.loading2 = false
})
}
},
{
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
})
}
}
},
]
}
],
loadCallback: () => {
tableStore.table.data.forEach((item: any) => {
item.loading = false
item.evtParamTm = item.evtParamTm.split('s')[0]
item.evtParamTm =
item.evtParamTm.split('s')[0] != '-' ? (item.evtParamTm.split('s')[0] - 0).toFixed(2) : '-'
item.evtParamVVaDepth =
item.evtParamVVaDepth.split('%')[0] != '-' ? (item.evtParamVVaDepth.split('%')[0] - 0).toFixed(2) : '-'
})
}
})
Object.assign(tableStore.table.params, {
featureAmplitudeMin: undefined,
featureAmplitudeMax: undefined,
evtParamTmMin: undefined,
evtParamTmMax: undefined,
severityMin: undefined,
severityMax: undefined,
fileFlag: ''
})
const flag = ref(false)
tableStore.table.params.type = 0
tableStore.table.params.eventType = ''
@@ -282,7 +289,7 @@ const deviceTypeChange = (val: any, obj: any) => {
}
const nodeClick = async (e: anyObj) => {
// console.log("🚀 ~ nodeClick ~ e:", e)
if ((e.level == 2 || e.level == 3) ) {
if ((e.level == 2 || e.level == 3)) {
loading.value = false
tableStore.table.params.deviceId = e.id
nextTick(() => {
@@ -290,25 +297,31 @@ const nodeClick = async (e: anyObj) => {
})
}
}
const openFilterDialog = () => {
filterVisible.value = true
}
const onFilterConfirm = () => {
tableStore.onTableAction('search', {})
}
// 重置
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()
}
onMounted(() => {
queryByCode('Device_Type').then(res => {
queryCsDictTree(res.data.id).then((list: any) => {
DeviceType.value = list.data
})
})
})
const changeView = () => {
showBoxi.value = false
setTimeout(() => {
showBoxi.value = true
}, 0)
}
const bxhandleClick = (tab: any) => {
if (tab.name == 'ssbx') {
bxactiveName.value = 'ssbx'
} else if (tab.name == 'rmsbx') {
bxactiveName.value = 'rmsbx'
}
// console.log(tab, event);
}
const backbxlb = () => {
view.value = true
view2.value = false
}
const bxecharts = mainHeight(95).height as any
</script>
@@ -320,7 +333,7 @@ const bxecharts = mainHeight(95).height as any
height: 100%;
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
// padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
}

View File

@@ -47,7 +47,7 @@
</el-form-item>
<el-form-item style="position: relative; z-index: 2" v-else>
<span style="font-size: 16px; font-weight: bold; color: var(--el-color-primary)">
治理设备/便携式设备不支持操作
MQTT通讯方式的设备不支持修改
</span>
</el-form-item>
@@ -70,7 +70,7 @@
<el-form class="main-form overview_scroll" :label-position="'right'" label-width="130px"
:inline="true" ref="mainForm" :model="formData">
<!--工程-->
<el-form-item id="id300" class="form-item" label="工程名称:"
<el-form-item id="id300" class="form-item" label="工程名称"
v-if="nodeLevel > 0 || pageStatus == 2" prop="engineeringParam.name"
:rules="{ required: true, message: '请输入工程名称', trigger: 'blur' }">
<el-input maxlength="32" show-word-limit clearable v-model="formData.engineeringParam.name" placeholder="请输入工程名称"
@@ -78,7 +78,7 @@
"></el-input>
</el-form-item>
<!-- 省下拉框 -->
<el-form-item id="id200" class="form-item" label="省:" prop="engineeringParam.province"
<el-form-item id="id200" class="form-item" label="省" prop="engineeringParam.province"
v-if="nodeLevel > 0 || pageStatus == 2"
:rules="{ required: true, message: '请选择省', trigger: 'change' }">
<el-select clearable filterable v-model="formData.engineeringParam.province" :disabled="!((nodeLevel == 1 && pageStatus == 3) || (nodeLevel == 0 && pageStatus == 2))
@@ -88,7 +88,7 @@
</el-select>
</el-form-item>
<!-- 市下拉框 -->
<el-form-item id="id200" class="form-item" label="市:" prop="engineeringParam.city"
<el-form-item id="id200" class="form-item" label="市" prop="engineeringParam.city"
v-if="nodeLevel > 0 || pageStatus == 2"
:rules="{ required: true, message: '请选择市', trigger: 'change' }">
<el-select clearable filterable v-model="formData.engineeringParam.city" :disabled="!((nodeLevel == 1 && pageStatus == 3) || (nodeLevel == 0 && pageStatus == 2))
@@ -98,13 +98,13 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="描述:" prop="engineeringParam.description"
<el-form-item class="form-item" label="描述" prop="engineeringParam.description"
v-if="nodeLevel > 0 || pageStatus == 2">
<el-input maxlength="32" show-word-limit clearable v-model="formData.engineeringParam.description" placeholder="请输入描述"
:disabled="!((nodeLevel == 1 && pageStatus == 3) || (nodeLevel == 0 && pageStatus == 2))
"></el-input>
</el-form-item>
<el-form-item class="form-item" label="排序:" prop="engineeringParam.sort"
<el-form-item class="form-item" label="排序" prop="engineeringParam.sort"
v-if="nodeLevel > 0 || pageStatus == 2"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="formData.engineeringParam.sort"
@@ -120,7 +120,7 @@
<el-tab-pane v-for="(item, index) in formData.projectInfoList" :key="index"
:label="item.name ? item.name : '新建项目' + index" :name="index + ''">
<div class="flex mt10">
<el-form-item class="form-item" label="项目名称:"
<el-form-item class="form-item" label="项目名称"
:prop="'projectInfoList[' + index + '].name'" :rules="[
{ required: true, message: '请输入项目名称', trigger: 'blur' }
]">
@@ -131,7 +131,7 @@
)
"></el-input>
</el-form-item>
<el-form-item class="form-item" label="地址:"
<el-form-item class="form-item" label="地址"
:prop="'projectInfoList[' + index + '].area'"
:rules="[{ required: true, message: '请输入地址', trigger: 'blur' }]">
<el-input maxlength="32" show-word-limit clearable v-model="item.area" placeholder="请输入地址" :disabled="!(
@@ -141,7 +141,7 @@
)
"></el-input>
</el-form-item>
<el-form-item class="form-item" label="描述:"
<el-form-item class="form-item" label="描述"
:prop="'projectInfoList[' + index + '].description'"
:rules="[{ required: true, message: '请输入描述', trigger: 'blur' }]">
<el-input maxlength="300" show-word-limit clearable v-model="item.description" placeholder="请输入描述"
@@ -152,7 +152,7 @@
)
"></el-input>
</el-form-item>
<el-form-item label="拓扑图:" :prop="'projectInfoList[' + index + '].topoId'"
<el-form-item label="拓扑图" :prop="'projectInfoList[' + index + '].topoId'"
:rules="[
{ required: true, message: '请选择拓扑图', trigger: 'change' }
]">
@@ -175,7 +175,7 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="排序:"
<el-form-item class="form-item" label="排序"
:prop="'projectInfoList[' + index + '].sort'"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="item.sort" placeholder="请输入排序"
@@ -202,7 +202,7 @@
<el-tab-pane v-for="(busItem, bIndex) in formData.deviceInfoList" :key="bIndex"
:label="busItem.name ? busItem.name : '新建设备' + bIndex" :name="bIndex + ''">
<div class="flex mt10">
<el-form-item class="form-item" label="设备名称:"
<el-form-item class="form-item" label="设备名称"
:prop="'deviceInfoList[' + bIndex + '].name'" :rules="[
{ required: true, message: '请输入设备名称', trigger: 'blur' }
]">
@@ -214,7 +214,7 @@
)
"></el-input>
</el-form-item>
<el-form-item id="id200" class="form-item" label="设备类型:"
<el-form-item id="id200" class="form-item" label="设备类型"
:prop="'deviceInfoList[' + bIndex + '].devType'" :rules="[
{ required: true, message: '请选择设备类型', trigger: 'change' }
]">
@@ -231,7 +231,7 @@
:value=" item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item id="id200" class="form-item" label="设备型号:"
<el-form-item id="id200" class="form-item" label="设备型号"
:prop="'deviceInfoList[' + bIndex + '].devModel'" :rules="[
{ required: true, message: '请选择设备型号', trigger: 'change' }
]">
@@ -248,12 +248,12 @@
:value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item id="id200" class="form-item" label="设备接入方式:"
<el-form-item id="id200" class="form-item" label="设备通讯方式"
:prop="'deviceInfoList[' + bIndex + '].devAccessMethod'" :rules="[
{ required: true, message: '请选择设备接入方式', trigger: 'change' }
{ required: true, message: '请选择设备通讯方式', trigger: 'change' }
]">
<el-select clearable filterable v-model="busItem.devAccessMethod"
placeholder="请选择设备接入方式" style="width: 100%" :disabled="!(
placeholder="请选择设备通讯方式" style="width: 100%" :disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
((nodeLevel == 2 || (nodeLevel == 1 && pageStatus == 2)) &&
pageStatus == 2)
@@ -264,7 +264,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="设备mac地址:"
<el-form-item class="form-item" label="设备mac地址"
:prop="'deviceInfoList[' + bIndex + '].mac'" :rules="{
required: true,
message: '请输入设备mac地址',
@@ -275,7 +275,7 @@
</el-form-item>
<el-form-item class="form-item" v-if="busItem.devAccessMethod == 'CLD'"
label="icd模型:" :prop="'deviceInfoList[' + bIndex + '].idc'">
label="icd模型" :prop="'deviceInfoList[' + bIndex + '].idc'">
<el-select clearable filterable v-model="busItem.icd"
placeholder="请选择icd模型" style="width: 100%" :disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
@@ -288,7 +288,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="合同号:"
<el-form-item class="form-item" label="合同号"
:prop="'deviceInfoList[' + bIndex + '].cntractNo'">
<el-input maxlength="32" show-word-limit clearable v-model="busItem.cntractNo" placeholder="请输入合同号"
:disabled="!(
@@ -299,7 +299,7 @@
"></el-input>
</el-form-item>
<el-form-item v-if="busItem.devAccessMethod == 'CLD'" class="form-item"
label="所属前置机:" :prop="'deviceInfoList[' + bIndex + '].nodeId'" :rules="[
label="所属前置机" :prop="'deviceInfoList[' + bIndex + '].nodeId'" :rules="[
{ required: true, message: '请选择所属前置机', trigger: 'change' }
]">
<el-select clearable filterable v-model="busItem.nodeId"
@@ -310,12 +310,12 @@
</el-select>
</el-form-item>
<el-form-item v-if="busItem.devAccessMethod == 'CLD'" class="form-item"
label="进程号:" :prop="'deviceInfoList[' + bIndex + '].nodeProcess'">
label="进程号" :prop="'deviceInfoList[' + bIndex + '].nodeProcess'">
<el-input maxlength="32" show-word-limit clearable v-model="busItem.nodeProcess" placeholder="自动分配"
:disabled="true"></el-input>
</el-form-item>
<el-form-item v-if="busItem.devAccessMethod == 'CLD'" class="form-item"
label="日志等级:" :prop="'deviceInfoList[' + bIndex + '].devLogLevel'">
label="日志等级" :prop="'deviceInfoList[' + bIndex + '].devLogLevel'">
<el-select clearable filterable v-model="busItem.devLogLevel"
placeholder="请选择日志等级" style="width: 100%" :disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
@@ -327,7 +327,7 @@
:label="value.label" :value="value.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="是否启用:"
<el-form-item class="form-item" label="是否启用"
:prop="'deviceInfoList[' + bIndex + '].usageStatus'">
<el-select clearable filterable v-model="busItem.usageStatus"
placeholder="请选择是否启用" style="width: 100%" :disabled="!(
@@ -340,7 +340,7 @@
<el-option label="停用" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="是否支持升级:"
<el-form-item class="form-item" label="是否支持升级"
v-if="busItem.devAccessMethod == 'CLD'"
:prop="'deviceInfoList[' + bIndex + '].upgrade'">
<el-select clearable filterable v-model="busItem.upgrade"
@@ -355,7 +355,7 @@
</el-select>
</el-form-item>
<!-- <el-form-item
label="接入:"
label="接入"
v-if="connectionMethod == 'MQTT' && busItem?.id"
>
<el-button
@@ -368,7 +368,7 @@
{{ accessLoading ? '接入中' : '接入' }}
</el-button>
</el-form-item> -->
<el-form-item class="form-item" label="排序:"
<el-form-item class="form-item" label="排序"
:prop="'deviceInfoList[' + bIndex + '].sort'"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="busItem.sort"
@@ -395,7 +395,7 @@
<el-tab-pane v-for="(lineItem, lIndex) in formData.lineInfoList" :key="lIndex"
:label="lineItem.name ? lineItem.name : '新建监测点' + lIndex" :name="lIndex + ''">
<div class="flex mt10" v-if="connectionMethod != 'MQTT'">
<el-form-item class="form-item" label="监测点名称:"
<el-form-item class="form-item" label="监测点名称"
:prop="'lineInfoList[' + lIndex + '].name'" :rules="{
required: true,
message: '请输入监测点名称',
@@ -409,7 +409,7 @@
)
"></el-input>
</el-form-item>
<el-form-item class="form-item" label="线路号:"
<el-form-item class="form-item" label="线路号"
:prop="'lineInfoList[' + lIndex + '].lineNo'" :rules="{
required: true,
message: '请选择线路号',
@@ -426,7 +426,7 @@
:label="option.name" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="接线方式:"
<el-form-item class="form-item" label="接线方式"
:prop="'lineInfoList[' + lIndex + '].conType'" :rules="{
required: true,
message: '请选择接线方式',
@@ -443,7 +443,7 @@
:label="option.name" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="电压等级:"
<el-form-item class="form-item" label="电压等级"
:prop="'lineInfoList[' + lIndex + '].volGrade'" :rules="{
required: true,
message: '请选择电压等级',
@@ -462,7 +462,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="PT变比:"
<el-form-item class="form-item" label="PT变比"
:prop="'lineInfoList[' + lIndex + '].ptRatio'"
:rules="{ required: true, message: '请输入pt', trigger: 'blur' }">
<div style="width: 100%; display: flex; justify-content: space-between">
@@ -493,7 +493,7 @@
"></el-input>
</div>
</el-form-item>
<el-form-item class="form-item" label="CT变比:"
<el-form-item class="form-item" label="CT变比"
:prop="'lineInfoList[' + lIndex + '].ctRatio'"
:rules="{ required: true, message: '请输入ct', trigger: 'blur' }">
<div style="width: 100%; display: flex; justify-content: space-between">
@@ -524,7 +524,7 @@
"></el-input>
</div>
</el-form-item>
<el-form-item class="form-item" label="统计间隔:"
<el-form-item class="form-item" label="统计间隔"
:prop="'lineInfoList[' + lIndex + '].lineInterval'" :rules="{
required: true,
message: '请选择统计间隔',
@@ -542,7 +542,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="基准容量(MVA):"
<el-form-item class="form-item" label="基准容量(MVA)"
:prop="'lineInfoList[' + lIndex + '].basicCapacity'"
:rules="{ required: true, message: '请输入基准容量', trigger: 'blur' }">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
@@ -553,7 +553,7 @@
)
" placeholder="请输入基准容量(MVA)"></el-input>
</el-form-item>
<el-form-item class="form-item" label="短路容量(MVA):"
<el-form-item class="form-item" label="短路容量(MVA)"
:prop="'lineInfoList[' + lIndex + '].shortCircuitCapacity'"
:rules="{ required: true, message: '请输入短路容量', trigger: 'blur' }">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
@@ -565,7 +565,7 @@
)
" placeholder="请输入短路容量(MVA)"></el-input>
</el-form-item>
<el-form-item class="form-item" label="设备容量(MVA):"
<el-form-item class="form-item" label="设备容量(MVA)"
:prop="'lineInfoList[' + lIndex + '].devCapacity'"
:rules="{ required: true, message: '请输入设备容量', trigger: 'blur' }">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
@@ -576,7 +576,7 @@
)
" placeholder="请输入设备容量(MVA)"></el-input>
</el-form-item>
<el-form-item class="form-item" label="协议容量(MVA):"
<el-form-item class="form-item" label="协议容量(MVA)"
:prop="'lineInfoList[' + lIndex + '].protocolCapacity'"
:rules="{ required: true, message: '请输入协议容量', trigger: 'blur' }">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
@@ -587,7 +587,7 @@
)
" placeholder="请输入协议容量(MVA)"></el-input>
</el-form-item>
<el-form-item class="form-item" label="运行状态:"
<el-form-item class="form-item" label="运行状态"
:prop="'lineInfoList[' + lIndex + '].runStatus'" :rules="{
required: true,
message: '请选择运行状态',
@@ -609,9 +609,9 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="用户对象:">
<!-- <el-form-item class="form-item" label="敏感用户">
<el-select clearable filterable v-model="lineItem.monitorUser"
placeholder="请选择用户对象" :disabled="!(
placeholder="请选择敏感用户" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
((nodeLevel == 3 || (nodeLevel == 2 && pageStatus == 2)) &&
pageStatus == 2)
@@ -620,8 +620,8 @@
<el-option v-for="option in userList" :key="option.id"
:label="option.name" :value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="监测对象类型:"
</el-form-item> -->
<el-form-item class="form-item" label="监测对象类型"
>
<el-select clearable filterable v-model="lineItem.monitorObj"
placeholder="请选择监测对象类型" :disabled="!(
@@ -634,7 +634,7 @@
:label="option.name" :value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="监测位置:"
<el-form-item class="form-item" label="监测位置"
>
<el-select clearable filterable v-model="lineItem.position"
placeholder="请选择监测位置" :disabled="!(
@@ -647,7 +647,7 @@
:label="option.name" :value="option.id"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="是否治理:"
<!-- <el-form-item class="form-item" label="是否治理"
>
<el-select clearable filterable v-model="lineItem.govern"
placeholder="请选择是否治理" :disabled="!(
@@ -659,9 +659,9 @@
<el-option label="未治理" :value="0" />
<el-option label="已治理" :value="1" />
</el-select>
</el-form-item>
</el-form-item> -->
<el-form-item class="form-item" label="日志等级:"
<el-form-item class="form-item" label="日志等级"
:prop="'lineInfoList[' + lIndex + '].lineLogLevel'" :rules="{
required: true,
message: '请选择日志等级',
@@ -679,7 +679,7 @@
:label="value.label" :value="value.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="是否主要监测点:"
<el-form-item class="form-item" label="是否主要监测点"
:prop="'lineInfoList[' + lIndex + '].isImportant'" :rules="{
required: true,
message: '请选择是否主要监测点',
@@ -699,32 +699,32 @@
</el-form-item>
</div>
<div class="flex mt10" v-else>
<el-form-item class="form-item" label="监测点名称:">
<el-form-item class="form-item" label="监测点名称">
<el-input maxlength="32" show-word-limit clearable v-model="lineItem.name" placeholder="请输入监测点名称"
:disabled="true"></el-input>
</el-form-item>
<el-form-item class="form-item" label="线路号:">
<!-- <el-form-item class="form-item" label="线路号">
<el-select clearable filterable v-model="lineItem.lineNo"
placeholder="请选择线路号" :disabled="true">
<el-option v-for="option in pointNumArr" :key="option.name"
:label="option.name" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="接线方式:">
</el-form-item> -->
<el-form-item class="form-item" label="接线方式">
<el-select clearable filterable v-model="lineItem.conType"
placeholder="请选择接线方式" :disabled="true">
<el-option v-for="option in wiringTypeArr" :key="option.name"
:label="option.name" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="统计间隔:">
<el-form-item class="form-item" label="统计间隔">
<el-select clearable filterable v-model="lineItem.lineInterval"
placeholder="请选择统计间隔" :disabled="true">
<el-option v-for="option in lineSpaceArr" :key="option.name"
:label="option.name" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item class="form-item" label="PT变比:">
<el-form-item class="form-item" label="PT变比">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
@@ -741,7 +741,7 @@
v-model.number="lineItem.pt2Ratio" :disabled="true"></el-input>
</div>
</el-form-item>
<el-form-item class="form-item" label="CT变比:">
<el-form-item class="form-item" label="CT变比">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
@@ -759,7 +759,7 @@
</div>
</el-form-item>
<el-form-item class="form-item" label="电压等级:">
<el-form-item class="form-item" label="电压等级">
<el-select clearable filterable v-model="lineItem.volGrade"
placeholder="请选择电压等级" :disabled="true">
<el-option v-for="option in voltageLevelOptions" :key="option.value"
@@ -767,7 +767,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="监测位置:">
<el-form-item class="form-item" label="监测位置">
<el-select clearable filterable v-model="lineItem.position"
placeholder="请选择监测位置" :disabled="true">
<el-option v-for="option in linePosition" :key="option.id"
@@ -775,8 +775,7 @@
</el-select>
</el-form-item>
<el-form-item class="form-item" label="运行状态:">
<!-- 0运行1检修2停运3调试4退运 -->
<!-- <el-form-item class="form-item" label="运行状态">
<el-select clearable filterable v-model="lineItem.runStatus"
placeholder="请选择通讯状态" :disabled="true">
<el-option label="运行" :value="0" />
@@ -785,7 +784,7 @@
<el-option label="调试" :value="3" />
<el-option label="退运" :value="4" />
</el-select>
</el-form-item>
</el-form-item> -->
</div>
</el-tab-pane>
</el-tabs>
@@ -2816,7 +2815,7 @@ const area = async () => {
queryCsDictTree(res.data.id).then(res => {
devTypeOptions.value = res.data
console.log("🚀 ~ area ~ devTypeOptions.value:", devTypeOptions.value)
console.log("🚀 ~ area ~ devTypeOptions.value", devTypeOptions.value)
})
})
})

View File

@@ -1208,6 +1208,7 @@ const handleClick = async (tab?: any) => {
}
//运行趋势
if (dataSet.value.includes('_devRunTrend')) {
tableLoading.value=true
setTimeout(async () => {
if (tab.props != undefined) await (datePickerRef.value && datePickerRef.value?.setInterval(5))

View File

@@ -5,18 +5,26 @@
<template v-for="(section, sectionIndex) in tableSections" :key="sectionIndex">
<vxe-table class="near-realtime-table" border height="" width="100%" :data="[section.row]"
:column-config="tableColumnConfig" :tooltip-config="tableTooltipConfig">
<vxe-colgroup v-for="(item, colIndex) in section.abcItems" :key="`abc-${colIndex}`" align="center"
:title="item.otherName" :width="getMetricWidth(section)">
<vxe-column align="center" :field="`v${colIndex}A`" :title="item.phaseLabels[0]"
<template v-for="(item, colIndex) in section.columns" :key="colIndex">
<vxe-colgroup v-if="item.type === 'abc'" align="center" :title="getPrimaryTitle(item.data)"
:width="getMetricWidth(section)">
<vxe-column align="center" :field="`v${colIndex}A`" :title="item.data.phaseLabels![0]"
:width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}B`" :title="item.phaseLabels[1]"
<vxe-column align="center" :field="`v${colIndex}B`" :title="item.data.phaseLabels![1]"
:width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}C`" :title="item.phaseLabels[2]"
<vxe-column align="center" :field="`v${colIndex}C`" :title="item.data.phaseLabels![2]"
:width="getPhaseWidth(section)" :formatter="cellFormatter"></vxe-column>
</vxe-colgroup>
<vxe-column v-for="(item, colIndex) in section.scalarItems" :key="`scalar-${colIndex}`"
align="center" :field="`s${colIndex}`" :title="item.otherName" :width="getMetricWidth(section)"
show-overflow :formatter="cellFormatter"></vxe-column>
<vxe-colgroup v-else-if="item.type === 't-multi'" align="center"
:title="getPrimaryTitle(item.data)" :width="getMetricWidth(section)">
<vxe-column v-for="(sub, subIndex) in item.data.subItems" :key="subIndex" align="center"
:field="`t${colIndex}_${subIndex}`" :title="sub.subTitle"
:width="getSubColWidth(section, item.data.subItems!.length)"
:formatter="cellFormatter"></vxe-column>
</vxe-colgroup>
<vxe-column v-else align="center" :field="`s${colIndex}`" :title="getScalarTitle(item.data)"
:width="getMetricWidth(section)" show-overflow :formatter="cellFormatter"></vxe-column>
</template>
<vxe-column v-for="emptyIndex in section.emptySlotCount" :key="`empty-${emptyIndex}`" align="center"
:width="getMetricWidth(section)"></vxe-column>
</vxe-table>
@@ -32,27 +40,36 @@
import { mainHeight } from '@/utils/layout'
import { ref } from 'vue'
const ROW_WITH_ABC = 4
const ROW_WITH_GROUP = 4
const ROW_SCALAR_ONLY = 5
const SECONDARY_KEYWORDS = ['正序', '负序', '零序', '无功', '有功', '视在'] as const
interface SubColumn {
subTitle: string
value: unknown
}
interface DisplayMetric {
name: string
otherName: string
unit?: string | null
type: 'abc' | 'scalar' | 't-multi'
valueA?: unknown
valueB?: unknown
valueC?: unknown
valueM?: unknown
phaseLabels: [string, string, string]
sort: number
phaseLabels?: [string, string, string]
subItems?: SubColumn[]
}
interface MetricItem {
type: 'abc' | 'scalar'
type: 'abc' | 'scalar' | 't-multi'
data: DisplayMetric
}
interface TableSection {
abcItems: DisplayMetric[]
scalarItems: DisplayMetric[]
columns: MetricItem[]
row: Record<string, unknown>
slotsPerRow: number
emptySlotCount: number
@@ -64,6 +81,7 @@ interface RawMetricItem {
unit?: string | null
phase?: string
sort?: number
otherNameSort?: number
data?: unknown
otherName?: string
valueA?: unknown
@@ -72,9 +90,14 @@ interface RawMetricItem {
valueM?: unknown
}
interface NameGroup {
name: string
items: RawMetricItem[]
}
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] },
{ keys: ['AB', 'BC', 'CA'], labels: ['AB', 'BC', 'CA'] as [string, string, string] },
]
const height = mainHeight(345)
@@ -87,6 +110,9 @@ const getMetricWidth = (section: TableSection) => `${100 / section.slotsPerRow}%
const getPhaseWidth = (section: TableSection) => `${100 / section.slotsPerRow / 3}%`
const getSubColWidth = (section: TableSection, subCount: number) =>
`${100 / section.slotsPerRow / subCount}%`
const formatCellValue = (value: unknown): string | number => {
if (value == null || value === 3.14159) return '/'
return typeof value === 'number' || typeof value === 'string' ? value : String(value)
@@ -94,140 +120,200 @@ const formatCellValue = (value: unknown): string | number => {
const cellFormatter = ({ cellValue }: { cellValue: unknown }) => formatCellValue(cellValue)
const buildOtherName = (name: string, unit?: string | null) => (unit ? `${name}(${unit})` : name)
const buildTitle = (name: string, unit?: string | null) => (unit ? `${name}(${unit})` : name)
const getSharedUnit = (items: RawMetricItem[]) => {
const units = [...new Set(items.map(item => item.unit ?? null))]
return units.length === 1 ? units[0] : null
}
const getPrimaryTitle = (item: DisplayMetric) => buildTitle(item.name, item.unit)
const getScalarTitle = (item: DisplayMetric) => buildTitle(item.otherName || item.name, item.unit)
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
const isGroupedColumn = (item: MetricItem) => item.type === 'abc' || item.type === 't-multi'
const normalizeOldItem = (item: RawMetricItem): DisplayMetric => ({
otherName: item.otherName || buildOtherName(item.name || ''),
const extractSubTitle = (otherName: string) => {
for (const keyword of SECONDARY_KEYWORDS) {
if (otherName.includes(keyword)) return keyword
}
return otherName
}
const groupByName = (list: RawMetricItem[]): NameGroup[] => {
const order: string[] = []
const map = new Map<string, RawMetricItem[]>()
list.forEach(item => {
const name = item.name || ''
if (!map.has(name)) {
order.push(name)
map.set(name, [])
}
map.get(name)!.push(item)
})
return order.map(name => ({ name, items: map.get(name)! }))
}
const buildAbcMetric = (name: string, items: RawMetricItem[]): DisplayMetric => {
const phaseMap: Record<string, unknown> = {}
items.forEach(item => {
if (item.phase != null) phaseMap[item.phase] = item.data
})
for (const { keys, labels } of PHASE_GROUPS) {
if (keys.some(key => phaseMap[key] != null)) {
return {
name,
otherName: items[0].otherName || name,
unit: items[0].unit ?? null,
type: 'abc',
valueA: phaseMap[keys[0]],
valueB: phaseMap[keys[1]],
valueC: phaseMap[keys[2]],
phaseLabels: labels,
}
}
}
return {
name,
otherName: items[0].otherName || name,
unit: items[0].unit ?? null,
type: 'abc',
valueA: phaseMap.A ?? phaseMap.AB,
valueB: phaseMap.B ?? phaseMap.BC,
valueC: phaseMap.C ?? phaseMap.CA,
phaseLabels: phaseMap.AB != null ? ['AB相', 'BC相', 'CA相'] : ['A相', 'B相', 'C相'],
}
}
const buildTMetric = (name: string, items: RawMetricItem[]): DisplayMetric => {
const needSubHeader = items.some(item => (item.otherName || item.name || '') !== name)
if (!needSubHeader) {
return {
name,
otherName: items[0].otherName || name,
unit: items[0].unit ?? null,
type: 'scalar',
valueM: items[0].data,
}
}
const sharedUnit = getSharedUnit(items)
const unitsDiffer = sharedUnit == null && new Set(items.map(item => item.unit ?? null)).size > 1
return {
name,
otherName: name,
unit: sharedUnit,
type: 't-multi',
subItems: items.map(item => {
const keyword = extractSubTitle(item.otherName || item.name || '')
return {
subTitle: unitsDiffer ? buildTitle(keyword, item.unit) : keyword,
value: item.data,
}
}),
}
}
const groupToMetric = (group: NameGroup): DisplayMetric => {
const { name, items } = group
const phases = items.map(item => item.phase)
const allT = phases.every(phase => phase === 'T')
if (allT) return buildTMetric(name, items)
return buildAbcMetric(name, items)
}
const normalizeOldItem = (item: RawMetricItem): DisplayMetric => {
const name = item.name || ''
const otherName = item.otherName || name
const hasAbc = item.valueA != null || item.valueB != null || item.valueC != null
if (hasAbc) {
return {
name,
otherName,
unit: item.unit ?? null,
type: 'abc',
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 || '',
return {
name,
otherName,
unit: item.unit ?? null,
sort: item.sort ?? 0,
phases: {},
})
type: 'scalar',
valueM: item.valueM,
}
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 groupByName(data).map(groupToMetric)
}
return data.map(normalizeOldItem).sort((a, b) => a.sort - b.sort)
return data.map(normalizeOldItem)
}
const buildRow = (abcItems: DisplayMetric[], scalarItems: DisplayMetric[]) => {
const toMetricItem = (data: DisplayMetric): MetricItem => ({
type: data.type,
data,
})
const buildRow = (columns: MetricItem[]) => {
const row: Record<string, unknown> = {}
abcItems.forEach((item, index) => {
row[`v${index}A`] = item.valueA
row[`v${index}B`] = item.valueB
row[`v${index}C`] = item.valueC
columns.forEach((item, colIndex) => {
if (item.type === 'abc') {
row[`v${colIndex}A`] = item.data.valueA
row[`v${colIndex}B`] = item.data.valueB
row[`v${colIndex}C`] = item.data.valueC
} else if (item.type === 't-multi') {
item.data.subItems?.forEach((sub, subIndex) => {
row[`t${colIndex}_${subIndex}`] = sub.value
})
scalarItems.forEach((item, index) => {
row[`s${index}`] = item.valueM
} else {
row[`s${colIndex}`] = item.data.valueM
}
})
return row
}
const buildTableSections = (abcList: DisplayMetric[], scalarList: DisplayMetric[]) => {
const unified: MetricItem[] = [
...abcList.map(data => ({ type: 'abc' as const, data })),
...scalarList.map(data => ({ type: 'scalar' as const, data })),
]
const buildTableSections = (metrics: DisplayMetric[]) => {
const unified = metrics.map(toMetricItem)
const sections: TableSection[] = []
let index = 0
while (index < unified.length) {
const hasAbc = unified[index].type === 'abc'
const maxCount = hasAbc ? ROW_WITH_ABC : ROW_SCALAR_ONLY
const chunk = unified.slice(index, index + maxCount)
index += chunk.length
const remaining = unified.length - index
let slotsPerRow = isGroupedColumn(unified[index]) ? ROW_WITH_GROUP : ROW_SCALAR_ONLY
if (slotsPerRow === ROW_SCALAR_ONLY) {
const peekCount = Math.min(ROW_SCALAR_ONLY, remaining)
if (unified.slice(index, index + peekCount).some(isGroupedColumn)) {
slotsPerRow = ROW_WITH_GROUP
}
}
const columns = unified.slice(index, index + Math.min(slotsPerRow, remaining))
index += columns.length
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 slotsPerRow = abcItems.length > 0 ? ROW_WITH_ABC : ROW_SCALAR_ONLY
const metricCount = abcItems.length + scalarItems.length
sections.push({
abcItems,
scalarItems,
row: buildRow(abcItems, scalarItems),
columns,
row: buildRow(columns),
slotsPerRow,
emptySlotCount: slotsPerRow - metricCount,
emptySlotCount: Math.max(0, slotsPerRow - columns.length),
})
}
@@ -236,11 +322,7 @@ const buildTableSections = (abcList: DisplayMetric[], scalarList: DisplayMetric[
const setData = (data: RawMetricItem[], _targetType?: unknown) => {
const list = JSON.parse(JSON.stringify(data || [])) as RawMetricItem[]
const metrics = normalizeMetrics(list)
const abcList = metrics.filter(item => hasAbcValues(item))
const scalarList = metrics.filter(item => !hasAbcValues(item))
tableSections.value = buildTableSections(abcList, scalarList)
tableSections.value = buildTableSections(normalizeMetrics(list))
}
defineExpose({ setData })

View File

@@ -73,7 +73,7 @@ const tableStore: any = new TableStore({
sortable: true,
formatter: (row: any) => {
//row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/'
row.cellValue = row.cellValue != null ? Number(row.cellValue).toFixed(2) : '-'
row.cellValue = row.cellValue != null ? Number(row.cellValue).toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
row.cellValue = String(row.cellValue).split('.')[0]
}
@@ -88,7 +88,7 @@ const tableStore: any = new TableStore({
formatter: (row: any) => {
// console.log('🚀 ~ row.cellValue:', row.cellValue)
return row.cellValue ? (row.cellValue - 0).toFixed(2) : '-'
return row.cellValue ? (row.cellValue - 0).toFixed(2) : '/'
},
sortable: true
},
@@ -97,7 +97,7 @@ const tableStore: any = new TableStore({
title: '相别',
minWidth: 80,
formatter: (row: any) => {
return row.cellValue || '-'
return row.cellValue || '/'
}
},
{
@@ -151,12 +151,14 @@ const tableStore: any = new TableStore({
loading.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row
boxoList.value.systemType = 'YPT'
boxoList.value.engineeringName = tableParams.value.engineeringName
boxoList.value.featureAmplitude =
row.featureAmplitude != null ? Number(row.featureAmplitude / 100) : '-'
boxoList.value.persistTime = row.persistTime ? row.persistTime.toFixed(2) : '-'
boxoList.value = {
...row,
systemType: 'YPT',
engineeringName: tableParams.value.engineeringName,
featureAmplitude:
row.featureAmplitude != null ? Number(row.featureAmplitude / 100) : '-',
persistTime: row.persistTime ? row.persistTime.toFixed(2) : '-',
}
wp.value = res.data
view.value = false
view2.value = true

View File

@@ -127,6 +127,8 @@ const countOptions: any = ref([])
// Harmonic_Type
// portable-harmonic
const legendDictList: any = ref([])
queryByCode(
props?.TrendList?.lineType == 0
? 'apf-harmonic'

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div v-show="!isWaveCharts">
<TableHeader showExport ref="headerRef" @onResetForm="onResetForm">
<TableHeader showExport ref="headerRef" :showQuery="false" @onResetForm="onResetForm">
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Operation" @click="openFilterDialog">事件筛选</el-button>
</template>
@@ -57,7 +57,8 @@ const multiConditionRef = ref()
const tableStore = new TableStore({
url: '/cs-harmonic-boot/data/getEventByItem',
method: 'POST',
paramsPOST: true,
// paramsPOST: true,
exportName: '暂态事件',
showPage: false,
publicHeight: 355,
column: [
@@ -151,10 +152,12 @@ const tableStore = new TableStore({
isWaveCharts.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row
boxoList.value.featureAmplitude =
row.featureAmplitude != '-' ? (row.featureAmplitude - 0) / 100 : null
boxoList.value.systemType = 'YPT'
boxoList.value = {
...row,
featureAmplitude:
row.featureAmplitude != '-' ? (row.featureAmplitude - 0) / 100 : null,
systemType: 'YPT',
}
wp.value = res.data

View File

@@ -78,17 +78,19 @@ export default {
this.leftData = val
const config = useConfig()
this.leftData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.icon = 'el-icon-Platform'
// item.color = config.getColorVal('elementUiPrimary')
item.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.icon = 'local-监测点'
// item2.color = config.getColorVal('elementUiPrimary')
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.icon = 'el-icon-List'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
// if (item3.runStatus === 1) {
// item3.color = '#e26257 !important'
// }
})
})
})
@@ -100,17 +102,19 @@ export default {
const config = useConfig()
this.$emit('getData', this.rightData)
this.rightData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.icon = 'el-icon-Platform'
// item.color = config.getColorVal('elementUiPrimary')
item.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.icon = 'local-监测点'
// item2.color = config.getColorVal('elementUiPrimary')
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.icon = 'el-icon-List'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
// if (item3.runStatus === 1) {
// item3.color = '#e26257 !important'
// }
})
})
})

View File

@@ -0,0 +1,93 @@
<template>
<div>
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="设备名称">
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入设备名称"
/>
</el-form-item>
</template>
</TableHeader>
<Table ref="tableRef" :isGroup="true" />
</div>
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { ref, onMounted, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
defineOptions({
name: 'govern/log/debug'
})
const tableStore = new TableStore({
url: '/cs-device-boot/process/queryPage',
method: 'POST',
publicHeight: 65,
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '设备名称', field: 'devName', align: 'center' },
{ title: '操作用户', field: 'operatorName', align: 'center' },
{
title: '操作内容',
field: 'process',
align: 'center',
formatter: (row: any) => {
return row.cellValue == 1
? '设备登记'
: row.cellValue == 2
? '功能调试'
: row.cellValue == 3
? '出厂调试'
: row.cellValue == 4
? '设备投运'
: ''
}
},
{ title: '开始时间', field: 'startTime', align: 'center', sortable: true },
{
title: '结束时间',
field: 'endTime',
align: 'center',
sortable: true,
formatter: (row: any) => {
return row.cellValue || '/'
}
}
],
loadCallback: () => {
tableStore.table.data.forEach((item: any) => {
item.result = item.result === 1 ? '成功' : '失败'
// for (let key in item) {
// if (typeof item[key] !== 'number') {
// item[key] = item[key] || '/'
// }
// }
})
}
})
provide('tableStore', tableStore)
tableStore.table.params.searchState = 0
tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = ''
tableStore.table.params.searchValue = ''
onMounted(() => {
tableStore.index()
})
const addMenu = () => {}
</script>

View File

@@ -1,92 +1,29 @@
<template>
<div class="default-main">
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="设备名称">
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入设备名称"
/>
</el-form-item>
</template>
</TableHeader>
<Table ref="tableRef" :isGroup="true" />
<div class="default-main apf">
<el-tabs type="border-card" v-model="activeName">
<el-tab-pane label="治理交互日志" name="1">
<governance v-if="activeName == '1'" />
</el-tab-pane>
<el-tab-pane label="前置交互日志" name="2">
<front v-if="activeName == '2'" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { ref, onMounted, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
<script setup lang="ts">
import { ref, reactive } from 'vue'
import front from './components/front.vue'
import governance from './components/governance.vue'
defineOptions({
name: 'govern/log/debug'
})
const tableStore = new TableStore({
url: '/cs-device-boot/process/queryPage',
method: 'POST',
column: [
{
field: 'index',
title: '序号',
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '设备名称', field: 'devName', align: 'center' },
{ title: '操作用户', field: 'operatorName', align: 'center' },
{
title: '操作内容',
field: 'process',
align: 'center',
formatter: (row: any) => {
return row.cellValue == 1
? '设备登记'
: row.cellValue == 2
? '功能调试'
: row.cellValue == 3
? '出厂调试'
: row.cellValue == 4
? '设备投运'
: ''
}
},
{ title: '开始时间', field: 'startTime', align: 'center', sortable: true },
{
title: '结束时间',
field: 'endTime',
align: 'center',
sortable: true,
formatter: (row: any) => {
return row.cellValue || '/'
}
}
],
const activeName = ref('1')
loadCallback: () => {
tableStore.table.data.forEach((item: any) => {
item.result = item.result === 1 ? '成功' : '失败'
// for (let key in item) {
// if (typeof item[key] !== 'number') {
// item[key] = item[key] || '/'
// }
// }
})
}
})
provide('tableStore', tableStore)
tableStore.table.params.searchState = 0
tableStore.table.params.sortBy = ''
tableStore.table.params.orderBy = ''
tableStore.table.params.searchValue = ''
onMounted(() => {
tableStore.index()
})
const addMenu = () => {}
</script>
<style lang="scss" scoped>
:deep(.cn-tree) {
padding: 0 10px 0 0 !important;
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<el-dialog :title="dialogTitle" v-model="visible" :close-on-click-modal="false" :before-close="handleBeforeClose"
draggable width="800px">
<el-form :model="form" label-width="auto" :rules="rules" ref="ruleFormRef" class="form-two">
<el-form-item label="设备名称" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" autocomplete="off" clearable
placeholder="请输入(项目名称+设备名称)"></el-input>
</el-form-item>
<el-form-item label="网络设备ID" prop="ndid" class="top">
<el-input maxlength="32" show-word-limit v-model.trim="form.ndid" autocomplete="off"
placeholder="请输入网络设备ID"></el-input>
</el-form-item>
<el-form-item label="设备类型" prop="devType" class="top">
<el-select v-model.trim="form.devType" placeholder="请选择设备类型" @change="formDevTypeChange" clearable>
<el-option v-for="item in formDevTypeOptions" :key="item.value" :label="item.label || item.name"
:value="item.value || item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备型号" prop="devModel" class="top">
<el-select v-model.trim="form.devModel" filterable placeholder="请选择设备型号" clearable>
<el-option v-for="item in formDevModelOptionsFilter" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="治理类型" class="top">
<el-select v-model.trim="form.governType" filterable placeholder="请选择治理类型" clearable>
<el-option label="暂态" value="event"></el-option>
<el-option label="稳态" value="harmonic"></el-option>
</el-select>
</el-form-item>
<el-form-item label="敏感用户"class="top">
<div style="display: flex;">
<el-select v-model.trim="form.monitorUser" style="width: 230px;" filterable placeholder="请选择敏感用户"
clearable>
<el-option v-for="option in userList" :key="option.id" :label="option.name"
:value="option.id"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-Plus" class="ml10" @click="addMonitorUser" />
</div>
</el-form-item>
<el-form-item label="治理方法" class="top">
<el-input maxlength="32" show-word-limit v-model="form.governMethod" autocomplete="off" clearable
placeholder="例: 250A APF 或 100kVar SVG"></el-input>
</el-form-item>
<el-form-item label="通讯协议" prop="devAccessMethod" class="top">
<el-select v-model.trim="form.devAccessMethod" placeholder="请选择通讯协议" clearable disabled>
<el-option label="MQTT" value="MQTT"></el-option>
<el-option label="CLD" value="CLD"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关联项目" class="top">
<el-cascader v-model.trim="form.association" filterable :options="engineeringList"
:props="cascaderProps" clearable placeholder="请选择关联项目" />
</el-form-item>
<el-form-item label="排序" class="top" prop="sort">
<el-input-number v-model.trim="form.sort" style="width: 100%;" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="onSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, nextTick, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
addEquipmentDelivery,
editEquipmentDelivery,
portableDeviceRegister,
} from '@/api/cs-system-boot/device'
import { useRouter } from 'vue-router'
const router = useRouter() // 路由对象
const props = defineProps<{
userList: any[]
engineeringList: any[]
devTypeOptions: any[]
devTypeOptions2: any
devModelOptions: any[]
devModelOptions2: any[]
}>()
const emit = defineEmits<{
success: []
}>()
const visible = defineModel<boolean>('visible', { default: false })
const dialogTitle = ref('新增设备')
const ruleFormRef = ref()
const form: any = reactive(getDefaultForm())
const cascaderProps = {
label: 'projectName',
value: 'projectId',
children: 'projectInfoList'
}
const rules = reactive({
name: [{ required: true, message: '请输入设备名', trigger: 'blur' }],
ndid: [{ required: true, message: '请输入网络设备id', trigger: 'blur' }],
devType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
devModel: [{ required: true, message: '请选择设备型号', trigger: 'change' }],
devAccessMethod: [{ required: true, message: '请输入接入方式', trigger: 'blur' }],
cntractNo: [{ required: true, message: '请输入合同号', trigger: 'blur' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
})
function getDefaultForm() {
return {
cntractNo: '',
devAccessMethod: 'MQTT',
devModel: '',
devType: '',
name: '',
ndid: '',
associatedEngineering: '',
associatedProject: '',
association: [] as any[],
sort: 0,
monitorUser: '',
governType: '',
governMethod: '',
id: undefined as string | number | undefined,
}
}
const normalizeFormValue = (value: unknown, fallback = '') =>
value == null ? fallback : value
const buildSubmitPayload = () => {
const association = Array.isArray(form.association) ? form.association : []
return {
id: form.id,
cntractNo: normalizeFormValue(form.cntractNo),
devAccessMethod: normalizeFormValue(form.devAccessMethod, 'MQTT'),
devModel: normalizeFormValue(form.devModel),
devType: normalizeFormValue(form.devType),
name: normalizeFormValue(form.name),
ndid: normalizeFormValue(form.ndid),
associatedEngineering: normalizeFormValue(association[0]),
associatedProject: normalizeFormValue(association[1]),
sort: form.sort ?? 0,
monitorUser: normalizeFormValue(form.monitorUser),
governType: normalizeFormValue(form.governType),
governMethod: normalizeFormValue(form.governMethod),
}
}
const formDevModelOptionsFilter = computed(() => {
if (form.devAccessMethod === 'CLD') {
return props.devModelOptions2.filter((item: any) => {
return form.devType ? item.pid == form.devType : true
})
}
return props.devModelOptions.filter((item: any) => {
return form.devType ? item.pid == form.devType : true
})
})
const formDevTypeOptions = computed(() => {
if (form.devAccessMethod === 'CLD') {
return [props.devTypeOptions2]
}
return props.devTypeOptions
})
const formDevTypeChange = (e: any) => {
if (!e) return
form.devModel = ''
}
const openAdd = () => {
dialogTitle.value = '新增设备'
Object.assign(form, getDefaultForm())
visible.value = true
}
const openEdit = (row: any) => {
dialogTitle.value = '编辑设备'
Object.assign(form, getDefaultForm())
for (const key in form) {
if (key in row) {
form[key] = row[key]
}
}
form.association = row.associatedProject ? [row.associatedEngineering, row.associatedProject] : []
form.id = row.id
visible.value = true
}
const clearForm = () => {
ruleFormRef.value?.resetFields()
nextTick(() => {
Object.assign(form, getDefaultForm())
})
}
const handleCancel = () => {
clearForm()
visible.value = false
}
const handleBeforeClose = (done: () => void) => {
clearForm()
done()
}
const resetForm = () => {
clearForm()
visible.value = false
}
const addMonitorUser = () => {
sessionStorage.setItem('factoryNeedRefreshUserList', '1')
router.push({
name: 'govern/sensitiveLoadMange/index',
})
}
const onSubmit = () => {
ruleFormRef.value.validate((valid: boolean) => {
if (!valid) return
const payload = buildSubmitPayload()
if (dialogTitle.value === '新增设备') {
addEquipmentDelivery(payload).then(res => {
ElMessage.success('新增成功')
const devType = props.devTypeOptions.find((item: any) => item.value == payload.devType)
if (devType?.code === 'Portable') {
setTimeout(() => {
ElMessage.warning('设备正在接入中,请稍等。')
}, 3000)
portableDeviceRegister({ nDid: res.data.ndid })
.then(pres => {
ElMessage.success(pres.message)
})
.catch(() => { })
}
resetForm()
emit('success')
})
} else {
editEquipmentDelivery(payload).then(() => {
ElMessage.success('修改成功')
resetForm()
emit('success')
})
}
})
}
defineExpose({ openAdd, openEdit })
</script>

View File

@@ -3,12 +3,8 @@
<TableHeader>
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
style="width: 200px"
clearable
placeholder="请输入设备名称/网络设备ID"
/>
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
style="width: 200px" clearable placeholder="请输入设备名称/网络设备ID" />
</el-form-item>
<!-- <el-form-item label="流程阶段">
<el-select v-model.trim="tableStore.table.params.process" clearable placeholder="请选择状态">
@@ -18,11 +14,7 @@
</el-select>
</el-form-item> -->
<el-form-item label="物联状态">
<el-select
v-model.trim="tableStore.table.params.connectStatus"
clearable
placeholder="请选择物联状态"
>
<el-select v-model.trim="tableStore.table.params.connectStatus" clearable placeholder="请选择物联状态">
<el-option label="已连接" :value="1"></el-option>
<el-option label="未连接" :value="0"></el-option>
</el-select>
@@ -85,107 +77,22 @@
<el-button type="primary" @click="downLoadFile1" class="ml10" icon="el-icon-Download">
模版下载
</el-button>
<el-upload
style="display: inline-block"
action=""
accept=".xlsx"
class="upload-demo"
:show-file-list="false"
:auto-upload="false"
:on-change="bulkImport"
>
<el-upload style="display: inline-block" action="" accept=".xlsx" class="upload-demo"
:show-file-list="false" :auto-upload="false" :on-change="bulkImport">
<el-button type="primary" class="ml10" icon="el-icon-Tickets">批量导入</el-button>
</el-upload>
<el-button type="primary" class="ml10" @click="add" icon="el-icon-Plus">新增设备</el-button>
<el-button
type="primary"
class="ml10"
icon="el-icon-Download"
@click="downLoadQrCode"
:disabled="!showQrCode"
>
<el-button type="primary" class="ml10" icon="el-icon-Download" @click="downLoadQrCode"
:disabled="!showQrCode">
批量下载二维码
</el-button>
</template>
</TableHeader>
<Table ref="tableRef" :checkbox-config="checkboxConfig" :key="tableKey" @sort-change="handleSortChange"></Table>
<el-dialog
:title="dialogTitle"
v-model.trim="dialogFormVisible"
:close-on-click-modal="false"
:before-close="resetForm"
draggable
width="800px"
>
<el-form :model="form" label-width="auto" :rules="rules" ref="ruleFormRef" class="form-two">
<el-form-item label="设备名称:" prop="name">
<el-input maxlength="32" show-word-limit
v-model.trim="form.name"
autocomplete="off"
clearable
placeholder="请输入(项目名称+设备名称)"
></el-input>
</el-form-item>
<el-form-item label="网络设备ID:" prop="ndid" class="top">
<el-input maxlength="32" show-word-limit
v-model.trim="form.ndid"
autocomplete="off"
placeholder="请输入"
></el-input>
</el-form-item>
<el-form-item label="设备类型:" prop="devType" class="top">
<el-select v-model.trim="form.devType" placeholder="请选择" @change="formDevTypeChange" clearable>
<el-option
v-for="item in formDevTypeOptions"
:key="item.value"
:label="item.label || item.name"
:value="item.value || item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备型号:" prop="devModel" class="top">
<el-select v-model.trim="form.devModel" filterable placeholder="请选择" clearable>
<el-option
v-for="item in formDevModelOptionsFilter"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="通讯协议:" prop="devAccessMethod" class="top">
<el-select v-model.trim="form.devAccessMethod" placeholder="请选择" clearable disabled>
<el-option label="MQTT" value="MQTT"></el-option>
<el-option label="CLD" value="CLD"></el-option>
</el-select>
</el-form-item>
<el-form-item label="关联项目:" class="top">
<el-cascader
v-model.trim="form.association"
filterable
:options="engineeringList"
:props="props"
clearable
/>
</el-form-item>
<el-form-item label="排序:" class="top" prop="sort">
<el-input-number v-model.trim="form.sort" :min="0" />
</el-form-item>
<!-- <el-form-item label="合同号:" prop="cntractNo" class="top">
<el-input maxlength="32" show-word-limit v-model.trim="form.cntractNo" autocomplete="off" placeholder="请输入"></el-input>
</el-form-item> -->
</el-form>
<template #footer>
<el-button @click="resetForm"> </el-button>
<el-button type="primary" @click="onSubmit"> </el-button>
</template>
</el-dialog>
<FactoryForm ref="factoryFormRef" :user-list="userList" :engineering-list="engineeringList"
:dev-type-options="devTypeOptions" :dev-type-options2="devTypeOptions2" :dev-model-options="devModelOptions"
:dev-model-options2="devModelOptions2" @success="onFormSuccess" />
<div class="qrcode-label">
<div class="qrcode-label-title">{{ deivce.mac }}</div>
@@ -200,35 +107,36 @@
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, computed, reactive, nextTick } from 'vue'
import { ref, onMounted, onActivated, provide, computed, reactive } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import FactoryForm from './components/factoryForm.vue'
import { queryByCode, queryByid, queryCsDictTree } from '@/api/system-boot/dictTree'
import { ElMessage, ElMessageBox } from 'element-plus'
import { activateUser, deluser, passwordConfirm } from '@/api/user-boot/user'
import { downLoadFile } from '@/api/cs-system-boot/manage'
import {
addEquipmentDelivery,
deleteEquipmentDelivery,
editEquipmentDelivery,
batchImportDevice,
resetEquipmentDelivery,
governDeviceRegister,
portableDeviceRegister,
portableDeviceAccess,
getExcelTemplate,
engineeringProject,
onlineRegister,
resetFactory
resetFactory,
portableDeviceRegister,
portableDeviceAccess,
} from '@/api/cs-system-boot/device'
import html2canvas from 'html2canvas'
import { fullUrl } from '@/utils/common'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'
import { getList } from '@/api/cs-harmonic-boot/recruitment'
defineOptions({
name: 'govern/manage/factory'
})
const userList: any = ref([])
const showQrCode = ref(false)
const devTypeOptions: any = ref([])
const devTypeOptions2: any = ref([])
@@ -236,37 +144,9 @@ const devModelOptions2: any = ref([])
const engineeringList: any = ref([])
const deivce: any = ref({})
const ruleFormRef = ref()
const form: any = reactive({
cntractNo: '',
devAccessMethod: 'MQTT',
devModel: '',
devType: '',
name: '',
ndid: '',
associatedEngineering: '',
associatedProject: '',
association: [],
sort: 0
})
const props = {
label: 'projectName',
value: 'projectId',
children: 'projectInfoList'
}
const rules = reactive({
name: [{ required: true, message: '请输入设备名', trigger: 'blur' }],
ndid: [{ required: true, message: '请输入网络设备id', trigger: 'blur' }],
devType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
devModel: [{ required: true, message: '请选择设备型号', trigger: 'change' }],
devAccessMethod: [{ required: true, message: '请输入接入方式', trigger: 'blur' }],
cntractNo: [{ required: true, message: '请输入合同号', trigger: 'blur' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
})
const factoryFormRef = ref<InstanceType<typeof FactoryForm>>()
const qrcodeFlag = ref(false)
const tableKey = ref(0)
const dialogFormVisible = ref(false)
const dialogTitle = ref('新增设备')
const loading = ref<boolean>(false)
const devModelOptions: any = ref([])
const queryTheDictionary = () => {
@@ -330,37 +210,6 @@ const devModelOptionsFilter = computed(() => {
})
})
const formDevModelOptionsFilter = computed(() => {
if (form.devAccessMethod === 'CLD') {
return devModelOptions2.value.filter((item: any) => {
if (form.devType) {
return item.pid == form.devType
} else {
return true
}
})
} else {
return devModelOptions.value.filter((item: any) => {
if (form.devType) {
return item.pid == form.devType
} else {
return true
}
})
}
})
const formDevTypeOptions = computed(() => {
// 如果协议是CLD使用devTypeOptions2否则使用devTypeOptions
if (form.devAccessMethod === 'CLD') {
// 对于devTypeOptions2使用id和name字段
return [devTypeOptions2.value]
} else {
// 对于devTypeOptions使用value和label字段
return devTypeOptions.value
}
})
const tableStore = new TableStore({
url: '/cs-device-boot/EquipmentDelivery/list',
method: 'POST',
@@ -377,8 +226,8 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '设备名称', field: 'name', minWidth: 120 },
{ title: '网络设备ID', field: 'ndid', minWidth: 120 },
{ title: '设备名称', field: 'name', minWidth: 160 },
{ title: '网络设备ID', field: 'ndid', minWidth: 130 },
{
title: '设备类型',
field: 'devType',
@@ -393,7 +242,7 @@ const tableStore = new TableStore({
return item ? item.label : '/'
}
},
minWidth: 120
minWidth: 110
},
{
title: '设备型号',
@@ -405,13 +254,37 @@ const tableStore = new TableStore({
},
minWidth: 120
},
{
title: '治理类型',
field: 'governType',
minWidth: 100,
formatter: row => {
return row.cellValue === 'event' ? '暂态' : row.cellValue === 'harmonic' ? '稳态' : (row.cellValue || '/')
}
},
{
title: '治理方法',
field: 'governMethod',
minWidth: 100,
formatter: row => {
return row.cellValue || '/'
}
},
{
title: '敏感用户',
field: 'monitorUser',
minWidth: 120,
formatter: row => {
return userList.value.find((item: any) => item.id == row.cellValue)?.name || '/'
}
},
{
title: '通讯协议',
field: 'devAccessMethod',
formatter: row => {
return row.cellValue === 'MQTT' ? 'MQTT' : row.cellValue === 'CLD' ? 'CLD' : row.cellValue
},
minWidth: 120
minWidth: 100
},
{ title: '录入时间', field: 'createTime', sortable: true, minWidth: 150 },
@@ -425,7 +298,7 @@ const tableStore = new TableStore({
inactiveValue: '0',
activeValue: '1',
onChangeField: (row: any, value: any) => {
// console.log("🚀 ~ row:", row)
// console.log("🚀 ~ row", row)
ElMessageBox.prompt('二次校验密码确认', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
@@ -492,28 +365,28 @@ const tableStore = new TableStore({
},
minWidth: 80
},
{
title: '流程阶段',
field: 'process',
width: 100,
render: 'tag',
custom: {
2: 'warning',
3: 'warning',
4: 'success'
},
replaceValue: {
2: '功能调试',
3: '出厂调试',
4: '正式投运'
},
minWidth: 80
},
// {
// title: '流程阶段',
// field: 'process',
// width: 100,
// render: 'tag',
// custom: {
// 2: 'warning',
// 3: 'warning',
// 4: 'success'
// },
// replaceValue: {
// 2: '功能调试',
// 3: '出厂调试',
// 4: '正式投运'
// },
// minWidth: 80
// },
{
title: '操作',
fixed: 'right',
align: 'center',
width: 220,
width: 180,
render: 'buttons',
buttons: [
//直连设备注册
@@ -758,16 +631,7 @@ const tableStore = new TableStore({
render: 'basicButton',
click: async row => {
dialogFormVisible.value = true
dialogTitle.value = '编辑设备'
for (let key in form) {
form[key] = row[key]
}
form.association = row.associatedProject
? [row.associatedEngineering, row.associatedProject]
: []
form.id = row.id
factoryFormRef.value?.openEdit(row)
}
},
{
@@ -810,7 +674,7 @@ const tableStore = new TableStore({
tableStore.onTableAction('search', {})
})
})
.catch(e => {})
.catch(e => { })
}
}
]
@@ -956,81 +820,35 @@ const handleSortChange = ({ column, order }: { column: TableColumn; order: 'asc'
// 新增
const add = () => {
dialogFormVisible.value = true
dialogTitle.value = '新增设备'
factoryFormRef.value?.openAdd()
}
// 确认
const onSubmit = () => {
ruleFormRef.value.validate((valid: any) => {
if (valid) {
form.associatedEngineering = form.association ? form.association[0] : ''
form.associatedProject = form.association ? form.association[1] : ''
if (dialogTitle.value == '新增设备') {
addEquipmentDelivery(form).then(res => {
ElMessage.success('新增成功')
if (devTypeOptions.value.filter((item: any) => item.value == form.devType)[0].code == 'Portable') {
setTimeout(() => {
ElMessage.warning('设备正在接入中,请稍等。')
}, 3000)
portableDeviceRegister({
nDid: res.data.ndid
})
.then(pres => {
ElMessage.success(pres.message)
setTimeout(() => {
tableStore.index()
}, 1000)
})
.catch(err => {
// clearTimeout (time)
})
}
resetForm()
const onFormSuccess = () => {
tableStore.onTableAction('search', {})
})
} else {
editEquipmentDelivery(form).then(res => {
ElMessage.success('修改成功')
resetForm()
tableStore.onTableAction('search', {})
})
}
}
})
}
// 清空表格
const resetForm = () => {
ruleFormRef.value.resetFields()
dialogFormVisible.value = false
nextTick(() => {
// 模拟待编辑数据
let user = {
cntractNo: '',
devAccessMethod: 'MQTT',
devModel: '',
devType: '',
name: '',
associatedEngineering: '',
associatedProject: '',
association: [],
ndid: ''
}
Object.assign(form, user)
const getUserList = () => {
return getList({
pageNum: 1,
pageSize: 2000
}).then(res => {
userList.value = res.data.records
})
}
const formDevTypeChange = (e: any) => {
if (!e) {
return
// 页面被 keep-alive 缓存后,从敏感用户页返回时刷新下拉列表
onActivated(() => {
if (sessionStorage.getItem('factoryNeedRefreshUserList')) {
sessionStorage.removeItem('factoryNeedRefreshUserList')
getUserList()
}
form.devModel = ''
}
})
provide('tableStore', tableStore)
onMounted(() => {
getUserList()
queryTheDictionary()
engineeringProject().then(res => {
engineeringList.value = res.data.filter(item => {
@@ -1041,10 +859,10 @@ onMounted(() => {
})
})
setTimeout(() => {}, 100)
setTimeout(() => { }, 100)
})
const addMenu = () => {}
const addMenu = () => { }
</script>
<style lang="scss" scoped>
.qrcode-label {

View File

@@ -89,24 +89,24 @@ const tableStore = new TableStore({
}
},
{ title: 'Mac地址', field: 'mac', width: 140 },
{
title: '流程阶段',
field: 'process',
width: 100,
fixed: 'right',
render: 'tag',
custom: {
2: 'warning',
3: 'warning',
4: 'success'
},
replaceValue: {
2: '功能调试',
3: '出厂调试',
4: '正式投运'
},
minWidth: 80
},
// {
// title: '流程阶段',
// field: 'process',
// width: 100,
// fixed: 'right',
// render: 'tag',
// custom: {
// 2: 'warning',
// 3: 'warning',
// 4: 'success'
// },
// replaceValue: {
// 2: '功能调试',
// 3: '出厂调试',
// 4: '正式投运'
// },
// minWidth: 80
// },
{
title: '运行状态',
field: 'operationalStatus',

View File

@@ -1,144 +1,126 @@
<template>
<div class="default-main report-zl-page" :style="height">
<div class="report-zl-sidebar">
<!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil">
</pointTreeWx> -->
<CloudDeviceEntryTree ref="TerminalRef" template @Policy="stencil" @node-click="handleNodeClick"
@init="handleNodeClick"></CloudDeviceEntryTree>
</div>
<div class="report-zl-main">
<div class="statistics-wx-zl default-main" :style="height">
<aside>
<!-- <CloudDeviceEntryTree
ref="TerminalRef"
template
@Policy="stencil"
@init="handleNodeClick"
/> -->
<APFTree @node-click="handleNodeClick" template @init="handleNodeClick" @Policy="stencil"></APFTree>
</aside>
<main class="statistics-wx-zl__main ">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<!-- <el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> -->
<template #select>
<el-form-item label="模板策略">
<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"
:value="item"></el-option>
<el-select
v-model="Template"
@change="changetype"
placeholder="请选择模版"
value-key="id"
>
<el-option
v-for="item in templatePolicy"
:key="item.id"
:label="item.excelName"
:value="item"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="监测对象">
<el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item> -->
</template>
<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>
</TableHeader>
<div class="box report-zl-box">
<div class="report-zl-sheet-wrap">
<div class="statistics-wx-zl__sheet-box pl0" v-loading="tableStore.table.loading">
<div
v-if="tableStore.table.data.length > 0"
id="luckysheet"
class="report-zl-sheet"
v-loading="tableStore.table.loading"
element-loading-background="rgba(255, 255, 255, 0.85)"
></div>
<el-empty
v-show="!tableStore.table.loading && tableStore.table.data.length === 0"
class="report-zl-empty"
description="暂无数据"
class="statistics-wx-zl__sheet"
/>
<el-empty v-else class="statistics-wx-zl__empty" description="暂无数据" />
</div>
</div>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, provide } from 'vue'
import { onMounted, ref, provide, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import APFTree from '@/components/tree/govern/APFTree.vue'
import { mainHeight } from '@/utils/layout'
import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { isLineTreeLeaf } from '@/components/tree/govern/lineTreeUtils'
import DatePicker from '@/components/form/datePicker/time.vue'
import { exportExcel } from '@/views/system/reportForms/export.js'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { ElMessage } from 'element-plus'
// import data from './123.json'
defineOptions({
name: 'govern/reportCore/statisticsWx/indexZL'
})
const height = mainHeight(20)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
const Template: any = ref({})
const reportForm: any = ref('')
const datePickerRef = ref()
const templatePolicy: any = ref([])
const dotList = ref<any>({})
const Template = ref<any>({})
const reportForm = ref('')
const templatePolicy = ref<any[]>([])
const name = ref('')
const idList = ref<any[]>([])
const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getSensitiveUserReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
tableStore.table.params.sensitiveUserId = dotList.value.id
// ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]),
// (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]),
if (!tableStore.table.params.tempId) {
ElMessage.warning('请选择模板')
return false
return ElMessage.warning('请选择模板')
}
if (!dotList.value?.id) {
ElMessage.warning('请选择监测点')
return false
}
destroyLuckysheet()
delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag
},
loadCallback: () => {
name.value = dotList.value.name
if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
} else {
destroyLuckysheet()
}
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '',
lang: 'zh',
showtoolbar: false,
showinfobar: false,
showsheetbar: true,
allowEdit: false,
data: tableStore.table.data
})
}, 10)
}
})
provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = 1
const flag = ref(true)
onMounted(() => {
initListByIds()
})
onUnmounted(() => {
destroyLuckysheet()
})
const idList = ref([])
// 监测对象
const initListByIds = () => {
function initListByIds() {
getListByIds({}).then((res: any) => {
if (res.data.length > 0) {
if (res.data?.length > 0) {
idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
if (!tableStore.table.params.sensitiveUserId) {
tableStore.table.params.sensitiveUserId = idList.value[0].id
}
// templateListData()
}
})
}
const stencil = (val: any) => {
templatePolicy.value = val.filter((item: any) => item.excelType == '4')
const stencil = (val: any[]) => {
templatePolicy.value = val.filter(item => item.excelType == '4')
Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType
}
@@ -147,12 +129,10 @@ const changetype = (val: any) => {
reportForm.value = val.excelType
}
const handleNodeClick = (data: any, node: any) => {
if (isLineTreeLeaf(data) || data?.level == 3) {
const handleNodeClick = (data: any) => {
if (data?.level == 3||data?.level == 2) {
dotList.value = data
setTimeout(() => {
tableStore.index()
}, 500)
nextTick(() => tableStore.index())
} else {
tableStore.table.loading = false
}
@@ -160,64 +140,76 @@ const handleNodeClick = (data: any, node: any) => {
const exportEvent = () => {
const now = new Date()
const year = now.getFullYear() // 4位年份
const month = now.getMonth() + 1 // 月份0-11需+1
const day = now.getDate() // 日期1-31
// 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
const formattedDate = [
now.getFullYear(),
String(now.getMonth() + 1).padStart(2, '0'),
String(now.getDate()).padStart(2, '0')
].join('')
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate)
}
</script>
<style lang="scss">
.report-zl-page {
<style scoped lang="scss">
.statistics-wx-zl {
display: flex;
overflow: hidden;
}
gap: 0;
.report-zl-sidebar {
width: 280px;
&__sidebar {
flex-shrink: 0;
min-height: 0;
width: 280px;
height: 100%;
overflow: hidden;
}
background: var(--el-bg-color);
border-right: 1px solid var(--el-border-color-lighter);
box-sizing: border-box;
padding: 10px 8px 10px 10px;
.report-zl-main {
:deep(.cn-tree) {
height: 100%;
padding: 0;
}
:deep(.el-tree) {
height: calc(100% - 42px) !important;
}
}
&__main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
background: var(--el-bg-color);
}
.box {
&__template-select {
min-width: 220px;
}
&__sheet-box {
flex: 1;
min-height: 0;
padding: 10px 0;
padding: 0 12px 12px;
box-sizing: border-box;
}
&__sheet,
&__empty {
height: 100%;
min-height: 240px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 4px;
overflow: hidden;
}
background: var(--el-fill-color-blank);
}
.report-zl-box {
&__empty {
display: flex;
flex-direction: column;
}
.report-zl-sheet-wrap {
flex: 1;
min-height: 0;
position: relative;
}
.report-zl-sheet {
height: 100%;
}
.report-zl-empty {
position: absolute;
inset: 0;
height: 100%;
background: #fff;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -9,7 +9,7 @@
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
placeholder="请输入敏感负荷名称"
placeholder="请输入敏感用户名称"
/>
</el-form-item>
</template>
@@ -51,16 +51,17 @@ const tableStore: any = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '所属厂站名称', field: 'substationName', minWidth: 180 },
{ title: '敏感用户名称', field: 'name', minWidth: 180 },
{
title: '敏感负荷类型',
title: '敏感用户类型',
field: 'loadType',
minWidth: 120,
formatter: row => {
return interferenceType.filter(item => item.id == row.cellValue)[0]?.name
}
},
{ title: '所属厂站名称', field: 'substationName', minWidth: 180 },
{ title: '用户协议容量(MVA)', field: 'userAgreementCapacity', minWidth: 100 },
{ title: '装机容量(MW)', field: 'installedCapacity', minWidth: 100 },
{

View File

@@ -21,7 +21,7 @@
<Icon class="ba-icon-dark" :name="row.icon || ''" />
</template>
</vxe-column>
<vxe-column field="startTime" title="是否激活">
<vxe-column field="startTime" title="是否激活" width="90">
<template #default="{ row }">
<el-switch
v-model="row.state"
@@ -35,7 +35,7 @@
/>
</template>
</vxe-column>
<vxe-column field="startTime" title="是否全局" v-if="hasAdmin">
<vxe-column field="startTime" title="是否全局" v-if="hasAdmin" width="90">
<template #default="{ row }">
<el-switch
v-model="row.scope"
@@ -49,18 +49,21 @@
/>
</template>
</vxe-column>
<vxe-column field="sort" title="排序">
<vxe-column field="sort" width="80" title="排序">
</vxe-column>
<vxe-column field="startTime" title="操作">
<vxe-column field="startTime" title="操作" width="150">
<template #default="{ row }">
<el-button type="primary" link @click="edit(row)" v-if="!(!hasAdmin && row.scope)">
<el-button type="primary" link size="small" @click="edit(row)" v-if="!(!hasAdmin && row.scope)">
编辑
</el-button>
<el-button type="danger" link @click="deletes(row)" v-if="!(!hasAdmin && row.scope)">
<el-button type="danger" link size="small" @click="deletes(row)" v-if="!(!hasAdmin && row.scope)">
删除
</el-button>
<el-button type="info" link size="small" v-if="(!hasAdmin && row.scope)">
管理员分配页面
</el-button>
</template>
</vxe-column>
</vxe-table>

View File

@@ -30,8 +30,8 @@
<div class="tableBox" v-if="show">
<vxe-table border auto-resize height="230px" :data="tableData" ref="tableRef">
<vxe-column type="seq" title="序号" align="center" width="80px"></vxe-column>
<vxe-column field="date" align="center" sortable title="时间" width="200px"></vxe-column>
<vxe-column field="name" align="center" title="监测点名" width="200px"></vxe-column>
<vxe-column field="date" align="center" sortable title="发生时间" width="200px"></vxe-column>
<vxe-column field="name" align="center" title="监测点名" width="200px"></vxe-column>
<vxe-column field="address" align="center" title="事件描述"></vxe-column>
</vxe-table>
</div>

View File

@@ -52,11 +52,26 @@ const tableStore = new TableStore({
column: [
{ title: '名称', field: 'name' },
{ title: '编码', field: 'code' },
{ title: '排序', field: 'sort' },
{ title: '计算值', field: 'value' },
{ title: '事件等级', field: 'levelName' },
{ title: '算法描述', field: 'algoDescribe' },
{ title: '状态', field: 'stateName' },
{
title: '状态',
field: 'state',
width: '80',
render: 'tag',
custom: {
0: 'danger',
1: 'success'
},
replaceValue: {
0: '删除',
1: '正常'
}
},
{ title: '排序', field: 'sort', width: '80', },
{
title: '操作', fixed: 'right',
width: '180',

View File

@@ -50,13 +50,17 @@ const tableStore = new TableStore({
{ title: '更新时间', field: 'updateTime', sortable: true},
{
title: '状态',
field: 'stateName',
field: 'state',
width: '80',
render: 'tag',
custom: {
'正常': 'success',
'删除': 'danger'
0: 'danger',
1: 'success'
},
replaceValue: {
0: '删除',
1: '正常'
}
},
{
title: '操作', fixed: 'right',

View File

@@ -32,22 +32,22 @@ const tableStore = new TableStore({
// { title: '排序', field: 'sort',width:'80' },
{ title: '编码', field: 'code' },
{ title: '描述', field: 'remark' },
{
title: '状态',
field: 'status',
width: '80',
render: 'tag',
custom: {
0: 'success',
1: 'warning',
2: 'danger'
},
replaceValue: {
0: '正常',
1: '禁用',
2: '删除'
}
},
// {
// title: '状态',
// field: 'status',
// width: '80',
// render: 'tag',
// custom: {
// 0: 'success',
// 1: 'warning',
// 2: 'danger'
// },
// replaceValue: {
// 0: '正常',
// 1: '禁用',
// 2: '删除'
// }
// },
{
title: '操作', fixed: 'right',
width: '180',