绘制稳态事件配置页面

This commit is contained in:
guanj
2026-04-17 08:49:22 +08:00
parent 01a28d88f3
commit 99bc99a6fc
4 changed files with 483 additions and 9 deletions

View File

@@ -65,3 +65,35 @@ export function savePageIdWithUser(data: any) {
params: data
})
}
//新增稳态指标方案
export function save(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/save',
method: 'post',
data: data
})
}
//修改稳态指标方案
export function update(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/update',
method: 'post',
data: data
})
}
//新增稳态指标方案
export function deletePlan(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/delete',
method: 'post',
data: data
})
}
//根据ID查询稳态指标方案
export function getById(data: any) {
return createAxios({
url: '/cs-harmonic-boot/csHarmonicPlan/getById',
method: 'GET',
params: data
})
}

View File

@@ -1,6 +1,6 @@
<template>
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="transition: all 0.3s; overflow: hidden">
<div class="mt10 mr10" style="display: flex; justify-content: end">
<div class="mt10 mr10" style="display: flex; justify-content: end" v-if="showBut">
<el-button type="primary" icon="el-icon-Select" @click="save" :loading="loading">保存</el-button>
</div>
<Icon
@@ -14,7 +14,7 @@
/>
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>
@@ -39,7 +39,7 @@
</div>
<el-tree
:style="{ height: 'calc(100vh - 267px)' }"
:style="{ height: `calc(100vh - ${height}px)` }"
style="overflow: auto"
ref="treeRef"
:props="defaultProps"
@@ -69,9 +69,9 @@
<script lang="ts" setup>
import useCurrentInstance from '@/utils/useCurrentInstance'
import { ElTree } from 'element-plus'
import { emit } from 'process'
import { ref, watch } from 'vue'
import { t } from 'vxe-table'
import { useConfig } from '@/stores/config'
defineOptions({
@@ -82,12 +82,16 @@ interface Props {
width?: string
canExpand?: boolean
showPush?: boolean
showBut?: boolean
height?: number
}
const loading = ref(false)
const props = withDefaults(defineProps<Props>(), {
width: '280px',
canExpand: true,
showPush: false
showPush: false,
showBut: true,
height: 267
})
const config = useConfig()
const { proxy } = useCurrentInstance()
@@ -183,7 +187,5 @@ defineExpose({ treeRef, loading })
.custom-tree-node {
display: flex;
align-items: center;
}
</style>

View File

@@ -30,8 +30,9 @@ import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
defineOptions({
name: 'govern/alarm/index'
})
const deviceTree = ref([])
const activeName = ref('0')
const activeName = ref('1')
const key = ref(0)
getDeviceTree().then(res => {
// res.data.forEach((item: any) => {

View File

@@ -0,0 +1,439 @@
<template>
<div class="default-main" style="display: flex" :style="height">
<div style="width: 400px; overflow: hidden">
<div class="custom-table-header">
<div class="title">方案列表</div>
<el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增</el-button>
</div>
<Table ref="tableRef" :row-config="{ isCurrent: true, isHover: true }" @currentChange="currentChange" />
</div>
<div style="flex: 1; overflow: hidden">
<div class="custom-table-header">
<div class="title">指标配置</div>
<!-- <el-button :icon="Select" type="primary" @click="saveIndicator" class="ml10">保存</el-button> -->
</div>
<div class="pd10 borderBox" :style="height1" style="overflow-y: auto">
<H4 class="mt10">基础指标</H4>
<el-checkbox-group v-model="indicator">
<el-checkbox label="频率偏差" value="频率偏差" />
<el-checkbox label="电压偏差" value="电压偏差" />
<el-checkbox label="三相电压不平衡度" value="三相电压不平衡度" />
<el-checkbox label="闪变" value="闪变" />
<el-checkbox label="电压总谐波畸变率" value="电压总谐波畸变率" />
<el-checkbox label="负序电流" value="负序电流" />
</el-checkbox-group>
<H4 class="mt10">谐波电压</H4>
<div class="df">
<div class="title">谐波电压含有率:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll1" @change="handleCheckAllChange1">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleHarmonicVoltageChange">
<el-checkbox
v-for="num in 24"
:key="`harmonicVoltage_${num + 1}`"
:label="`${num + 1}次`"
:value="`${num + 1}次谐波电压含有率`"
/>
</el-checkbox-group>
</div>
</div>
<H4 class="mt10">谐波电流</H4>
<div class="df">
<div class="title">谐波电流有效值:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll2" @change="handleCheckAllChange2">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleHarmonicCurrentChange">
<el-checkbox
v-for="num in 24"
:key="`harmonicCurrent_${num + 1}`"
:label="`${num + 1}次`"
:value="`${num + 1}次谐波电流有效值`"
/>
</el-checkbox-group>
</div>
</div>
<H4 class="mt10">间谐波电压</H4>
<div class="df">
<div class="title">间谐波电压含有率:</div>
<div style="flex: 1">
<el-checkbox v-model="checkAll3" @change="handleCheckAllChange3">全选</el-checkbox>
<el-checkbox-group v-model="indicator" @change="handleInterharmonicVoltageChange">
<el-checkbox
v-for="num in 16"
:key="`interharmonicVoltage_${num}`"
:label="`${num - 0.5}次`"
:value="`${num - 0.5}次间谐波电压含有率`"
/>
</el-checkbox-group>
</div>
</div>
</div>
</div>
<div style="width: 370px">
<div class="custom-table-header">
<div class="title">监测点绑定</div>
<el-button :icon="Select" type="primary" @click="saveIndicator" class="ml10" :loading="loading">
保存
</el-button>
</div>
<!-- <steadyStateTree /> -->
<Tree
class="borderBox"
ref="treeRef"
show-checkbox
:showBut="false"
width="370px"
:height="260"
:data="menuTree"
:checkStrictly="checkStrictly"
></Tree>
</div>
<!-- 新增/编辑弹框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="400px" :before-close="handleClose">
<el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
<el-form-item label="方案名称" prop="name">
<el-input v-model.trim="formData.name" placeholder="请输入方案名称" clearable />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="formData.sort" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { Plus, Select } from '@element-plus/icons-vue'
import { ref, onMounted, provide, computed } from 'vue'
import TableStore from '@/utils/tableStore'
import { getLineTree, getCldTree } from '@/api/cs-device-boot/csLedger'
import Table from '@/components/table/index.vue'
import { save, deletePlan, update, getById } from '@/api/cs-harmonic-boot/mxgraph'
import { mainHeight } from '@/utils/layout'
import { ElMessage } from 'element-plus'
import steadyStateTree from '@/components/tree/govern/steadyStateTree.vue'
import { useConfig } from '@/stores/config'
import Tree from '@/components/tree/allocation.vue'
import { id } from 'element-plus/es/locale'
defineOptions({
name: 'govern/steadyStateEvent'
})
const height = mainHeight(20)
const height1 = mainHeight(90)
const config = useConfig()
const tableRef = ref()
const checkAll1 = ref(false)
const checkAll2 = ref(false)
const checkAll3 = ref(false)
const menuTree: any = ref([])
const checkStrictly = ref(false)
const indicator: any = ref([])
const lineList: any = ref([])
const copyRow: any = ref({})
const treeRef = ref()
const loading = ref(false)
// 弹框相关
const dialogVisible = ref(false)
const dialogTitle = ref('新增方案')
const formRef = ref()
const formData = ref({
id: '',
sort: 0,
lineList: [],
harmonicTarget: '',
name: ''
})
const formRules = {
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
}
// 生成各分组的指标值数组
const harmonicVoltageValues = computed(() => Array.from({ length: 24 }, (_, i) => `${i + 2}次谐波电压含有率`))
const harmonicCurrentValues = computed(() => Array.from({ length: 24 }, (_, i) => `${i + 2}次谐波电流有效值`))
const interharmonicVoltageValues = computed(() => Array.from({ length: 16 }, (_, i) => `${i + 0.5}次间谐波电压含有率`))
// 谐波电压全选/取消全选
const handleCheckAllChange1 = (val: any) => {
if (val) {
const newValues = harmonicVoltageValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !harmonicVoltageValues.value.includes(v))
}
}
// 谐波电流全选/取消全选
const handleCheckAllChange2 = (val: any) => {
if (val) {
const newValues = harmonicCurrentValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !harmonicCurrentValues.value.includes(v))
}
}
// 间谐波电压全选/取消全选
const handleCheckAllChange3 = (val: any) => {
if (val) {
const newValues = interharmonicVoltageValues.value.filter(v => !indicator.value.includes(v))
indicator.value = [...indicator.value, ...newValues]
} else {
indicator.value = indicator.value.filter(v => !interharmonicVoltageValues.value.includes(v))
}
}
// 谐波电压分组内值变化时,更新全选状态
const handleHarmonicVoltageChange = () => {
const selectedInGroup = harmonicVoltageValues.value.filter(v => indicator.value.includes(v))
checkAll1.value =
selectedInGroup.length == harmonicVoltageValues.value.length && harmonicVoltageValues.value.length > 0
}
// 谐波电流分组内值变化时,更新全选状态
const handleHarmonicCurrentChange = () => {
const selectedInGroup = harmonicCurrentValues.value.filter(v => indicator.value.includes(v))
checkAll2.value =
selectedInGroup.length == harmonicCurrentValues.value.length && harmonicCurrentValues.value.length > 0
}
// 间谐波电压分组内值变化时,更新全选状态
const handleInterharmonicVoltageChange = () => {
const selectedInGroup = interharmonicVoltageValues.value.filter(v => indicator.value.includes(v))
checkAll3.value =
selectedInGroup.length == interharmonicVoltageValues.value.length && interharmonicVoltageValues.value.length > 0
}
const tableStore = new TableStore({
showPage: false,
url: '/cs-harmonic-boot/csHarmonicPlan/list',
method: 'GET',
publicHeight: 70,
column: [
{ title: '方案名称', field: 'name', align: 'center' },
{
title: '操作',
fixed: 'right',
align: 'center',
width: '100',
render: 'buttons',
buttons: [
{
name: 'edit',
title: '编辑',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
openDialog('edit', row)
}
},
{
name: 'del',
title: '删除',
type: 'danger',
icon: 'el-icon-Delete',
render: 'confirmButton',
popconfirm: {
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonType: 'danger',
title: '确定删除该角色吗?'
},
click: row => {
deletePlan([row.id]).then(() => {
ElMessage.success('删除成功')
tableStore.index()
})
}
}
]
}
],
beforeSearchFun: () => {},
loadCallback: () => {
tableRef.value.getRef().setCurrentRow(tableStore.table.data[0])
currentChange({
row: tableStore.table.data[0]
})
}
})
tableStore.table.params.searchValue = ''
getCldTree().then(res => {
res.data?.children.map((item: any) => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item: any) => {
item.icon = 'el-icon-List'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach((item2: any) => {
item2.icon = 'el-icon-Platform'
item2.color = item2.comFlag == 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
item2.children.forEach((item3: any) => {
item3.icon = 'el-icon-Platform'
item3.color = item3.comFlag == 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
})
})
})
})
menuTree.value = filterTreeKeepPath(res.data.children)
})
provide('tableStore', tableStore)
const filterTreeKeepPath = (treeData: any) => {
const result = []
for (const node of treeData) {
// 递归处理子节点
const filteredChildren: any = node.children?.length ? filterTreeKeepPath(node.children) : []
// 如果当前节点 level == 3或者子节点中有符合条件的数据
if (node.level == 3 || filteredChildren.length > 0) {
result.push({
...node,
children: filteredChildren
})
}
}
return result
}
// 打开弹框
const openDialog = (type: string, row?: any) => {
if (type == 'add') {
dialogTitle.value = '新增方案'
formData.value = {
id: '',
name: '',
lineList: [],
harmonicTarget: '',
sort: 0
}
} else if (type == 'edit') {
dialogTitle.value = '编辑方案'
formData.value = {
id: row.id,
lineList: row.lineList,
harmonicTarget: row.harmonicTarget,
sort: row.sort,
name: row.name
}
}
dialogVisible.value = true
}
// 关闭弹框
const handleClose = () => {
dialogVisible.value = false
formRef.value?.resetFields()
}
// 提交表单
const submitForm = async () => {
await formRef.value?.validate()
if (dialogTitle.value == '新增方案') {
await save({
harmonicTarget: '',
id: '',
lineList: [],
name: formData.value.name,
sort: formData.value.sort
}).then((res: any) => {
ElMessage.success(`${dialogTitle.value}成功`)
dialogVisible.value = false
tableStore.index() // 刷新列表
})
} else {
await update({
harmonicTarget: formData.value.harmonicTarget,
id: formData.value.id,
lineList: formData.value.lineList,
name: formData.value.name,
sort: formData.value.sort
}).then((res: any) => {
ElMessage.success(`${dialogTitle.value}成功`)
dialogVisible.value = false
tableStore.index() // 刷新列表
})
}
// 模拟保存成功
}
// 保存指标
const saveIndicator = () => {
// TODO: 调用保存指标接口
loading.value = true
update({
harmonicTarget: indicator.value.join(','),
id: copyRow.value.id,
lineList: treeRef.value.treeRef
.getCheckedNodes(false, true)
.filter(item => item.level == 3)
.map((node: any) => node.id),
name: copyRow.value.name,
sort: copyRow.value.sort
})
.then((res: any) => {
ElMessage.success(`保存成功`)
loading.value = false
})
.catch(() => {
loading.value = false
})
}
const currentChange = (data: any) => {
getById({ id: data.row.id }).then((res: any) => {
checkStrictly.value = true
indicator.value = res.data.harmonicTarget.split(',')
lineList.value = res.data.lineList || []
copyRow.value = res.data
treeRef.value.treeRef.setCheckedKeys(lineList.value)
setTimeout(() => {
handleInterharmonicVoltageChange()
handleHarmonicCurrentChange()
handleHarmonicVoltageChange()
checkStrictly.value = false
}, 100)
})
}
onMounted(() => {
tableStore.index()
})
const addRole = () => {
openDialog('add')
}
</script>
<style lang="scss" scoped>
:deep(.el-tabs--border-card > .el-tabs__content) {
padding: 0px !important;
}
.df {
display: flex;
.title {
width: 125px;
}
:deep(.el-checkbox) {
width: 70px;
}
}
.custom-table-header {
height: 60px;
}
.borderBox {
border: 1px solid var(--el-border-color);
}
</style>