修改测试bug

This commit is contained in:
guanj
2026-06-08 18:34:49 +08:00
parent 4f907a80c4
commit 03d302ded8
133 changed files with 3991 additions and 3442 deletions

View File

@@ -12,7 +12,7 @@ import { provide, onMounted, ref } from 'vue'
// let buildUrl = 'wss://pqmcn.com:8087/mqtt'//102
// let buildUrl = 'ws://pqmcn.com:8073/mqtt' //27
let buildUrl = 'ws://192.168.1.103:8083/mqtt' //27
let buildUrl = 'ws://112.4.144.18:38083/mqtt' //27
// 从 Nginx 获取 MQTT URL
const fetchMqttUrl = async () => {

View File

@@ -6,10 +6,10 @@
</el-row>
<el-row class="ba-array-item" v-for="(item, idx) in state.value" :gutter="10" :key="idx">
<el-col :span="10">
<el-input v-model="item.key"></el-input>
<el-input maxlength="32" show-word-limit v-model="item.key"></el-input>
</el-col>
<el-col :span="10">
<el-input v-model="item.value"></el-input>
<el-input maxlength="32" show-word-limit v-model="item.value"></el-input>
</el-col>
<el-col :span="4">
<el-button @click="onDelArrayItem(idx)" size="small" icon="el-icon-Delete" circle />

View File

@@ -61,7 +61,7 @@
</transition>
</div>
<template #reference>
<el-input
<el-input maxlength="32" show-word-limit
v-model="state.inputValue"
:size="size"
:disabled="disabled"

View File

@@ -1,37 +1,18 @@
<template>
<div>
<!--治理效果报表 -->
<TableHeader
:showReset="false"
:timeKeyList="prop.timeKey"
ref="TableHeaderRef"
datePicker
@selectChange="selectChange"
v-if="fullscreen"
>
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" datePicker
@selectChange="selectChange" v-if="fullscreen">
<template v-slot:select>
<el-form-item label="模板策略">
<el-select
filterable
v-model="tableStore.table.params.tempId"
placeholder="请选择模板策略"
clearable
>
<el-option
v-for="item in templateList"
:key="item.id"
:label="item.excelName"
:value="item.id"
/>
<el-select filterable v-model="tableStore.table.params.tempId" placeholder="请选择模板策略" clearable>
<el-option v-for="item in templateList" :key="item.id" :label="item.excelName"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="监测对象">
<el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<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>
@@ -40,14 +21,12 @@
<el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出</el-button>
</template>
</TableHeader>
<div style="display: flex">
<div
id="luckysheet"
:style="{
width: `calc(${prop.width} )`,
height: `calc(${prop.height} - 57px + ${fullscreen ? 0 : 56}px)`
}"
></div>
<div style="display: flex" >
<div id="luckysheet" :style="{
width: `calc(${prop.width} )`,
height: `calc(${prop.height} - 57px + ${fullscreen ? 0 : 56}px)`
}"></div>
</div>
</div>
</template>
@@ -118,6 +97,7 @@ const downloadExcel = () => {
}
onMounted(() => {
initListByIds()
})
onUnmounted(() => {
@@ -199,6 +179,12 @@ watch(
tableStore.index()
}
)
watch(
() => prop.height,
val => {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
}
)
watch(
() => prop.timeValue,
(newVal, oldVal) => {

View File

@@ -11,7 +11,7 @@
>
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input 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>

View File

@@ -10,9 +10,9 @@
>
<template #select>
<el-form-item label="关键字筛选">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable
@@ -342,10 +342,11 @@ const cellClickEvent = ({ row, column }: any) => {
// 下载报告
const downloadTheReport = (lineId: string, name: string) => {
getReportUrl({ lineId: lineId }).then((res: any) => {
ElMessage.info('下载中......')
forceDownloadPdf(res.data, name.split('/').pop() || '')
})
}
const forceDownloadPdf = async (pdfUrl, fileName = '文件.pdf') => {
const forceDownloadPdf = async (pdfUrl:any, fileName = '文件.pdf') => {
try {
// 1. 请求 PDF 并转为 Blob关键绕开浏览器直接解析
const response = await fetch(pdfUrl, {
@@ -372,8 +373,9 @@ const forceDownloadPdf = async (pdfUrl, fileName = '文件.pdf') => {
// 4. 清理资源(避免内存泄漏)
document.body.removeChild(a)
URL.revokeObjectURL(blobUrl)
ElMessage.success('下载成功')
} catch (error) {
console.error('PDF 下载失败:', error)
console.error('下载失败:', error)
// ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus
}
}

View File

@@ -10,9 +10,9 @@
>
<template #select>
<el-form-item label="关键字筛选">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
style="width: 240px"
v-model.trim="tableStore.table.params.searchValue"
clearable

View File

@@ -258,7 +258,7 @@ self.onmessage = function (e) {
boxoList.measurementPointName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'%  持续时间:' +
boxoList.duration +
@@ -270,7 +270,7 @@ self.onmessage = function (e) {
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
@@ -282,7 +282,7 @@ self.onmessage = function (e) {
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
@@ -295,7 +295,7 @@ self.onmessage = function (e) {
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +

View File

@@ -609,7 +609,7 @@ const initWave = (
$(`#${rmsId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
}
} else {
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值:${(
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降幅值:${(
Number(eventValue.value) * 1
).toFixed(0)}% 持续时间:${persistTime.value}s`
}
@@ -854,7 +854,7 @@ const initWave = (
data: rmscu
},
{
name: '最小暂降(骤升)幅值',
name: '最小暂降幅值',
type: 'scatter',
symbol: 'image://' + url2,
itemStyle: { width: 45, height: 45 },

View File

@@ -131,7 +131,7 @@ self.addEventListener('message', function (e) {
boxoList.measurementPointName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +
@@ -143,7 +143,7 @@ self.addEventListener('message', function (e) {
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
@@ -155,7 +155,7 @@ self.addEventListener('message', function (e) {
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
@@ -168,7 +168,7 @@ self.addEventListener('message', function (e) {
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
' 暂降幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +

View File

@@ -375,7 +375,7 @@ const initWave = (
$(`#${waveId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
}
} else {
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值:${(
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降幅值:${(
Number(eventValue.value) * 1
).toFixed(0)}% 持续时间:${persistTime.value}s`
}

View File

@@ -95,6 +95,7 @@ const shortcuts = [
// 计算过滤后的 timeOptions
const filteredTimeOptions = computed(() => {
console.log("🚀 ~ props.timeKeyList :", props.timeKeyList )
if (!props.timeKeyList || props.timeKeyList.length === 0) {
return timeOptions.value
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="mac-address-input" :class="{ disabled: disabled }">
<el-input
<el-input show-word-limit
ref="inputRef"
placeholder="请输入设备mac地址"
v-model="macValue"

View File

@@ -81,7 +81,7 @@ import { defineProps } from 'vue'
import { useTimeCacheStore } from '@/stores/timeCache'
import { useRoute } from 'vue-router'
const emit = defineEmits(['selectChange'])
const emit = defineEmits(['selectChange','onResetForm'])
const tableStore = inject('tableStore') as TableStore
const tableHeader = ref()
@@ -244,6 +244,7 @@ const setDatePicker = (list: any) => {
const onResetForm = () => {
//时间重置成默认值
datePickerRef.value?.setInterval(3)
emit('onResetForm')
tableStore.onTableAction('reset', {})
}
const setInterval = (val: any) => {

View File

@@ -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" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>

View File

@@ -11,7 +11,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" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>

View File

@@ -12,8 +12,8 @@
/> -->
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
<div style="display: flex; align-items: center" class="mb10">
<el-input
maxlength="32"
<el-input maxlength="32" show-word-limit
v-model.trim="filterText"
autocomplete="off"
placeholder="请输入内容"
@@ -202,10 +202,10 @@ const props = withDefaults(defineProps<Props>(), {
leafMode: 'line'
})
const treeType = ref('1')
const treeType = ref('2')
const options = [
{ label: '工程', value: '2' },
{ label: '设备', value: '1' },
{ label: '工程', value: '2' }
]
const { proxy } = useCurrentInstance()
@@ -379,7 +379,7 @@ const changeTreeType = (val: string) => {
}
onMounted(() => {
treeType.value = props.engineering ? '2' : '1'
// treeType.value = props.engineering ? '2' : '1'
})
</script>

View File

@@ -62,9 +62,8 @@ async function loadTree() {
}
await selectTreeNode(treRef.value, node, {
level: 3,
onSelect: selected => {
emit('init', { ...selected, level: 3 })
emit('init', { ...selected, level: selected.level ?? 3 })
changePointType('4', selected)
}
})

View File

@@ -1,12 +1,12 @@
<template>
<Tree ref="treRef" @check-change="handleCheckChange" :default-checked-keys="defaultCheckedKeys"
<Tree ref="treRef" @check-change="handleCheckChange" :default-checked-keys="defaultCheckedKeys" default-expand-all
:show-checkbox="props.showCheckbox" :data="tree" :height="props.height" @changeDeviceType="changeDeviceType"
@changeTreeType="loadTree" :engineering="props.engineering" leaf-mode="device" />
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { throttle } from 'lodash'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { debounce } from 'lodash-es'
import Tree from '../device.vue'
import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
import { useConfig } from '@/stores/config'
@@ -39,7 +39,11 @@ const tree = ref<any[]>([])
const treRef = ref<InstanceType<typeof Tree>>()
const decorators = createLineTreeDecorators(() => config.getColorVal('elementUiPrimary'))
const changeDeviceType = (val: any, obj: any) => emit('deviceTypeChange', val, obj)
const changeDeviceType = debounce((val: any, obj: any) => {
emit('deviceTypeChange', val, obj)
}, 300)
onBeforeUnmount(() => changeDeviceType.cancel())
async function selectInitialNode(type: string | undefined, leaves: LineTreeLeaves) {
const candidates: { refKey: TreeRefKey; list: any[]; level: number }[] =
@@ -70,7 +74,7 @@ const loadTree = (type?: string) => {
})
}
onMounted(() => loadTree(props.engineering ? '2' : '1'))
onMounted(() => loadTree( '2' ))
const handleCheckChange =
(data: any, checked: any, indeterminate: any) => {

View File

@@ -18,7 +18,8 @@ export interface LineTreeDecorators {
export function createLineTreeDecorators(getPrimaryColor: () => string): LineTreeDecorators {
const offlineColor = '#e26257 !important'
const statusColor = (comFlag: number) => (comFlag === 2 ? getPrimaryColor() : offlineColor)
// const statusColor = (comFlag: number) => (comFlag === 2 ? getPrimaryColor() : offlineColor)
const statusColor = (comFlag: number) => (comFlag === 2 ? "#2ab914" : offlineColor)
const applyMeta = (
node: any,
@@ -85,7 +86,7 @@ export function decorateLineTree(
})
grand.children?.forEach((leaf: any) => {
applyMeta(leaf, {
icon: 'el-icon-Platform',
icon: 'local-监测点',
color: statusColor(leaf.comFlag),
...LINE_LEAF_META
})
@@ -110,7 +111,7 @@ export function decorateLineTree(
})
l3.children?.forEach((l4: any) => {
applyMeta(l4, {
icon: 'el-icon-Platform',
icon: 'local-监测点',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})
@@ -124,7 +125,7 @@ export function decorateLineTree(
applyMeta(l1, { icon: 'el-icon-Platform', color: statusColor(l1.comFlag) })
l1.children?.forEach((l2: any) => {
applyMeta(l2, {
icon: 'el-icon-Platform',
icon: 'local-监测点',
color: statusColor(l2.comFlag),
...LINE_LEAF_META
})
@@ -145,7 +146,7 @@ export function decorateLineTree(
})
l3.children?.forEach((l4: any) => {
applyMeta(l4, {
icon: 'el-icon-Platform',
icon: 'local-监测点',
color: statusColor(l4.comFlag),
...LINE_LEAF_META
})

View File

@@ -76,6 +76,7 @@ async function selectInitialNode(type: string | undefined, leaves: LineTreeLeave
}
const loadTree = (type?: string) => {
console.log("🚀 ~ loadTree ~ type:", type)
tree.value = []
getLineTree({ type: type === '2' ? 'engineering' : '' }).then(res => {
const leaves = decorateLineTree(res.data, type, decorators, { disableParents: false })
@@ -89,11 +90,13 @@ function bootstrap() {
querySysExcel({})
.then((res: any) => {
emit('Policy', res.data)
loadTree()
loadTree('2')
})
.catch(() => loadTree())
} else {
loadTree()
} else {
loadTree('2')
}
}

View File

@@ -3,7 +3,7 @@
<div style="transition: all 0.3s; overflow: hidden; height: 100%">
<div class="cn-tree">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<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>

View File

@@ -30,11 +30,27 @@ export async function selectTreeNode(
) {
if (!node) return false
const treeInstance = await waitForSingleTreeRef(treRef)
treeInstance?.setCurrentKey(node.id)
if (!treeInstance) return false
await nextTick()
expandTreeNodePath(treeInstance, node.id)
treeInstance.setCurrentKey(node.id)
options?.onSelect?.(node)
return true
}
/** 展开到目标节点的全部祖先,保证深层节点可见 */
export function expandTreeNodePath(treeInstance: any, nodeId: string) {
const target = treeInstance.getNode?.(nodeId)
if (!target) return
let parent = target.parent
while (parent && parent.level > 0) {
parent.expanded = true
parent = parent.parent
}
}
export function bootstrapWithTemplate(
template: boolean,
loadFn: () => void,

View File

@@ -13,8 +13,8 @@
/> -->
<div class="cn-tree" :class="{ 'is-hidden': menuCollapse }">
<div class="cn-tree__toolbar">
<el-input
maxlength="32"
<el-input maxlength="32" show-word-limit
v-model.trim="filterText"
placeholder="请输入内容"
clearable

View File

@@ -13,7 +13,7 @@
/> -->
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<el-input v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prepend>
<el-select v-model="treeType" @change="changeTreeType" style="min-width: 75px">
<el-option
@@ -193,10 +193,10 @@ const props = withDefaults(defineProps<Props>(), {
const route = useRoute()
const { proxy } = useCurrentInstance()
const treeType = ref('1')
const treeType = ref('2')
const options = [
{ label: '工程', value: '2' },
{ label: '设备', value: '1' },
{ label: '工程', value: '2' }
]
const menuCollapse = ref(false)

View File

@@ -11,7 +11,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" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
<template #prefix>
<Icon name="el-icon-Search" style="font-size: 16px" />
</template>

View File

@@ -63,7 +63,7 @@
:model-value="configStore.getColorVal('menuTopBarBackground')" />
</el-form-item>
<el-form-item label="侧边菜单宽度(展开时)">
<el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')"
<el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')"
type="number" :step="10" :model-value="configStore.layout.menuWidth">
<template #append>px</template>
</el-input>

View File

@@ -116,11 +116,11 @@ const handleCommand = async (key: string) => {
popupPwd.value.open()
break
case 'layout':
await window.location.reload()
navTabs.closeTabs()
window.localStorage.clear()
adminInfo.reset()
setTimeout(() => {
navTabs.closeTabs()
window.localStorage.clear()
adminInfo.reset()
window.location.reload()
router.push({ name: 'login' })
}, 0)
break
@@ -231,9 +231,11 @@ const handleCommand = async (key: string) => {
0% {
transform: scale(0);
}
80% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}

View File

@@ -3,22 +3,22 @@
<el-form :inline="false" :model="form" label-width="auto" class="form-one">
<el-form-item label="用户名称:">
<el-input v-model.trim="form.name" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.name" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="登录名称:" class="top">
<el-input v-model.trim="form.loginName" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" :disabled="true"></el-input>
</el-form-item>
<!-- <el-form-item label="归属部门名称:" class="top">
<el-input v-model.trim="form.deptName" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.deptName" :disabled="true"></el-input>
</el-form-item> -->
<el-form-item label="拥有的角色:" class="top">
<el-input v-model.trim="form.role" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.role" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="电话号码:" class="top">
<el-input v-model.trim="form.phone" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="电子邮箱:" class="top">
<el-input v-model.trim="form.email" :disabled="true"></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="form.email" :disabled="true"></el-input>
</el-form-item>
</el-form>
</el-dialog>

View File

@@ -3,13 +3,13 @@
<el-scrollbar>
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one" ref="formRef">
<el-form-item label="校验密码:" prop="password">
<el-input v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password />
<el-input maxlength="32" show-word-limit v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password />
</el-form-item>
<el-form-item label="新密码:" prop="newPwd">
<el-input v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password />
<el-input maxlength="32" show-word-limit v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password />
</el-form-item>
<el-form-item label="确认密码:" prop="confirmPwd">
<el-input
<el-input maxlength="32" show-word-limit
v-model.trim="form.confirmPwd"
type="password"
placeholder="请输入确认密码"

View File

@@ -1,88 +1,88 @@
<script setup lang="ts">
import { ref, computed, reactive } from 'vue'
import { useValidator } from '@/hooks/web/useValidator'
import { useDesign } from '@/hooks/web/useDesign'
import { useLockStore } from '@/stores/modules/lock'
import avatarImg from '@/assets/imgs/avatar.gif'
import { useUserStore } from '@/stores/modules/user'
import { useI18n } from 'vue-i18n'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('lock-dialog')
const { required } = useValidator()
const { t } = useI18n()
const lockStore = useLockStore()
const props = defineProps({
modelValue: {
type: Boolean
}
})
const userStore = useUserStore()
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
const userName = computed(() => userStore.user.nickname ?? 'Admin')
const emit = defineEmits(['update:modelValue'])
const dialogVisible = computed({
get: () => props.modelValue,
set: val => {
// console.log('set: ', val)
emit('update:modelValue', val)
}
})
const dialogTitle = ref(t('lock.lockScreen'))
const formData = ref({
password: undefined
})
const formRules = reactive({
password: [required()]
})
const formRef = ref() // 表单 Ref
const handleLock = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
dialogVisible.value = false
lockStore.setLockInfo({
...formData.value,
isLock: true
})
}
</script>
<template>
<Dialog v-model.trim="dialogVisible" width="500px" max-height="170px" :class="prefixCls" :title="dialogTitle">
<div class="flex flex-col items-center">
<img :src="avatar" alt="" class="w-70px h-70px rounded-[50%]" />
<span class="text-14px my-10px text-[var(--top-header-text-color)]">
{{ userName }}
</span>
</div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item :label="t('lock.lockPassword')" prop="password">
<el-input maxlength="32" show-word-limit type="password" v-model.trim="formData.password"
:placeholder="'请输入' + t('lock.lockPassword')" clearable show-password />
</el-form-item>
</el-form>
<template #footer>
<ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
</template>
</Dialog>
</template>
<style lang="scss" scoped>
:global(.v-lock-dialog) {
@media (max-width: 767px) {
max-width: calc(100vw - 16px);
}
}
</style>
<script setup lang="ts">
import { ref, computed, reactive } from 'vue'
import { useValidator } from '@/hooks/web/useValidator'
import { useDesign } from '@/hooks/web/useDesign'
import { useLockStore } from '@/stores/modules/lock'
import avatarImg from '@/assets/imgs/avatar.gif'
import { useUserStore } from '@/stores/modules/user'
import { useI18n } from 'vue-i18n'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('lock-dialog')
const { required } = useValidator()
const { t } = useI18n()
const lockStore = useLockStore()
const props = defineProps({
modelValue: {
type: Boolean
}
})
const userStore = useUserStore()
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
const userName = computed(() => userStore.user.nickname ?? 'Admin')
const emit = defineEmits(['update:modelValue'])
const dialogVisible = computed({
get: () => props.modelValue,
set: val => {
// console.log('set: ', val)
emit('update:modelValue', val)
}
})
const dialogTitle = ref(t('lock.lockScreen'))
const formData = ref({
password: undefined
})
const formRules = reactive({
password: [required()]
})
const formRef = ref() // 表单 Ref
const handleLock = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
dialogVisible.value = false
lockStore.setLockInfo({
...formData.value,
isLock: true
})
}
</script>
<template>
<Dialog v-model.trim="dialogVisible" width="500px" max-height="170px" :class="prefixCls" :title="dialogTitle">
<div class="flex flex-col items-center">
<img :src="avatar" alt="" class="w-70px h-70px rounded-[50%]" />
<span class="text-14px my-10px text-[var(--top-header-text-color)]">
{{ userName }}
</span>
</div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item :label="t('lock.lockPassword')" prop="password">
<el-input maxlength="32" show-word-limit type="password" v-model.trim="formData.password"
:placeholder="'请输入' + t('lock.lockPassword')" clearable show-password />
</el-form-item>
</el-form>
<template #footer>
<ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
</template>
</Dialog>
</template>
<style lang="scss" scoped>
:global(.v-lock-dialog) {
@media (max-width: 767px) {
max-width: calc(100vw - 16px);
}
}
</style>

View File

@@ -3,13 +3,13 @@
<el-scrollbar>
<el-form :inline='false' :model='form' label-width='auto' class="form-one" :rules='rules' ref='formRef'>
<el-form-item label='角色名称'>
<el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' />
<el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' />
</el-form-item>
<el-form-item label='角色编码'>
<el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' />
<el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' />
</el-form-item>
<el-form-item label='角色描述'>
<el-input maxlength="300" show-word-limit v-model.trim='form.remark' :rows='2' type='textarea'
<el-input show-word-limit maxlength="300" v-model.trim='form.remark' :rows='2' type='textarea'
placeholder='请输入描述' />
</el-form-item>
</el-form>

View File

@@ -1,4 +1,5 @@
import { ElMessage } from 'element-plus'
import { nextTick } from 'vue'
import { exportExcel } from '@/views/system/reportForms/export.js'
/** 解析 Luckysheet 接口返回的 sheet 数据 */
@@ -48,14 +49,16 @@ export function renderLuckysheetReport(
) {
parseLuckysheetSheets(sheets)
destroyLuckysheet()
setTimeout(() => {
luckysheet.create({
container,
...DEFAULT_REPORT_OPTIONS,
...options,
data: sheets,
nextTick(() => {
requestAnimationFrame(() => {
luckysheet.create({
container,
...DEFAULT_REPORT_OPTIONS,
...options,
data: sheets,
})
})
}, 10)
})
}
/** 安全导出 Luckysheet无数据时提示并返回 false */
@@ -75,6 +78,7 @@ export function exportLuckysheetFile(filename: string, hasData = true): boolean
return false
}
exportExcel(sheets, filename)
ElMessage.success('生成成功')
return true
} catch {
ElMessage.warning('导出失败,请先加载报表数据')

View File

@@ -2,9 +2,9 @@
<div>
<div class="custom-table-header">
<div class="title">接口权限列表</div>
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
style="width: 240px"
placeholder="请输入菜单名称"

View File

@@ -2,7 +2,7 @@
<div>
<div class="custom-table-header">
<div class="title">菜单列表</div>
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
style="width: 310px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" />
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button>
</div>

View File

@@ -11,18 +11,18 @@
class="form-one"
>
<el-form-item prop="name" label="接口/按钮名称">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" />
</el-form-item>
<el-form-item prop="code" label="接口/按钮标识">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.code"
placeholder="请输入英文接口标识"
/>
</el-form-item>
<el-form-item prop="path" label="接口路径">
<el-input v-model.trim="form.path" placeholder="请输入接口路径" />
<el-input maxlength="32" show-word-limit v-model.trim="form.path" placeholder="请输入接口路径" />
</el-form-item>
<el-form-item prop="type" label="接口类型">
<el-radio-group v-model.trim="form.type">
@@ -31,12 +31,12 @@
</el-radio-group>
</el-form-item>
<el-form-item prop="sort" label="排序">
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />
</el-form-item>
<el-form-item prop="remark" label="接口/按钮描述">
<el-input
<el-input show-word-limit
maxlength="300"
show-word-limit
v-model.trim="form.remark"
:rows="2"
type="textarea"

View File

@@ -12,27 +12,27 @@
/>
</el-form-item>
<el-form-item label="菜单名称" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="图标">
<IconSelector v-model.trim="form.icon" placeholder="请选择图标" />
</el-form-item>
<el-form-item label="菜单路由" prop="path">
<el-input v-model.trim="form.path" placeholder="请输入菜单名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.path" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="组件路径" prop="routeName">
<el-input
<el-input maxlength="32" show-word-limit
v-model.trim="form.routeName"
placeholder="请输入组件路径,如/src/views/dashboard/index.vue"
/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />
</el-form-item>
<el-form-item label="菜单描述">
<el-input
<el-input show-word-limit
maxlength="300"
show-word-limit
v-model.trim="form.remark"
:rows="2"
type="textarea"

View File

@@ -2,15 +2,15 @@
<el-dialog class="cn-operate-dialog" width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" ref="formRef" :model="form" label-width="auto" class="form-one" :rules="rules">
<el-form-item label="角色名称" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="角色编码" prop="code">
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入菜单名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="角色描述">
<el-input
<el-input show-word-limit
maxlength="300"
show-word-limit
v-model.trim="form.remark"
:rows="2"
type="textarea"

View File

@@ -15,7 +15,7 @@
</el-select>
</el-form-item>
<el-form-item label="关键字筛选:">
<el-input maxlength="32" show-word-limit style="width: 240px"
<el-input maxlength="32" show-word-limit style="width: 240px"
v-model.trim="tableStore.table.params.searchValue" clearable placeholder="仅根据用户名/登录名" />
</el-form-item>
</template>

View File

@@ -2,15 +2,15 @@
<el-dialog class="cn-operate-dialog" v-model.trim="dialogVisible" :title="title">
<el-form :model="form" ref="formRef" label-width="auto" class="form-two" :rules="rules">
<el-form-item label="用户名" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入昵称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="登录名" prop="loginName">
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" />
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" />
</el-form-item>
<el-form-item label="默认密码" prop="password" v-if="title === '新增用户'">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.password"
placeholder="请输入密码"
disabled
@@ -51,19 +51,19 @@
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" />
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
</el-form-item>
<el-form-item label="时间段" prop="limitTime">
<el-slider v-model.trim="form.limitTime" style="width: 95%" range :max="24" />
</el-form-item>
<el-form-item label="起始IP" prop="limitIpStart">
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />
</el-form-item>
<el-form-item label="结束IP" prop="limitIpEnd">
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" />
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" />
</el-form-item>
<el-form-item label="短信通知" prop="smsNotice">
@@ -84,9 +84,9 @@
<el-radio-button :label="1"></el-radio-button>
<el-radio-button :label="0"></el-radio-button>
</el-radio-group>
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
:disabled="title !== '新增用户'"
v-model.trim="form.id"
placeholder="请输入用户id"

View File

@@ -3,11 +3,11 @@
<el-scrollbar>
<el-form :inline="false" :model="form" label-width="120px" class="form-one" :rules="rules" ref="formRef">
<el-form-item label="新密码" prop="newPwd">
<el-input maxlength="32" show-word-limit v-model.trim="form.newPwd" type="password"
<el-input maxlength="32" show-word-limit v-model.trim="form.newPwd" type="password"
placeholder="请输入新密码" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPwd">
<el-input maxlength="32" show-word-limit v-model.trim="form.confirmPwd" type="password"
<el-input maxlength="32" show-word-limit v-model.trim="form.confirmPwd" type="password"
placeholder="请输入确认密码" show-password />
</el-form-item>
</el-form>

View File

@@ -24,11 +24,11 @@
</el-form-item>
</template> -->
<template #select>
<el-form-item label="设备名称">
<el-input
maxlength="32"
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit
clearable
show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入设备名称"
/>

View File

@@ -1,7 +1,7 @@
<template>
<TableHeader datePicker ref="refheader" showExport>
<template v-slot:select>
<el-form-item label="数据来源">
<!-- <el-form-item label="数据来源">
<el-cascader
v-model.trim="tableStore.table.params.cascader"
filterable
@@ -12,8 +12,11 @@
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable
></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
</el-form-item>
</el-form-item> -->
<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.level" placeholder="请选择级别" clearable>
<el-option
@@ -100,10 +103,10 @@ const tableStore = new TableStore({
}
},
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120 },
{ title: '项目名称', field: 'projectName', align: 'center', minWidth: 120 },
{ title: '设备名称', field: 'equipmentName', align: 'center', minWidth: 120 },
{ title: '监测点名称', field: 'lineName', align: 'center', minWidth: 120 },
{ title: '项目名称', field: 'projectName', align: 'center', minWidth: 120 },
{ title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120 },
// { title: '监测点名称', field: 'lineName', align: 'center', minWidth: 120 },
{
title: '模块信息',
@@ -189,6 +192,7 @@ provide('tableStore', tableStore)
// "target": [],
// "type": "",
// "userId": ""
tableStore.table.params.searchValue = ''
tableStore.table.params.cascader = ''
tableStore.table.params.level = ''
tableStore.table.params.engineeringid = ''

View File

@@ -2,9 +2,9 @@
<TableHeader datePicker ref="refheader" showExport>
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入前置服务器名称ip"
/>

View File

@@ -1,12 +1,15 @@
<template>
<TableHeader datePicker ref="refheader" showExport>
<template v-slot:select>
<el-form-item label="数据来源">
<!-- <el-form-item label="数据来源">
<el-cascader v-model.trim="tableStore.table.params.cascader" filterable placeholder="请选择数据来源"
@change="sourceChange" :options="deviceTreeOptions" :show-all-levels="false"
:props="{ checkStrictly: true, value: 'id', label: 'name' }" clearable></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
</el-form-item>
</el-form-item> -->
<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.level" placeholder="请选择级别" clearable>
<el-option
@@ -77,9 +80,12 @@ const tableStore = new TableStore({
}
},
{ title: '发生时刻', field: 'startTime', align: 'center', sortable: true, minWidth: 180, },
{ title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120, },
{ title: '项目名称', field: 'projectName', align: 'center', minWidth: 120, },
{ title: '监测点名称', field: 'lineName', align: 'center', minWidth: 120,formatter: (row: any) => {
return row.cellValue ? row.cellValue : '/'
} },
{ title: '设备名称', field: 'equipmentName', align: 'center', minWidth: 120, },
{ title: '项目名称', field: 'projectName', align: 'center', minWidth: 120, },
{ title: '工程名称', field: 'engineeringName', align: 'center', minWidth: 120, },
{ title: '事件描述', field: 'showName', align: 'center', minWidth: 250, }
@@ -91,6 +97,7 @@ provide('tableStore', tableStore)
// "target": [],
// "type": "",
// "userId": ""
tableStore.table.params.searchValue = ''
tableStore.table.params.engineeringid = ''
tableStore.table.params.deviceTypeId = ''
tableStore.table.params.projectId = ''

View File

@@ -1,74 +1,46 @@
<template>
<div ref="refheader" v-show="!isWaveCharts" style="width: 100%">
<TableHeader datePicker showExport>
<TableHeader datePicker showExport @onResetForm="onResetForm">
<template v-slot:select>
<el-form-item label="数据来源">
<!-- <el-form-item label="数据来源">
<el-cascader placeholder="请选择数据来源" @change="sourceChange" filterable
v-model.trim="tableStore.table.params.cascader" :options="deviceTreeOptions"
:show-all-levels="false" :props="{ checkStrictly: true, value: 'id', label: 'name' }"
clearable></el-cascader>
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
</el-form-item>
<!-- <el-form-item label="级别">
<el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable>
<el-option v-for="item in rankOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item> -->
<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>
<el-button type="primary" plain @click="openFilterDialog">多条件筛选</el-button>
</el-form-item>
</template>
</TableHeader>
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params"
ref="multiConditionRef" @confirm="onFilterConfirm" />
<Table></Table>
</div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" />
<!-- <div style="height: 300px;"> -->
<!-- <div style="padding: 10px" v-if="!view" v-loading="loading">
<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" />
</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" 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> -->
</template>
<script setup lang="ts">
import { ref, onMounted, provide, nextTick } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import shushiboxi from '@/components/echarts/shushiboxi.vue'
import MultiCondition from './multiCondition.vue'
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
import rmsboxi from '@/components/echarts/rmsboxi.vue'
import { analyseWave, getFileByEventId } from '@/api/common'
import { mainHeight } from '@/utils/layout'
import { ElMessage } from 'element-plus'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData'
const props = defineProps(['deviceTree'])
const refheader = ref()
const waveFormAnalysisRef = ref()
const view = ref(true)
@@ -78,32 +50,18 @@ const showBoxi = ref(true)
const loading = ref(false)
const bxactiveName = ref('ssbx')
const boxoList: any = ref({})
const wp = ref({})
const value = ref(1)
const options = ref([
{
value: 1,
label: '一次值'
},
{
value: 2,
label: '二次值'
}
])
const rankOptions = ref([
{
value: '1',
label: '1级'
},
{
value: '2',
label: '2级'
},
{
value: '3',
label: '3级'
}
])
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList = [
{ label: '电压暂降', value: '电压暂降' },
{ label: '电压暂升', value: '电压暂升' },
{ label: '电压中断', value: '电压中断' }
]
const filterVisible = ref(false)
const multiConditionRef = ref<InstanceType<typeof MultiCondition>>()
const tableStore = new TableStore({
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
@@ -118,20 +76,38 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '暂降发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
{ title: '暂降(骤升)幅值(%)', minWidth: 160, field: 'evtParamVVaDepth', align: 'center', sortable: true },
{ title: '暂降幅值(%)', minWidth: 120, field: 'evtParamVVaDepth', align: 'center', sortable: true, },
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 110, align: 'center', sortable: true },
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 120, align: 'center' },
{ title: '工程名称', field: 'engineeringName', minWidth: 120, align: 'center' },
{ title: '项目名称', field: 'projectName', minWidth: 120, align: 'center' },
{ title: '设备名称', field: 'equipmentName', minWidth: 120, align: 'center' },
{ title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
{ title: '发生位置', field: 'evtParamPosition', minWidth: 150, align: 'center' },
{
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{ 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 ? '下游' : '未知'
}
},
{
title: '操作',
@@ -160,6 +136,7 @@ const tableStore = new TableStore({
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
@@ -212,6 +189,7 @@ const tableStore = new TableStore({
return !row.wavePath
},
click: row => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
@@ -223,6 +201,9 @@ const tableStore = new TableStore({
link.click() //执行下载
document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
},
@@ -233,25 +214,25 @@ const tableStore = new TableStore({
icon: 'el-icon-DataLine',
render: 'basicButton',
disabled: row => {
return row.wavePath
return row.wavePath
}
},
{
name: 'edit',
title: '波形补召',
type: 'primary',
icon: 'el-icon-Check',
render: 'basicButton',
disabled: row => {
return row.wavePath || row.showName === '未知'
},
click: row => {
getFileByEventId(row.id).then(res => {
ElMessage.success(res.message)
tableStore.index()
})
}
}
// {
// name: 'edit',
// title: '波形补召',
// type: 'primary',
// icon: 'el-icon-Check',
// render: 'basicButton',
// disabled: row => {
// return row.wavePath || row.showName === '未知'
// },
// click: row => {
// getFileByEventId(row.id).then(res => {
// ElMessage.success(res.message)
// tableStore.index()
// })
// }
// }
]
}
],
@@ -275,6 +256,7 @@ provide('tableStore', tableStore)
// "target": [],
// "type": "",
// "userId": ""
tableStore.table.params.searchValue = ''
tableStore.table.params.engineeringid = ''
tableStore.table.params.projectId = ''
tableStore.table.params.deviceTypeId = ''
@@ -286,8 +268,18 @@ tableStore.table.params.target = []
tableStore.table.params.userId = ''
tableStore.table.params.cascader = ''
tableStore.table.params.deviceTypeName = ''
Object.assign(tableStore.table.params, {
featureAmplitude: '',
evtParamTm: '',
showName: '',
fileFlag: ''
})
// tableStore.table.params.level=''
const openFilterDialog = () => {
filterVisible.value = true
}
const deviceTreeOptions: any = ref<any>(props.deviceTree)
deviceTreeOptions.value.map((item: any, index: any) => {
if (item?.children.length == 0) {
@@ -335,38 +327,23 @@ const sourceChange = (e: any) => {
// tableStore.table.params.deviceId = e[2] || ''
// }
}
const getboxin = async (row: any) => {
console.log('🚀 ~ getboxin ~ row:', row)
// boxoList.value = row
// await analyseWave(row.id).then(res => {
// if (res != undefined) {
// wp.value = res.data
// view.value = false
// view2.value = true
// }
// })
const onFilterConfirm = () => {
tableStore.onTableAction('search', {})
}
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
setTimeout(() => {
tableStore.table.height = mainHeight(180).height as any
}, 0)
// 重置
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(() => {
tableStore.index()
})

View File

@@ -3,7 +3,7 @@
@closed="handleClosed">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-one">
<el-form-item label="方案名称" prop="name">
<el-input v-model.trim="form.name" maxlength="64" show-word-limit placeholder="请输入方案名称" clearable />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入方案名称" clearable />
</el-form-item>
<el-form-item label="在线率阈值" prop="onlineRateLimit">
<el-input-number v-model="form.onlineRateLimit" :min="0" :max="100" :precision="0" style="width: 100%"

View File

@@ -1,18 +1,20 @@
<template>
<div class="default-main">
<el-tabs v-model.trim="activeName" type="border-card" class="demo-tabs">
<el-tab-pane label="暂态事件" name="4">
<Transient v-if="activeName == '4'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="稳态越限告警" name="3">
<Steady v-if="activeName == '3'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="设备告警" name="1">
<Device v-if="activeName == '1'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="前置告警" name="2">
<Front v-if="activeName == '2'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="稳态越限告警" name="3">
<Steady v-if="activeName == '3'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="暂态事件" name="4">
<Transient v-if="activeName == '4'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
<el-tab-pane label="异常事件" name="5">
<Abnormal v-if="activeName == '5'" :deviceTree="deviceTree" :key="key" />
</el-tab-pane>
@@ -32,7 +34,7 @@ defineOptions({
})
const deviceTree = ref([])
const activeName = ref('1')
const activeName = ref('4')
const key = ref(0)
getDeviceTree().then(res => {
// res.data.forEach((item: any) => {
@@ -50,7 +52,7 @@ getDeviceTree().then(res => {
// })
deviceTree.value = res.data
key.value += 1
activeName.value = '1'
activeName.value = '4'
})
onMounted(() => { })

View File

@@ -0,0 +1,217 @@
<template>
<el-dialog v-model="visible" title="多条件筛选" width="450px" append-to-body draggable
class="transient-filter-dialog">
<el-form label-width="auto" class="filter-form">
<el-form-item label="暂态幅值(%)">
<div class="range-inputs">
<el-input-number v-model="filterForm.featureAmplitudeMin" class="range-input-item" :min="0"
:max="200" :precision="3" :step="0.01" :controls="false" placeholder="0"
@change="onRangeMinChange($event, 'featureAmplitudeMax')" />
<span class="range-sep">&lt;  幅值  &lt;</span>
<el-input-number v-model="filterForm.featureAmplitudeMax" class="range-input-item" :min="0"
:max="200" :precision="3" :step="0.01" :controls="false" placeholder="200"
@change="onRangeMaxChange($event, filterForm.featureAmplitudeMin, 'featureAmplitudeMax', '暂态幅值')" />
</div>
</el-form-item>
<el-form-item label="暂态持续时间">
<div class="range-inputs">
<el-input-number v-model="filterForm.evtParamTmMin" class="range-input-item" :min="0"
:precision="3" :step="0.01" :controls="false" placeholder="X毫秒"
@change="onRangeMinChange($event, 'evtParamTmMax')" />
<span class="range-sep">&lt; 持续时间 &lt;</span>
<el-input-number v-model="filterForm.evtParamTmMax" class="range-input-item" :min="0"
:precision="3" :step="0.01" :controls="false" placeholder="X毫秒"
@change="onRangeMaxChange($event, filterForm.evtParamTmMin, 'evtParamTmMax', '暂态持续时间')" />
</div>
</el-form-item>
<el-form-item label="触发类型">
<el-select v-model="params.showName" clearable placeholder="请选择触发类型" style="width: 100%">
<el-option v-for="item in eventTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否存在波形">
<el-select v-model="params.fileFlag" clearable placeholder="请选择是否存在波形" style="width: 100%">
<el-option v-for="item in fileFlagOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div>
<el-button type="primary" @click="onConfirm">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
const getDefaultFilterParams = () => ({
featureAmplitude: '',
evtParamTm: '',
showName: '',
fileFlag: '' as string | number
})
const visible = defineModel<boolean>('visible', { default: false })
const props = defineProps<{
params: Record<string, any>
}>()
const emit = defineEmits<{
confirm: []
}>()
const eventTypeList = [
{ label: '电压暂降', value: '电压暂降' },
{ label: '电压暂升', value: '电压暂升' },
{ label: '电压中断', value: '电压中断' }
]
const fileFlagOptions = [
{ label: '存在', value: 1 },
{ label: '不存在', value: 0 }
]
type RangeMaxField = 'featureAmplitudeMax' | 'evtParamTmMax'
const filterForm = ref<{
featureAmplitudeMin?: number
featureAmplitudeMax?: number
evtParamTmMin?: number
evtParamTmMax?: number
}>({})
const parseRangeParam = (val: string): [number | undefined, number | undefined] => {
if (!val) return [undefined, undefined]
const [min = '', max = ''] = String(val).split(',')
const minNum = min !== '' ? Number(min) : undefined
const maxNum = max !== '' ? Number(max) : undefined
return [
minNum != null && !Number.isNaN(minNum) ? minNum : undefined,
maxNum != null && !Number.isNaN(maxNum) ? maxNum : undefined
]
}
const joinRangeParam = (min?: number, max?: number) => {
if (min == null && max == null) return ''
return `${min ?? ''},${max ?? ''}`
}
const onRangeMinChange = (min: number | undefined, maxField: RangeMaxField) => {
const max = filterForm.value[maxField]
if (min != null && max != null && max <= min) {
filterForm.value[maxField] = undefined
}
}
const onRangeMaxChange = (
max: number | undefined,
min: number | undefined,
maxField: RangeMaxField,
label: string
) => {
if (min != null && max != null && max <= min) {
ElMessage.warning(`${label}最大值必须大于最小值`)
filterForm.value[maxField] = undefined
}
}
const validateRange = (
min: number | undefined,
max: number | undefined,
label: string,
options?: { maxLimit?: number }
) => {
const hasMin = min != null && !Number.isNaN(min)
const hasMax = max != null && !Number.isNaN(max)
if (!hasMin && !hasMax) return true
if (hasMin !== hasMax) {
ElMessage.warning(`${label}请同时填写最小值和最大值`)
return false
}
if (min! <= 0 || max! <= 0) {
ElMessage.warning(`${label}必须大于0`)
return false
}
if (max! <= min!) {
ElMessage.warning(`${label}最大值必须大于最小值`)
return false
}
if (options?.maxLimit !== undefined && max! > options.maxLimit) {
ElMessage.warning(`${label}最大值不能大于${options.maxLimit}`)
return false
}
return true
}
const syncFromParams = () => {
const [faMin, faMax] = parseRangeParam(props.params.featureAmplitude)
const [tmMin, tmMax] = parseRangeParam(props.params.evtParamTm)
filterForm.value.featureAmplitudeMin = faMin
filterForm.value.featureAmplitudeMax = faMax
filterForm.value.evtParamTmMin = tmMin
filterForm.value.evtParamTmMax = tmMax
}
const reset = () => {
filterForm.value = {}
Object.assign(props.params, getDefaultFilterParams())
}
const onConfirm = () => {
const { featureAmplitudeMin, featureAmplitudeMax, evtParamTmMin, evtParamTmMax } = filterForm.value
if (!validateRange(featureAmplitudeMin, featureAmplitudeMax, '暂态幅值', { maxLimit: 200 })) {
return
}
if (!validateRange(evtParamTmMin, evtParamTmMax, '暂态持续时间')) {
return
}
props.params.featureAmplitude = joinRangeParam(featureAmplitudeMin, featureAmplitudeMax)
props.params.evtParamTm = joinRangeParam(evtParamTmMin, evtParamTmMax)
visible.value = false
emit('confirm')
}
watch(visible, val => {
if (val) syncFromParams()
})
defineExpose({ reset, syncFromParams })
</script>
<style scoped lang="scss">
.range-inputs {
display: flex;
align-items: center;
width: 100%;
gap: 8px;
.range-input-item {
width: 120px;
:deep(.el-input__wrapper) {
width: 100%;
}
}
.range-sep {
flex-shrink: 0;
color: var(--el-text-color-regular);
font-size: 14px;
white-space: nowrap;
}
}
</style>

View File

@@ -4,19 +4,19 @@
<el-scrollbar max-height="60vh">
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto" class="form-two">
<el-form-item label="指标名称" prop="indexName">
<el-input v-model.trim="form.indexName" placeholder="请输入指标名称" clearable maxlength="128" />
<el-input maxlength="32" show-word-limit v-model.trim="form.indexName" placeholder="请输入指标名称" clearable/>
</el-form-item>
<el-form-item label="指标code" prop="indexCode">
<el-input v-model.trim="form.indexCode" placeholder="请输入指标code" clearable maxlength="64" />
<el-form-item label="指标Code" prop="indexCode">
<el-input maxlength="32" show-word-limit v-model.trim="form.indexCode" placeholder="请输入指标Code" clearable />
</el-form-item>
<el-form-item label="表名" prop="influxdbTableName">
<el-input v-model.trim="form.influxdbTableName" placeholder="请输入表名" clearable />
<el-input maxlength="32" show-word-limit v-model.trim="form.influxdbTableName" placeholder="请输入表名" clearable />
</el-form-item>
<el-form-item label="列属性" prop="influxdbColumnName">
<el-input v-model.trim="form.influxdbColumnName" placeholder="实体类属性名" clearable maxlength="128" />
<el-input maxlength="32" show-word-limit v-model.trim="form.influxdbColumnName" placeholder="实体类属性名" clearable />
</el-form-item>
<el-form-item label="谐波次数">
<el-slider v-model.trim="form.harmSlider" range :max="50" style="width: 90%" />
@@ -40,7 +40,7 @@
<el-radio :value="1">参与</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Ct变比参与" prop="ctAttendFlag">
<el-form-item label="CT变比参与" prop="ctAttendFlag">
<el-radio-group v-model="form.ctAttendFlag">
<el-radio :value="0">不参与</el-radio>
<el-radio :value="1">参与</el-radio>
@@ -60,10 +60,10 @@
</el-select>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model.trim="form.unit" placeholder="请输入单位" clearable maxlength="32" />
<el-input maxlength="32" show-word-limit v-model.trim="form.unit" placeholder="请输入单位" clearable />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :precision="0" style="width: 100%" />
<el-input-number v-model="form.sort" :min="0" style="width: 100%;" :precision="0" />
</el-form-item>
<!-- <el-form-item label="状态" prop="state">
<el-select v-model="form.state" placeholder="请选择" style="width: 100%">
@@ -72,12 +72,12 @@
</el-select>
</el-form-item> -->
<el-form-item label="条件描述" prop="otherAlgorithm" class="form-item-full">
<el-input v-model.trim="form.otherAlgorithm" type="textarea" :rows="2" placeholder="无具体范围时的判断条件描述"
maxlength="500" show-word-limit />
<el-input show-word-limit v-model.trim="form.otherAlgorithm" type="textarea" :rows="2" placeholder="无具体范围时的判断条件描述"
maxlength="300" />
</el-form-item>
<el-form-item label="备注" prop="remark" class="form-item-full">
<el-input v-model.trim="form.remark" type="textarea" :rows="2" placeholder="备注" maxlength="500"
show-word-limit />
<el-input show-word-limit v-model.trim="form.remark" type="textarea" :rows="2" placeholder="备注" maxlength="300"
/>
</el-form-item>
</el-form>
</el-scrollbar>
@@ -172,7 +172,7 @@ const validateMinMax = (_rule: any, _value: any, callback: (e?: Error) => void)
}
const rules: any = {
indexCode: [{ required: true, message: '请输入指标code', trigger: 'blur' }],
indexCode: [{ required: true, message: '请输入指标Code', trigger: 'blur' }],
indexName: [{ required: true, message: '请输入指标名称', trigger: 'blur' }],
phaseList: [{ required: true, message: '请输入指标名称', trigger: 'change' }],

View File

@@ -15,8 +15,8 @@
</el-select>
</el-form-item>
<el-form-item label="表名">
<el-input v-model.trim="tableStore.table.params.tableName" placeholder="请输入表名" clearable
maxlength="64" />
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.tableName" placeholder="请输入表名" clearable
/>
</el-form-item>
</template>
<template #operation>
@@ -64,7 +64,7 @@ const tableStore: any = new TableStore({
}
},
{ field: 'indexName', title: '指标名称', minWidth: 200 },
{ field: 'indexCode', title: '指标code', minWidth: 150 },
{ field: 'indexCode', title: '指标Code', minWidth: 150 },
{ field: 'influxdbTableName', title: '表名', minWidth: 150 },
{ field: 'influxdbColumnName', title: '列属性', minWidth: 150 },
@@ -91,7 +91,7 @@ const tableStore: any = new TableStore({
},
{
field: 'ctAttendFlag',
title: 'Ct变比参与',
title: 'CT变比参与',
width: 100,
formatter: (row: any) => yesNo(row.cellValue)
},

View File

@@ -1,440 +1,489 @@
<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
tableStore.index()
})
.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>
<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> -->
<el-tabs type="border-card">
<el-tab-pane label="基础指标" class="pd10">
<el-checkbox-group v-model="indicator"
class="indicator-checkbox-grid indicator-checkbox-grid--wide">
<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>
</el-tab-pane>
</el-tabs>
<el-tabs type="border-card" class="mt10">
<el-tab-pane label="谐波电压" class="pd10">
<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>
</el-tab-pane>
</el-tabs>
<!-- <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> -->
<el-tabs type="border-card" class="mt10">
<el-tab-pane label="谐波电流" class="pd10">
<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>
</el-tab-pane>
</el-tabs>
<el-tabs type="border-card" class="mt10">
<el-tab-pane label="间谐波电压" class="pd10">
<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>
</el-tab-pane>
</el-tabs>
<!-- <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 maxlength="32" show-word-limit v-model.trim="formData.name" placeholder="请输入方案名称" clearable />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-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
tableStore.index()
})
.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>

View File

@@ -3,10 +3,11 @@
<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">
<TableHeader :showSearch="false" ref="tableHeaderRef" @selectChange="selectChange"
>
<template v-slot:select>
<el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
<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
@@ -128,7 +129,7 @@ const deviceTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
const nodeClick = async (e: anyObj) => {
if ((e.level == 2 || e.level == 3) && flag.value) {
if ((e.level == 2 || e.level == 3)) {
formInline.devId = e.id
loading.value = true
if (zblist.value.length === 0) {

View File

@@ -5,18 +5,18 @@
<div class="analyze-dvr-right" v-if="tableStore.table.params.deviceId">
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="事件类型">
<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 label="位置">
<!-- <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"
:value="item.value" />
</el-select>
</el-form-item>
</el-form-item> -->
</template>
</TableHeader>
<Table v-if="view" ref="tableRef"></Table>
@@ -69,7 +69,7 @@ import Table from '@/components/table/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 { ElMessage } from 'element-plus'
defineOptions({
@@ -109,7 +109,9 @@ const locationList = ref([
}
])
const waveFormAnalysisRef = ref()
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const tableStore = new TableStore({
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
method: 'POST',
@@ -122,14 +124,14 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '暂降发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
{ title: '发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
{
title: '暂降(骤升)幅值(%)',
title: '暂降幅值(%)',
field: 'evtParamVVaDepth',
minWidth: 160,
minWidth: 120,
formatter: (row: any) => {
let a = row.cellValue.split('%')[0] - 0
return a ? a.toFixed(2) : '/'
return a ? a.toFixed(2) : '-'
},
sortable: true
},
@@ -140,22 +142,31 @@ const tableStore = new TableStore({
minWidth: 110,
formatter: (row: any) => {
const val = row.cellValue
if (val === null || val === undefined || val === '' || val === '-') return '/'
if (val === null || val === undefined || val === '' ) return '-'
const num = Number(val)
if (Number.isNaN(num)) return '/'
if (Number.isNaN(num)) return '-'
return Math.floor(num * 10000) / 100
}
},
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
{ title: '触发类型', field: 'showName', minWidth: 150 },
{
title: '发生位置', field: 'evtParamPosition', minWidth: 150,
formatter: (row: any) => {
const val = row.cellValue
if (val === null || val === undefined || val === '' || val === '-') return '/'
return val
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
{
title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => {
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
}
},
@@ -232,6 +243,7 @@ const tableStore = new TableStore({
return !row.wavePath
},
click: row => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
@@ -243,6 +255,9 @@ const tableStore = new TableStore({
link.click() //执行下载
document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
}
@@ -267,7 +282,7 @@ const deviceTypeChange = (val: any, obj: any) => {
}
const nodeClick = async (e: anyObj) => {
// console.log("🚀 ~ nodeClick ~ e:", e)
if ((e.level == 2 || e.level == 3) && flag.value) {
if ((e.level == 2 || e.level == 3) ) {
loading.value = false
tableStore.table.params.deviceId = e.id
nextTick(() => {

View File

@@ -34,10 +34,11 @@
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">导出</el-button>
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div class="box" v-loading="tableStore.table.loading" v-if="lineFlag">
<div id="luckysheet" class="report-zl-sheet" v-if="tableStore.table.data.length > 0"></div>
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
</div>
<el-empty class="report-zl-sheet" v-else description="治理监测点暂不支持出具报表" />
</div>
</div>
</template>
@@ -65,7 +66,7 @@ const reportForm: any = ref('')
const datePickerRef = ref()
const templatePolicy: any = ref([])
const name = ref('')
const lineFlag = ref(true)
const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getSensitiveUserReport',
method: 'POST',
@@ -130,7 +131,12 @@ const changetype = (val: any) => {
}
const handleNodeClick = (data: any, node: any) => {
if (data.name.includes('治理监测点')) {
lineFlag.value = false
return tableStore.table.loading = false
}
if (isLineTreeLeaf(data) || data?.level == 3) {
lineFlag.value = true
dotList.value = data
setTimeout(() => {
tableStore.index()
@@ -186,7 +192,7 @@ const exportEvent = () => {
}
.report-zl-sheet {
height: 100%;
height: 90%;
}
</style>
<style></style>

View File

@@ -73,7 +73,7 @@
<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 clearable v-model="formData.engineeringParam.name" placeholder="请输入工程名称"
<el-input maxlength="32" show-word-limit clearable v-model="formData.engineeringParam.name" placeholder="请输入工程名称"
:disabled="!((nodeLevel == 1 && pageStatus == 3) || (nodeLevel == 0 && pageStatus == 2))
"></el-input>
</el-form-item>
@@ -100,14 +100,14 @@
<el-form-item class="form-item" label="描述:" prop="engineeringParam.description"
v-if="nodeLevel > 0 || pageStatus == 2">
<el-input clearable v-model="formData.engineeringParam.description" placeholder="请输入描述"
<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"
v-if="nodeLevel > 0 || pageStatus == 2"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input clearable v-model.trim.number="formData.engineeringParam.sort"
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="formData.engineeringParam.sort"
placeholder="请输入排序" :disabled="!((nodeLevel == 1 && pageStatus == 3) || (nodeLevel == 0 && pageStatus == 2))
"></el-input>
</el-form-item>
@@ -124,7 +124,7 @@
:prop="'projectInfoList[' + index + '].name'" :rules="[
{ required: true, message: '请输入项目名称', trigger: 'blur' }
]">
<el-input clearable v-model="item.name" placeholder="请输入项目名称" :disabled="!(
<el-input maxlength="32" show-word-limit clearable v-model="item.name" placeholder="请输入项目名称" :disabled="!(
(nodeLevel == 2 && pageStatus == 3) ||
((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) &&
pageStatus == 2)
@@ -134,7 +134,7 @@
<el-form-item class="form-item" label="地址:"
:prop="'projectInfoList[' + index + '].area'"
:rules="[{ required: true, message: '请输入地址', trigger: 'blur' }]">
<el-input clearable v-model="item.area" placeholder="请输入地址" :disabled="!(
<el-input maxlength="32" show-word-limit clearable v-model="item.area" placeholder="请输入地址" :disabled="!(
(nodeLevel == 2 && pageStatus == 3) ||
((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) &&
pageStatus == 2)
@@ -144,7 +144,7 @@
<el-form-item class="form-item" label="描述:"
:prop="'projectInfoList[' + index + '].description'"
:rules="[{ required: true, message: '请输入描述', trigger: 'blur' }]">
<el-input clearable v-model="item.description" placeholder="请输入描述"
<el-input maxlength="32" show-word-limit clearable v-model="item.description" placeholder="请输入描述"
:disabled="!(
(nodeLevel == 2 && pageStatus == 3) ||
((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) &&
@@ -178,7 +178,7 @@
<el-form-item class="form-item" label="排序:"
:prop="'projectInfoList[' + index + '].sort'"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input clearable v-model.trim.number="item.sort" placeholder="请输入排序"
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="item.sort" placeholder="请输入排序"
:disabled="!(
(nodeLevel == 2 && pageStatus == 3) ||
((nodeLevel == 1 || (nodeLevel == 0 && pageStatus == 2)) &&
@@ -206,7 +206,7 @@
:prop="'deviceInfoList[' + bIndex + '].name'" :rules="[
{ required: true, message: '请输入设备名称', trigger: 'blur' }
]">
<el-input clearable v-model="busItem.name" placeholder="请输入设备名称"
<el-input maxlength="32" show-word-limit clearable v-model="busItem.name" placeholder="请输入设备名称"
:disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
((nodeLevel == 2 || (nodeLevel == 1 && pageStatus == 2)) &&
@@ -225,9 +225,10 @@
pageStatus == 2)
)
" @change="busItem.devModel = ''">
<el-option v-for="item in formDevTypeOptions" :key="item.value"
:label="item.label || item.name"
:value="item.value || item.id"></el-option>
<el-option v-for="item in formDevTypeOptions" :key="item.id"
:label=" item.name"
:value=" item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item id="id200" class="form-item" label="设备型号:"
@@ -258,7 +259,7 @@
pageStatus == 2)
)
">
<el-option label="CLD" value="CLD"></el-option>
<el-option label="1056协议" value="CLD"></el-option>
<!-- <el-option label="MQTT" value="MQTT"></el-option> -->
</el-select>
</el-form-item>
@@ -289,7 +290,7 @@
<el-form-item class="form-item" label="合同号:"
:prop="'deviceInfoList[' + bIndex + '].cntractNo'">
<el-input clearable v-model="busItem.cntractNo" placeholder="请输入合同号"
<el-input maxlength="32" show-word-limit clearable v-model="busItem.cntractNo" placeholder="请输入合同号"
:disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
((nodeLevel == 2 || (nodeLevel == 1 && pageStatus == 2)) &&
@@ -310,7 +311,7 @@
</el-form-item>
<el-form-item v-if="busItem.devAccessMethod == 'CLD'" class="form-item"
label="进程号:" :prop="'deviceInfoList[' + bIndex + '].nodeProcess'">
<el-input clearable v-model="busItem.nodeProcess" placeholder="自动分配"
<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"
@@ -370,7 +371,7 @@
<el-form-item class="form-item" label="排序:"
:prop="'deviceInfoList[' + bIndex + '].sort'"
:rules="[{ required: true, message: '请输入排序', trigger: 'blur' }]">
<el-input clearable v-model.trim.number="busItem.sort"
<el-input maxlength="32" show-word-limit clearable v-model.trim.number="busItem.sort"
placeholder="请输入排序" :disabled="!(
(nodeLevel == 3 && pageStatus == 3) ||
((nodeLevel == 2 || (nodeLevel == 1 && pageStatus == 2)) &&
@@ -400,7 +401,7 @@
message: '请输入监测点名称',
trigger: 'blur'
}">
<el-input clearable v-model="lineItem.name" placeholder="请输入监测点名称"
<el-input maxlength="32" show-word-limit clearable v-model="lineItem.name" placeholder="请输入监测点名称"
:disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
((nodeLevel == 3 || (nodeLevel == 2 && pageStatus == 2)) &&
@@ -465,7 +466,7 @@
:prop="'lineInfoList[' + lIndex + '].ptRatio'"
:rules="{ required: true, message: '请输入pt', trigger: 'blur' }">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ptRatio" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
@@ -481,7 +482,7 @@
">
:
</span>
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.pt2Ratio" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
@@ -496,7 +497,7 @@
:prop="'lineInfoList[' + lIndex + '].ctRatio'"
:rules="{ required: true, message: '请输入ct', trigger: 'blur' }">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ctRatio" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
@@ -512,7 +513,7 @@
">
:
</span>
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ct2Ratio" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
@@ -544,7 +545,7 @@
<el-form-item class="form-item" label="基准容量(MVA):"
:prop="'lineInfoList[' + lIndex + '].basicCapacity'"
:rules="{ required: true, message: '请输入基准容量', trigger: 'blur' }">
<el-input clearable-number :controls="false" :min="0"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
style="width: 100%" v-model="lineItem.basicCapacity" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
((nodeLevel == 3 || (nodeLevel == 2 && pageStatus == 2)) &&
@@ -555,7 +556,7 @@
<el-form-item class="form-item" label="短路容量(MVA):"
:prop="'lineInfoList[' + lIndex + '].shortCircuitCapacity'"
:rules="{ required: true, message: '请输入短路容量', trigger: 'blur' }">
<el-input clearable-number :controls="false" :min="0"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
style="width: 100%" v-model="lineItem.shortCircuitCapacity"
:disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
@@ -567,7 +568,7 @@
<el-form-item class="form-item" label="设备容量(MVA):"
:prop="'lineInfoList[' + lIndex + '].devCapacity'"
:rules="{ required: true, message: '请输入设备容量', trigger: 'blur' }">
<el-input clearable-number :controls="false" :min="0"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
style="width: 100%" v-model="lineItem.devCapacity" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
((nodeLevel == 3 || (nodeLevel == 2 && pageStatus == 2)) &&
@@ -578,7 +579,7 @@
<el-form-item class="form-item" label="协议容量(MVA):"
:prop="'lineInfoList[' + lIndex + '].protocolCapacity'"
:rules="{ required: true, message: '请输入协议容量', trigger: 'blur' }">
<el-input clearable-number :controls="false" :min="0"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="0"
style="width: 100%" v-model="lineItem.protocolCapacity" :disabled="!(
(nodeLevel == 4 && pageStatus == 3) ||
((nodeLevel == 3 || (nodeLevel == 2 && pageStatus == 2)) &&
@@ -699,7 +700,7 @@
</div>
<div class="flex mt10" v-else>
<el-form-item class="form-item" label="监测点名称:">
<el-input clearable v-model="lineItem.name" placeholder="请输入监测点名称"
<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="线路号:">
@@ -725,7 +726,7 @@
</el-form-item>
<el-form-item class="form-item" label="PT变比:">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ptRatio" :disabled="true"></el-input>
<span style="
@@ -735,14 +736,14 @@
">
:
</span>
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.pt2Ratio" :disabled="true"></el-input>
</div>
</el-form-item>
<el-form-item class="form-item" label="CT变比:">
<div style="width: 100%; display: flex; justify-content: space-between">
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ctRatio" :disabled="true"></el-input>
<span style="
@@ -752,7 +753,7 @@
">
:
</span>
<el-input clearable-number :controls="false" :min="1"
<el-input maxlength="32" show-word-limit clearable-number :controls="false" :min="1"
style="width: 48%" oninput="value=value.replace(/[^\d]/g,'')"
v-model.number="lineItem.ct2Ratio" :disabled="true"></el-input>
</div>
@@ -904,7 +905,7 @@ const currentGdName: any = ref('')
const affiliatiedFrontArr: any = ref([])
const voltageLevelArr = dictData.getBasicData('Dev_Voltage_Stand')
const devCLD: any = ref([])
const devTypeOptions: any = []
const devTypeOptions: any =ref([])
const devTypeOptions2: any = ref([])
const treeClickCount = ref(0)
const areaTree: any = tree
@@ -1059,7 +1060,7 @@ const cityOptions = computed(() => {
})
const formDevTypeOptions = computed(() => {
return devTypeOptions.value //[devTypeOptions2.value]
return devTypeOptions.value||[] //[devTypeOptions2.value]
})
// 电压等级选项,用于显示带单位的名称
@@ -2813,8 +2814,9 @@ const area = async () => {
})
})
return queryCsDictTree(res.data.id).then(res => {
queryCsDictTree(res.data.id).then(res => {
devTypeOptions.value = res.data
console.log("🚀 ~ area ~ devTypeOptions.value:", devTypeOptions.value)
})
})
})
@@ -2857,7 +2859,9 @@ area()
.instruction {
animation: rotating 2s linear infinite;
}
:deep(.cn-tree){
padding: 0px 10px 0 0;
}
@keyframes rotating {
0% {
transform: rotate(0deg);

View File

@@ -1,10 +1,6 @@
<template>
<div
class="default-main device-control"
:style="{ height: pageHeight.height }"
v-loading="loading"
style="position: relative"
>
<div class="default-main device-control" :style="{ height: pageHeight.height }" v-loading="loading"
style="position: relative">
<!-- @init="nodeClick" -->
<PointTree @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
<div class="device-control-right" v-if="deviceData">
@@ -19,12 +15,8 @@
<el-button v-if="deviceType == '1'" type="primary" icon="el-icon-Monitor" @click="handleaddDevice">
在线补召
</el-button> -->
<el-button
v-if="deviceType == '1'"
type="primary"
icon="el-icon-Tickets"
@click="handleAnalysisList"
>
<el-button v-if="deviceType == '1'" type="primary" icon="el-icon-Tickets"
@click="handleAnalysisList">
补召
</el-button>
</template>
@@ -38,10 +30,10 @@
devData.conType == 0
? '星型接线'
: devData.conType == 1
? '角型接线'
: devData.conType == 2
? 'V型接线'
: '/'
? '角型接线'
: devData.conType == 2
? 'V型接线'
: '/'
}}
</el-descriptions-item>
@@ -50,18 +42,13 @@
</el-descriptions-item> -->
<el-descriptions-item label="PT变比" width="160">
{{
devData.ptRatio == null
? '/'
: devData.ptRatio + (devData.pt2Ratio == null ? '' : ':' + devData.pt2Ratio)
}}
{{ devData.ptRatio || 1 }} :
{{ devData.pt2Ratio || 1 }}
</el-descriptions-item>
<el-descriptions-item label="CT变比" width="160">
{{
devData.ctRatio == null
? '/'
: devData.ctRatio + (devData.ct2Ratio == null ? '' : ':' + devData.ct2Ratio)
}}
{{ devData.ctRatio || 1 }} :
{{ devData.ct2Ratio || 1 }}
</el-descriptions-item>
<!-- <el-descriptions-item label="名称">
@@ -85,14 +72,8 @@
</el-descriptions-item> -->
</el-descriptions>
<el-tabs v-model.trim="dataSet" type="border-card" class="device-control-box-card" @tab-click="handleClick">
<el-tab-pane
lazy
:label="item.name"
:name="item.id"
v-for="(item, index) in deviceData.dataSetList"
:key="index"
:disabled="tableLoading"
>
<el-tab-pane lazy :label="item.name" :name="item.id" v-for="(item, index) in deviceData.dataSetList"
:key="index" :disabled="tableLoading">
<template #label>
<span class="custom-tabs-label">
<!-- <el-icon>
@@ -126,36 +107,25 @@
</span>
</template>
</el-tab-pane>
<TableHeader
:showSearch="false"
v-if="
(dataSet.indexOf('_trenddata') == -1 &&
<TableHeader :showSearch="false" v-if="
(dataSet.indexOf('_trenddata') == -1 &&
dataSet.indexOf('_kilowattHour') == -1 &&
dataSet.indexOf('_realtimedata') == -1 &&
dataSet.indexOf('_event') == -1) ||
realTimeFlag
"
>
dataSet.indexOf('_realtimedata') == -1 &&
dataSet.indexOf('_event') == -1) ||
realTimeFlag
">
<template #select>
<el-form-item
label="日期"
v-show="
dataSet.includes('_items') ||
dataSet.indexOf('_history') != -1 ||
dataSet.indexOf('_moduleData') != -1 ||
dataSet.indexOf('_devRunTrend') != -1
"
>
<DatePicker ref="datePickerRef"></DatePicker>
<el-form-item label="日期" v-show="dataSet.includes('_items') ||
dataSet.indexOf('_history') != -1 ||
dataSet.indexOf('_moduleData') != -1 ||
dataSet.indexOf('_devRunTrend') != -1
">
<DatePicker ref="datePickerRef" :timeKeyList="['3', '4', '5']"></DatePicker>
</el-form-item>
<el-form-item label="数据类型" v-if="!dataSet.includes('_')">
<el-select v-model.trim="formInline.targetType" @change="handleTargetTypeChange">
<el-option
v-for="item in queryList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
<el-option v-for="item in queryList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
@@ -165,16 +135,15 @@
<el-option value="Primary" label="一次值"></el-option>
<el-option value="Secondary" label="二次值"></el-option>
</el-select> -->
<el-radio-group
v-model.trim="formInline.dataLevel"
v-if="
!dataSet.includes('_devRunTrend') &&
!dataSet.includes('_moduleData') &&
TrendList?.lineType == 1
"
:disabled="TrendList?.lineType != 1"
@change="handleClick"
>
<!-- _realtimedata
-->
<el-radio-group v-model.trim="formInline.dataLevel" v-if="
!dataSet.includes('_devRunTrend') &&
!dataSet.includes('_moduleData') &&
TrendList?.lineType == 1
" :disabled="TrendList?.lineType != 1"
@change="dataSet.includes('_realtimedata') ? realtimeChange() : handleClick()">
<el-radio-button label="一次值" value="Primary" />
<el-radio-button label="二次值" value="Secondary" />
</el-radio-group>
@@ -182,102 +151,56 @@
<el-form-item label="谐波次数" v-show="oddAndEvenFlag && !dataSet.includes('_')">
<el-select v-model.trim="oddAndEven" style="min-width: 120px !important">
<el-option
v-for="item in oddAndEvenList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-option v-for="item in oddAndEvenList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="数据来源" v-if="dataSet.includes('_items')">
<el-select v-model.trim="formInline.dataSource" placeholder="请选择数据来源" clearable>
<el-option
v-for="item in dataSourceList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
<el-option v-for="item in dataSourceList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="关键字筛选" v-if="!dataSet.includes('_')">
<el-input
maxlength="32"
show-word-limit
v-model.trim="searchValue"
autocomplete="off"
clearable
@input="handleSearch"
placeholder="请输入关键字筛选"
style="width: 180px !important"
></el-input>
<el-input maxlength="32" show-word-limit v-model.trim="searchValue" autocomplete="off"
clearable @input="handleSearch" placeholder="请输入关键字筛选"
style="width: 180px !important"></el-input>
</el-form-item>
</template>
<template #operation>
<el-button
type="primary"
<el-button type="primary"
:icon="dataSet.indexOf('_realtimedata') != -1 ? 'el-icon-Refresh' : 'el-icon-Search'"
@click="handleClick"
>
@click="handleClick" :loading="tableLoading">
{{ dataSet.indexOf('_realtimedata') != -1 ? '刷新' : '查询' }}
</el-button>
<el-button
type="primary"
v-if="dataSet.indexOf('_moduleData') != -1 && moduleFlag"
:icon="Refresh"
@click="refreshTheState"
>
<el-button type="primary" v-if="dataSet.indexOf('_moduleData') != -1 && moduleFlag"
:icon="Refresh" @click="refreshTheState">
刷新状态
</el-button>
<el-button
type="primary"
:disabled="tableLoading"
v-if="showButton"
:icon="DataLine"
@click="handleTrend"
>
<el-button type="primary" :disabled="tableLoading" v-if="showButton" :icon="DataLine"
@click="handleTrend">
谐波频谱
</el-button>
<el-button
type="primary"
v-if="showButton"
:icon="TrendCharts"
@click="handleHarmonicSpectrum"
:disabled="tableLoading"
>
<el-button type="primary" v-if="showButton" :icon="TrendCharts" @click="handleHarmonicSpectrum"
:disabled="tableLoading">
实时趋势
</el-button>
<el-button
type="primary"
v-if="showButton"
:icon="Download"
@click="downloadTxt"
:disabled="tableLoading"
>
<el-button type="primary" v-if="showButton" :icon="Download" @click="downloadTxt"
:disabled="tableLoading">
下载数据
</el-button>
</template>
</TableHeader>
<div
class="data_time"
:style="{
height: realTimeFlag ? '30px' : '40px',
alignItems: realTimeFlag ? 'flex-end' : 'center'
}"
v-if="dataSet.includes('_realtimedata') && mqttMessage && mqttMessage.dataTime && sonTab != 2"
>
<p class="mb5">
<span v-if="trendDataTime && !realTimeFlag">数据时间:{{ trendDataTime }}</span>
<span v-if="realTimeFlag">数据时间:{{ mqttMessage.dataTime }}</span>
<div class="data_time" :style="{
alignItems: realTimeFlag ? 'flex-end' : 'center'
}" v-if="dataSet.includes('_realtimedata') && sonTab != 2 && trendDataTime && !realTimeFlag">
<p class="mb10 mt10">
<span>数据时间:{{ trendDataTime || '-' }}</span>
</p>
<el-button
v-if="!realTimeFlag && dataSet.includes('_realtimedata')"
style="float: right !important"
:icon="Back"
@click="handleReturn"
:loading="tableLoading"
>
<el-button v-if="!realTimeFlag && dataSet.includes('_realtimedata')" style="float: right !important"
:icon="Back" @click="handleReturn" :loading="tableLoading">
返回
</el-button>
</div>
@@ -293,52 +216,32 @@
></div> -->
<!-- v-loading="tableLoading" -->
<div :style="{ height: tableHeight }" v-loading="tableLoading" v-if="!dataSet.includes('_')">
<div
style="overflow: auto"
:style="{ height: tableHeight }"
v-if="
dataSet.indexOf('_trenddata') == -1 &&
dataSet.indexOf('_kilowattHour') == -1 &&
dataSet.indexOf('_realtimedata') == -1 &&
dataSet.indexOf('_event') == -1 &&
tableData.length != 0
"
>
<div
class="mb5 mt5"
v-if="dataSet.indexOf('_history') == -1"
style="font-weight: 800; font-size: 16px; text-align: center"
>
统计时间:{{ tableData[0]?.time || '' }}
<div style="overflow: auto" :style="{ height: tableHeight }" v-if="
dataSet.indexOf('_trenddata') == -1 &&
dataSet.indexOf('_kilowattHour') == -1 &&
dataSet.indexOf('_realtimedata') == -1 &&
dataSet.indexOf('_event') == -1 &&
tableData.length != 0
">
<div class="mb10 mt10" v-if="dataSet.indexOf('_history') == -1"
style="font-weight: 700; font-size: 13px; text-align: center">
最新数据时标:{{ tableData[0]?.time || '-' }}
</div>
<nearRealTimeData
ref="nearRealTimeDataRef"
v-if="dataSet.indexOf('_history') == -1"
:style="{ height: tableHeightBox, overflow: 'auto' }"
/>
<nearRealTimeData ref="nearRealTimeDataRef" v-if="dataSet.indexOf('_history') == -1"
:style="{ height: tableHeightBox, overflow: 'auto' }" />
<!-- 循环渲染的card 最新数据/历史数据显示 -->
<div
class="content"
<div class="content"
v-if="tableData.length != 0 && !tableLoading && dataSet.indexOf('_history') != -1"
:style="{ height: tableHeightBox }"
>
<el-card
class="box-card"
:class="dataSet.indexOf('_history') == -1 ? 'box-card-new' : ''"
v-for="(item, index) in tableData"
:key="index"
>
:style="{ height: tableHeightBox }">
<el-card class="box-card" :class="dataSet.indexOf('_history') == -1 ? 'box-card-new' : ''"
v-for="(item, index) in tableData" :key="index">
<template #header>
<div class="clearfix">
<span style="flex: 1">{{ item.name }}</span>
<Icon
name="el-icon-TrendCharts"
class="ml10"
@click="getDeviceDataTrend(item)"
style="font-size: 26px; cursor: pointer; color: #fff"
></Icon>
<Icon name="el-icon-TrendCharts" class="ml10" @click="getDeviceDataTrend(item)"
style="font-size: 26px; cursor: pointer; color: #fff"></Icon>
</div>
</template>
@@ -352,49 +255,37 @@
</div>
<div v-else-if="item.children.length" class="box-card-div">
<div style="display: flex; align-items: center">
<el-tag
effect="dark"
type="danger"
style="width: 40px; text-align: center"
class="mr10"
>
<el-tag effect="dark" type="danger" style="width: 40px; text-align: center"
class="mr10">
MAX
</el-tag>
{{
item.children[0].maxValue === 3.1415956 ||
typeof item.children[0].maxValue != 'number'
typeof item.children[0].maxValue != 'number'
? '暂无数据'
: item.children[0].maxValue
}}
</div>
<div style="display: flex; align-items: center">
<el-tag
effect="dark"
type="success"
style="width: 40px; text-align: center"
class="mr10"
>
<el-tag effect="dark" type="success" style="width: 40px; text-align: center"
class="mr10">
AVG
</el-tag>
{{
item.children[0].avgValue === 3.1415956 ||
typeof item.children[0].avgValue != 'number'
typeof item.children[0].avgValue != 'number'
? '暂无数据'
: item.children[0].avgValue
}}
</div>
<div style="display: flex; align-items: center">
<el-tag
effect="dark"
type="warning"
style="width: 40px; text-align: center"
class="mr10"
>
<el-tag effect="dark" type="warning" style="width: 40px; text-align: center"
class="mr10">
MIN
</el-tag>
{{
item.children[0].minValue === 3.1415956 ||
typeof item.children[0].minValue != 'number'
typeof item.children[0].minValue != 'number'
? '暂无数据'
: item.children[0].minValue
}}
@@ -403,82 +294,10 @@
</el-card>
<el-empty description="暂无数据" v-if="tableData.length === 0"></el-empty>
</div>
<!-- <div class="content" v-if="tableData.length != 0 && !tableLoading"
:style="{ height: tableHeightBox }">
<el-card class="box-card" :class="dataSet.indexOf('_history') == -1 ? 'box-card-new' : ''"
v-for="(item, index) in tableData" :key="index">
<template #header>
<div class="clearfix">
<span style="flex: 1">{{ item.name }}</span>
<Icon name="el-icon-TrendCharts" class="ml10" @click="getDeviceDataTrend(item)"
style="font-size: 26px; cursor: pointer; color: #fff"></Icon>
</div>
</template>
<div class="box-card-content" v-if="dataSet.indexOf('_history') == -1">
<div class="box-card-div">
<div v-for="(child, childIndex) in item.children" :key="childIndex">
{{ child.anotherName }}:
{{ child.dataValue === 3.1415926 ? '暂无数据' : child.dataValue }}
</div>
</div>
</div>
<div v-else-if="item.children.length" class="box-card-div">
<div style="display: flex; align-items: center">
<el-tag effect="dark" type="danger" style="width: 40px; text-align: center"
class="mr10">
MAX
</el-tag>
{{
item.children[0].maxValue === 3.1415956 ||
typeof item.children[0].maxValue != 'number'
? '暂无数据'
: item.children[0].maxValue
}}
</div>
<div style="display: flex; align-items: center">
<el-tag effect="dark" type="success" style="width: 40px; text-align: center"
class="mr10">
AVG
</el-tag>
{{
item.children[0].avgValue === 3.1415956 ||
typeof item.children[0].avgValue != 'number'
? '暂无数据'
: item.children[0].avgValue
}}
</div>
<div style="display: flex; align-items: center">
<el-tag effect="dark" type="warning" style="width: 40px; text-align: center"
class="mr10">
MIN
</el-tag>
{{
item.children[0].minValue === 3.1415956 ||
typeof item.children[0].minValue != 'number'
? '暂无数据'
: item.children[0].minValue
}}
</div>
</div>
</el-card>
<el-empty description="暂无数据" v-if="tableData.length === 0"></el-empty>
</div> -->
</div>
</div>
<!-- <el-pagination v-if="
tableData.length &&
dataSet.indexOf('_trenddata') == -1 &&
dataSet.indexOf('_realtimedata') == -1 &&
dataSet.indexOf('_event') == -1
" background class="mr2 mt10" style="float: right" @size-change="handleSizeChange"
@current-change="pageChange" :current-page="formInline.pageNum" :page-sizes="[20, 30, 40, 50, 100]"
:page-size="formInline.pageSize" layout="total, sizes, prev, pager, next, jumper"
:total="formInline.total"></el-pagination> -->
<!-- 趋势数据 -->
<div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_trenddata') != -1">
@@ -489,68 +308,47 @@
<electroplating ref="electroplatingRef" :TrendList="TrendList"></electroplating>
</div>
<!-- 实时数据 -->
<div
:style="`height: calc(100vh - (${sonTab == 1 ? '378px' : sonTab == 2 ? '340px' : '425px'}))`"
v-if="dataSet.indexOf('_realtimedata') != -1"
v-loading="tableLoading"
>
<div :style="`height: calc(100vh - (${sonTab == 1 ? '346px' : sonTab == 2 ? '308px' : '393px'}))`"
v-if="dataSet.indexOf('_realtimedata') != -1" v-loading="tableLoading"
>
<!-- <div class="view_top_btn" v-if="realTimeFlag">
<el-button type="primary" :icon="Platform" @click="handleRecordWaves">
实时录波
</el-button>
</div> -->
<div
class="view_top_btn"
v-if="dataSet.includes('_realtimedata') && !realTimeFlag && mqttMessage.dataTime && sonTab == 2"
>
<div class="view_top_btn"
v-if="dataSet.includes('_realtimedata') && !realTimeFlag && mqttMessage.dataTime && sonTab == 2">
<el-button :icon="Back" @click="handleReturn" :loading="tableLoading">返回</el-button>
</div>
<!-- 实时数据主界面组件 -->
<realTime v-if="realTimeFlag" ref="realTimeRef"></realTime>
<realTime v-if="realTimeFlag" ref="realTimeRef" :dataTime="mqttMessage.dataTime"></realTime>
<!-- 实时数据-实时录波组件 -->
<recordWoves v-if="!realTimeFlag && sonTab == 0"></recordWoves>
<!-- 实时数据-实时趋势组件 -->
<realTrend
v-if="!realTimeFlag && sonTab == 1"
ref="realTrendRef"
@changeTrendType="changeTrendType"
></realTrend>
<realTrend v-if="!realTimeFlag && sonTab == 1" ref="realTrendRef"
@changeTrendType="changeTrendType">
</realTrend>
<!-- 实时数据-谐波频谱组件 -->
<harmonicSpectrum
v-show="!realTimeFlag && sonTab == 2"
ref="harmonicSpectrumRef"
></harmonicSpectrum>
<harmonicSpectrum v-show="!realTimeFlag && sonTab == 2" ref="harmonicSpectrumRef">
</harmonicSpectrum>
</div>
<!-- 暂态事件 -->
<div
style="height: calc(100vh - 340px)"
v-if="dataSet.indexOf('_event') != -1"
v-loading="tableLoading"
>
<div style="height: calc(100vh - 340px)" v-if="dataSet.indexOf('_event') != -1">
<Event ref="eventRef" :deviceType="deviceType"></Event>
</div>
<!-- 测试项记录 -->
<div
style="height: calc(100vh - 395px)"
v-if="dataSet.indexOf('_items') != -1"
v-loading="tableLoading"
>
<div style="height: calc(100vh - 395px)" v-if="dataSet.indexOf('_items') != -1"
v-loading="tableLoading">
<testItemRecords ref="testItemRecordsRef" @onSubmit="handleClick" />
</div>
<!-- 模块数据 -->
<div
style="height: calc(100vh - 395px)"
v-if="dataSet.indexOf('_moduleData') != -1"
v-loading="tableLoading"
>
<div style="height: calc(100vh - 395px)" v-if="dataSet.indexOf('_moduleData') != -1"
v-loading="tableLoading">
<moduleData ref="moduleDataRef" @onSubmit="handleClick" />
</div>
<!-- 运行趋势 -->
<div
style="height: calc(100vh - 395px)"
v-if="dataSet.indexOf('_devRunTrend') != -1"
v-loading="tableLoading"
>
<div style="height: calc(100vh - 395px)" v-if="dataSet.indexOf('_devRunTrend') != -1"
v-loading="tableLoading">
<operatingTrend ref="operatingTrendRef" @onSubmit="handleClick" />
</div>
<div v-if="!tableData" style="height: 42px"></div>
@@ -592,7 +390,7 @@ import harmonicSpectrum from './tabs/components/harmonicSpectrum.vue' //实时
import recordWoves from './tabs/components/recordwoves.vue' //实时数据-实时录波子页面
import offLineDataImport from './offLineDataImport/index.vue'
import Event from './tabs/event.vue'
import nearRealTimeData from './nearRealTimeData.vue'
import nearRealTimeData from './nearRealTimeData.vue'//最新数据
import testItemRecords from './testItemRecords.vue'
import moduleData from './moduleData.vue'
import { useDictData } from '@/stores/dictData'
@@ -694,7 +492,7 @@ const volConTypeList = dictData.getBasicData('Dev_Connect')
const realTrendRef = ref()
const txtContent = ref('')
const operatingTrendRef = ref()
const electroplatingRef = ref()
const electroplatingRef = ref()
const changeTrendType = (val: any) => {
trendDataTime.value = ''
activeTrendName.value = val * 1
@@ -739,7 +537,7 @@ const handleTrend = async () => {
.then((res: any) => {
if (res.code == 'A0000') {
trendDataTime.value = ''
ElMessage.success('设备应答成功')
// ElMessage.success('设备应答成功')
//每隔30s调用一下接口通知后台推送mqtt消息
trendTimer.value = window.setInterval(() => {
if (!dataSet.value.includes('_realtimedata')) return
@@ -781,7 +579,7 @@ const handleHarmonicSpectrum = async () => {
// getRealDataMqttMsg()
await getBasicRealData(lineId.value).then((res: any) => {
if (res.code == 'A0000') {
ElMessage.success('设备应答成功')
// ElMessage.success('设备应答成功')
// mqttMessage.value = {}
showButton.value = true
realDataTimer.value = window.setInterval(() => {
@@ -811,7 +609,7 @@ const handleReturn = async () => {
tableLoading.value = true
await getBasicRealData(lineId.value).then((res: any) => {
if (res.code == 'A0000') {
ElMessage.success('设备应答成功')
// ElMessage.success('设备应答成功')
showButton.value = true
// mqttMessage.value = {}
realDataTimer.value = window.setInterval(() => {
@@ -905,7 +703,7 @@ const nodeClick = async (e: anyObj, node?: any) => {
}
//选中设备名称后,点击标签页也能查询数据,要求点击设备名称后,点击标签页默认查询第一个监测点数据
if (e.level == 3 ) {
if (e.level == 3) {
engineeringName.value = node?.parent.parent.data.name
await queryDictType({
@@ -1008,6 +806,7 @@ const bindMqttMessage = (handler: (topic: any, message: any) => void) => {
mqttRef.value.on('message', handler)
}
const connectMqtt = () => {
if (mqttRef.value) {
if (mqttRef.value.connected) {
@@ -1039,7 +838,7 @@ const getRealDataMqttMsg = async () => {
await getBasicRealData(lineId.value)
.then((res: any) => {
if (res.code == 'A0000') {
ElMessage.success('设备应答成功')
// ElMessage.success('设备应答成功')
mqttMessage.value = {}
showButton.value = true
realDataTimer.value = window.setInterval(async () => {
@@ -1064,7 +863,7 @@ const getRealDataMqttMsg = async () => {
tableLoading.value = false
}, 6000)
} else {
ElMessage.success('设备应答失败')
ElMessage.warning('设备应答失败')
tableLoading.value = false
}
})
@@ -1078,147 +877,150 @@ const getRealDataMqttMsg = async () => {
const realDataTimer: any = ref()
const mqttMessage = ref<any>({})
const realtimeMessage = ref<any>({})
const realtimeChange = () => {
onMqttRealDataMessage('', realtimeMessage.value)
}
/** 实时数据 / 实时趋势 MQTT 消息(命名函数,便于 off 避免重复注册) */
const onMqttRealDataMessage = (topic: any, message: any) => {
let obj = decodeMqttPayload(message)
if (lineId.value != obj.lineId || adminInfo.userIndex != obj.userId) return
realtimeMessage.value = message
//处理mqtt数据 1转2除 2转1乘
//如果消息返回值是二次值,下拉框是二次值只需要单位换算 除以1000
//如果消息返回值是一次值,下拉框是一次值只需要单位换算 除以1000
if (obj.dataLevel == formInline.dataLevel) {
obj = {
...obj,
// 电压有效值
vRmsA: obj.vRmsA,
vRmsB: obj.vRmsB,
vRmsC: obj.vRmsC,
//基波电压幅值
v1A: obj.v1A,
v1B: obj.v1B,
v1C: obj.v1C,
//有功功率
pA: obj.pA,
pB: obj.pB,
pC: obj.pC,
pTot: obj.pTot,
//无功功率
qA: obj.qA,
qB: obj.qB,
qC: obj.qC,
qTot: obj.qTot,
//视在功率
sA: obj.sA,
sB: obj.sB,
sC: obj.sC,
sTot: obj.sTot
}
}
//如果消息返回值是二次值,下拉框是一次值需要单位换算 除以1000 并且乘以pt ct
if (obj.dataLevel == 'Secondary' && formInline.dataLevel == 'Primary') {
obj = {
...obj,
// 电压有效值
vRmsA: (obj.vRmsA * obj.pt) / 1000,
vRmsB: (obj.vRmsB * obj.pt) / 1000,
vRmsC: (obj.vRmsC * obj.pt) / 1000,
// 电流有效值
iRmsA: obj.iRmsA * obj.ct,
iRmsB: obj.iRmsB * obj.ct,
iRmsC: obj.iRmsC * obj.ct,
//基波电压幅值
v1A: (obj.v1A * obj.pt) / 1000,
v1B: (obj.v1B * obj.pt) / 1000,
v1C: (obj.v1C * obj.pt) / 1000,
//基波电流幅值
i1A: obj.i1A * obj.ct,
i1B: obj.i1B * obj.ct,
i1C: obj.i1C * obj.ct,
//有功功率
pA: (obj.pA * obj.pt * obj.ct) / 1000,
pB: (obj.pB * obj.pt * obj.ct) / 1000,
pC: (obj.pC * obj.pt * obj.ct) / 1000,
pTot: (obj.pTot * obj.pt * obj.ct) / 1000,
//无功功率
qA: (obj.qA * obj.pt * obj.ct) / 1000,
qB: (obj.qB * obj.pt * obj.ct) / 1000,
qC: (obj.qC * obj.pt * obj.ct) / 1000,
qTot: (obj.qTot * obj.pt * obj.ct) / 1000,
//视在功率
sA: (obj.sA * obj.pt * obj.ct) / 1000,
sB: (obj.sB * obj.pt * obj.ct) / 1000,
sC: (obj.sC * obj.pt * obj.ct) / 1000,
sTot: (obj.sTot * obj.pt * obj.ct) / 1000
}
}
//如果消息返回值是一次值,下拉框是二次值需要单位换算 乘以1000 并且除以pt ct
if (obj.dataLevel == 'Primary' && formInline.dataLevel == 'Secondary') {
obj = {
...obj,
// 电压有效值
vRmsA: (obj.vRmsA / obj.pt) * 1000,
vRmsB: (obj.vRmsB / obj.pt) * 1000,
vRmsC: (obj.vRmsC / obj.pt) * 1000,
// 电流有效值
iRmsA: obj.iRmsA / obj.ct,
iRmsB: obj.iRmsB / obj.ct,
iRmsC: obj.iRmsC / obj.ct,
//基波电压幅值
v1A: (obj.v1A / obj.pt) * 1000,
v1B: (obj.v1B / obj.pt) * 1000,
v1C: (obj.v1C / obj.pt) * 1000,
//基波电流幅值
i1A: obj.i1A / obj.ct,
i1B: obj.i1B / obj.ct,
i1C: obj.i1C / obj.ct,
//有功功率
pA: (obj.pA / obj.pt / obj.ct) * 1000,
pB: (obj.pB / obj.pt / obj.ct) * 1000,
pC: (obj.pC / obj.pt / obj.ct) * 1000,
pTot: (obj.pTot / obj.pt / obj.ct) * 1000,
//无功功率
qA: (obj.qA / obj.pt / obj.ct) * 1000,
qB: (obj.qB / obj.pt / obj.ct) * 1000,
qC: (obj.qC / obj.pt / obj.ct) * 1000,
qTot: (obj.qTot / obj.pt / obj.ct) * 1000,
//视在功率
sA: (obj.sA / obj.pt / obj.ct) * 1000,
sB: (obj.sB / obj.pt / obj.ct) * 1000,
sC: (obj.sC / obj.pt / obj.ct) * 1000,
sTot: (obj.sTot / obj.pt / obj.ct) * 1000
}
}
//保留两位小数
for (var i in obj) {
if (typeof obj[i] == 'number' && obj[i] != 0 && Math.abs(obj[i]) % 1 != 0) {
obj[i] = obj[i].toFixed(2)
}
}
if (obj.hasOwnProperty('pA') && obj.hasOwnProperty('pB')) {
mqttMessage.value = obj
txtContent.value = JSON.stringify(obj)
//如果消息返回值是二次值,下拉框是二次值只需要单位换算 除以1000
//如果消息返回值是一次值,下拉框是一次值只需要单位换算 除以1000
if (obj.dataLevel == formInline.dataLevel) {
obj = {
...obj,
// 电压有效值
vRmsA: obj.vRmsA,
vRmsB: obj.vRmsB,
vRmsC: obj.vRmsC,
//基波电压幅值
v1A: obj.v1A,
v1B: obj.v1B,
v1C: obj.v1C,
//有功功率
pA: obj.pA,
pB: obj.pB,
pC: obj.pC,
pTot: obj.pTot,
//无功功率
qA: obj.qA,
qB: obj.qB,
qC: obj.qC,
qTot: obj.qTot,
//视在功率
sA: obj.sA,
sB: obj.sB,
sC: obj.sC,
sTot: obj.sTot
}
}
//如果消息返回值是二次值,下拉框是一次值需要单位换算 除以1000 并且乘以pt ct
if (obj.dataLevel == 'Secondary' && formInline.dataLevel == 'Primary') {
obj = {
...obj,
// 电压有效值
vRmsA: (obj.vRmsA * obj.pt) / 1000,
vRmsB: (obj.vRmsB * obj.pt) / 1000,
vRmsC: (obj.vRmsC * obj.pt) / 1000,
// 电流有效值
iRmsA: obj.iRmsA * obj.ct,
iRmsB: obj.iRmsB * obj.ct,
iRmsC: obj.iRmsC * obj.ct,
//基波电压幅值
v1A: (obj.v1A * obj.pt) / 1000,
v1B: (obj.v1B * obj.pt) / 1000,
v1C: (obj.v1C * obj.pt) / 1000,
//基波电流幅值
i1A: obj.i1A * obj.ct,
i1B: obj.i1B * obj.ct,
i1C: obj.i1C * obj.ct,
//有功功率
pA: (obj.pA * obj.pt * obj.ct) / 1000,
pB: (obj.pB * obj.pt * obj.ct) / 1000,
pC: (obj.pC * obj.pt * obj.ct) / 1000,
pTot: (obj.pTot * obj.pt * obj.ct) / 1000,
//无功功率
qA: (obj.qA * obj.pt * obj.ct) / 1000,
qB: (obj.qB * obj.pt * obj.ct) / 1000,
qC: (obj.qC * obj.pt * obj.ct) / 1000,
qTot: (obj.qTot * obj.pt * obj.ct) / 1000,
//视在功率
sA: (obj.sA * obj.pt * obj.ct) / 1000,
sB: (obj.sB * obj.pt * obj.ct) / 1000,
sC: (obj.sC * obj.pt * obj.ct) / 1000,
sTot: (obj.sTot * obj.pt * obj.ct) / 1000
}
}
//如果消息返回值是一次值,下拉框是二次值需要单位换算 乘以1000 并且除以pt ct
if (obj.dataLevel == 'Primary' && formInline.dataLevel == 'Secondary') {
obj = {
...obj,
// 电压有效值
vRmsA: (obj.vRmsA / obj.pt) * 1000,
vRmsB: (obj.vRmsB / obj.pt) * 1000,
vRmsC: (obj.vRmsC / obj.pt) * 1000,
// 电流有效值
iRmsA: obj.iRmsA / obj.ct,
iRmsB: obj.iRmsB / obj.ct,
iRmsC: obj.iRmsC / obj.ct,
//基波电压幅值
v1A: (obj.v1A / obj.pt) * 1000,
v1B: (obj.v1B / obj.pt) * 1000,
v1C: (obj.v1C / obj.pt) * 1000,
//基波电流幅值
i1A: obj.i1A / obj.ct,
i1B: obj.i1B / obj.ct,
i1C: obj.i1C / obj.ct,
//有功功率
pA: (obj.pA / obj.pt / obj.ct) * 1000,
pB: (obj.pB / obj.pt / obj.ct) * 1000,
pC: (obj.pC / obj.pt / obj.ct) * 1000,
pTot: (obj.pTot / obj.pt / obj.ct) * 1000,
//无功功率
qA: (obj.qA / obj.pt / obj.ct) * 1000,
qB: (obj.qB / obj.pt / obj.ct) * 1000,
qC: (obj.qC / obj.pt / obj.ct) * 1000,
qTot: (obj.qTot / obj.pt / obj.ct) * 1000,
//视在功率
sA: (obj.sA / obj.pt / obj.ct) * 1000,
sB: (obj.sB / obj.pt / obj.ct) * 1000,
sC: (obj.sC / obj.pt / obj.ct) * 1000,
sTot: (obj.sTot / obj.pt / obj.ct) * 1000
}
}
//保留两位小数
for (var i in obj) {
if (typeof obj[i] == 'number' && obj[i] != 0 && Math.abs(obj[i]) % 1 != 0) {
obj[i] = obj[i].toFixed(2)
}
}
if (obj.hasOwnProperty('pA') && obj.hasOwnProperty('pB')) {
mqttMessage.value = obj
txtContent.value = JSON.stringify(obj)
//更新实时数据主页面值
realTimeFlag.value &&
realTimeRef.value &&
realTimeRef.value.setRealData(mqttMessage.value, formInline.dataLevel)
tableLoading.value = false
//更新实时趋势折线图数据
if (sonTab.value == 2) {
!realTimeFlag.value &&
sonTab.value == 2 &&
harmonicSpectrumRef.value &&
harmonicSpectrumRef.value.setHarmonicSpectrumData(mqttMessage.value)
}
}
//更新实时数据主页面值
realTimeFlag.value &&
realTimeRef.value &&
realTimeRef.value.setRealData(mqttMessage.value, formInline.dataLevel)
tableLoading.value = false
//更新实时趋势折线图数据
if (sonTab.value == 2) {
!realTimeFlag.value &&
sonTab.value == 2 &&
harmonicSpectrumRef.value &&
harmonicSpectrumRef.value.setHarmonicSpectrumData(mqttMessage.value)
}
}
//更新谐波频谱数据
// !realTimeFlag.value &&
// sonTab.value == 1 &&
// realTrendRef.value &&
// realTrendRef.value.setRealTrendData(obj)
//更新谐波频谱数据
// !realTimeFlag.value &&
// sonTab.value == 1 &&
// realTrendRef.value &&
// realTrendRef.value.setRealTrendData(obj)
}
const handleClick = async (tab?: any) => {
@@ -1499,7 +1301,7 @@ const refreshTheState = () => {
moduleDataRef.value.getModule(deviceData.value.ndid)
}
//模版下载
const handleDownLoadTemplate = () => {}
const handleDownLoadTemplate = () => { }
//补召日志
const analysisListRef = ref()
//打开补召日志
@@ -1565,7 +1367,7 @@ const downloadTxt = () => {
URL.revokeObjectURL(url)
}
onMounted(() => {})
onMounted(() => { })
onBeforeUnmount(() => {
clearInterval(realDataTimer.value)
clearInterval(trendTimer.value)
@@ -1689,14 +1491,14 @@ onBeforeUnmount(() => {
.data_time {
width: 100%;
height: 40px;
// height: 40px;
text-align: center;
display: flex;
align-items: center;
p {
font-weight: 800;
font-size: 16px;
font-weight: 700;
font-size: 13px;
margin: 0 auto;
}
}

View File

@@ -1,116 +1,200 @@
<template>
<div class="near-realtime-data">
<div class="view_bot">
<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="A相" :width="getPhaseWidth(section)"
:formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}B`" title="B相" :width="getPhaseWidth(section)"
:formatter="cellFormatter"></vxe-column>
<vxe-column align="center" :field="`v${colIndex}C`" title="C相" :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-column v-for="emptyIndex in section.emptySlotCount" :key="`empty-${emptyIndex}`" align="center"
:width="getMetricWidth(section)"></vxe-column>
</vxe-table>
<div v-if="sectionIndex < tableSections.length - 1" class="table-gap"></div>
</template>
<div>
<vxe-table auto-resize :data="dataList" v-bind="defaultAttribute" :key="key" :mergeCells="mergeCells"
v-if="flag">
<vxe-column v-for="item in column" :field="item.field" :title="item.title" :formatter="formatter"
:width="item.width"></vxe-column>
</vxe-table>
<vxe-table v-if="flag1" auto-resize :data="dataList1" v-bind="defaultAttribute" :key="key"
:mergeCells="mergeCells">
<vxe-column field="otherName" title="名称"></vxe-column>
<vxe-column field="valueM" title="数据" :formatter="formatter"></vxe-column>
<vxe-column field="otherName1" title="名称"></vxe-column>
<vxe-column field="valueM1" title="数据" :formatter="formatter"></vxe-column>
</vxe-table>
<el-empty :style="height" description="暂无数据" v-if="!flag && !flag1"></el-empty>
<el-empty :style="height" description="暂无数据" v-if="tableSections.length === 0" />
</div>
</div>
</template>
<script setup lang='ts'>
<script setup lang="ts">
import { mainHeight } from '@/utils/layout'
import { ref, reactive } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import type { VxeTablePropTypes } from 'vxe-table'
const props = defineProps(['tableData'])
const height = mainHeight(325)
const dataList: any = ref([])
const key: any = ref(0)
const column: any = ref([])
const dataList1: any = ref([])
const targetName: any = ref('')
const flag = ref(true)
const flag1 = ref(true)
import { ref } from 'vue'
const mergeCells: any = ref<VxeTablePropTypes.MergeCells>([
// { row: 0, col: 1, rowspan: 1, colspan: 3 },
const ROW_WITH_ABC = 4
const ROW_SCALAR_ONLY = 5
])
const setData = (data: any, targetType: any) => {
let list = JSON.parse(JSON.stringify(data))
// dataList1.value = []
let list1: any = []
dataList.value = []
dataList1.value = []
flag.value = true
flag1.value = true
targetName.value = targetType[0].name
if (targetType[0].name == '基本数据') {
flag.value = false
flag1.value = false
list.forEach((item: any, index: number) => {
if (item.valueA == null && item.valueB == null && item.valueC == null) {
list1.push(item)
flag1.value = true
} else {
dataList.value.push(item)
flag.value = true
}
})
if (list1.length > 0) {
list1.forEach((item: any, index: number) => {
if (index % 2 == 0) {
dataList1.value.push(item)
} else {
dataList1.value[dataList1.value.length - 1].otherName1 = item.otherName
dataList1.value[dataList1.value.length - 1].valueM1 = item.valueM
}
})
}
column.value = [
{ field: 'otherName', title: '名称', },
{ field: 'valueA', title: 'A相', },
{ field: 'valueB', title: 'B相', },
{ field: 'valueC', title: 'C相', },
// { field: 'valueM', title: '', },
]
} else {
dataList.value = list
flag.value = true
flag1.value = false
column.value = [
{ field: 'otherName', title: '谐波次数', },
{ field: 'valueA', title: 'A相', },
{ field: 'valueB', title: 'B相', },
{ field: 'valueC', title: 'C相', },
]
}
key.value += 1
interface MetricItem {
type: 'abc' | 'scalar'
data: any
}
const formatter = (row: any) => {
if (row.column.property == 'valueM1') {
if (row.row.otherName1 == null) {
row.cellValue == ''
} else {
return row.cellValue == null ? '/' : row.cellValue == 3.14159 ? '/' : row.cellValue
}
interface TableSection {
abcItems: any[]
scalarItems: any[]
row: Record<string, unknown>
slotsPerRow: number
emptySlotCount: number
}
} else {
return row.cellValue == null ? '/' : row.cellValue == 3.14159 ? '/' : row.cellValue
const height = mainHeight(345)
const tableSections = ref<TableSection[]>([])
const tableColumnConfig = { resizable: false, tooltip: true }
const tableTooltipConfig = { enterable: true }
const getMetricWidth = (section: TableSection) => `${100 / section.slotsPerRow}%`
const getPhaseWidth = (section: TableSection) => `${100 / section.slotsPerRow / 3}%`
const formatCellValue = (value: unknown): string | number => {
if (value == null || value === 3.14159) return '/'
return typeof value === 'number' || typeof value === 'string' ? value : String(value)
}
const cellFormatter = ({ cellValue }: { cellValue: unknown }) => formatCellValue(cellValue)
const hasAbcValues = (item: any) =>
item.valueA != null || item.valueB != null || item.valueC != null
const buildRow = (abcItems: any[], scalarItems: any[]) => {
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
})
scalarItems.forEach((item, index) => {
row[`s${index}`] = item.valueM
})
return row
}
const buildTableSections = (abcList: any[], scalarList: any[]) => {
const unified: MetricItem[] = [
...abcList.map((data) => ({ type: 'abc' as const, data })),
...scalarList.map((data) => ({ type: 'scalar' as const, data })),
]
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 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),
slotsPerRow,
emptySlotCount: slotsPerRow - metricCount,
})
}
return sections
}
const setData = (data: any, _targetType: any) => {
const list = JSON.parse(JSON.stringify(data))
const abcList: any[] = []
const scalarList: any[] = []
list.forEach((item: any) => {
if (hasAbcValues(item)) {
abcList.push(item)
} else {
scalarList.push(item)
}
})
tableSections.value = buildTableSections(abcList, scalarList)
}
defineExpose({ setData })
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.near-realtime-data {
width: 100%;
height: 100%;
overflow: hidden;
box-sizing: border-box;
}
.view_bot {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
}
.table-gap {
height: 20px;
}
:deep(.view_bot) {
.near-realtime-table {
width: 100%;
}
.near-realtime-table .vxe-table--render-wrapper,
.near-realtime-table .vxe-table--main-wrapper,
.near-realtime-table .vxe-table--header-wrapper,
.near-realtime-table .vxe-table--body-wrapper,
.near-realtime-table .vxe-table--layout-wrapper {
width: 100% !important;
}
.near-realtime-table table {
width: 100% !important;
table-layout: fixed;
}
.near-realtime-table .vxe-header--column .vxe-cell,
.near-realtime-table .vxe-body--column .vxe-cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 22px !important;
}
.vxe-table--render-default .vxe-body--column:not(.col--ellipsis),
.vxe-table--render-default .vxe-footer--column:not(.col--ellipsis),
.vxe-table--render-default .vxe-header--column:not(.col--ellipsis) {
padding: 5px !important;
}
.vxe-table--body-wrapper {
min-height: 32px !important;
}
.vxe-body--row {
height: 32px !important;
}
.vxe-table--render-default {
font-size: 13px !important;
}
}
:deep(.vxe-table--render-default .vxe-body--column.col--ellipsis, .vxe-table--render-default .vxe-footer--column.col--ellipsis, .vxe-table--render-default .vxe-header--column.col--ellipsis, .vxe-table--render-default.vxe-editable .vxe-body--column) {
height: 32px !important;
}
</style>

View File

@@ -4,7 +4,7 @@
<TableHeader>
<template v-slot:select>
<el-form-item label="设备类型">
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备类型" /> -->
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备类型" /> -->
<el-select v-model.trim="tableStore.table.params.devType" clearable @change="devTypeChange"
placeholder="请选择设备类型">
<el-option v-for="item in devTypeOptions" :key="item.value" :label="item.label"
@@ -49,11 +49,11 @@
:before-close="resetForm" draggable width="40%">
<el-form :model="form" label-width="120px" :rules="rules" ref="ruleFormRef">
<el-form-item label="设备名称:" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" autocomplete="off" clearable
<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"
<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">
@@ -76,7 +76,7 @@
</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"
<el-input maxlength="32" show-word-limit v-model.trim="form.cntractNo" autocomplete="off"
placeholder="请输入"></el-input>
</el-form-item>
</el-form>

View File

@@ -5,7 +5,7 @@
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
<template v-slot:select>
<el-form-item>
<DatePicker ref="datePickerRef"></DatePicker>
<DatePicker ref="datePickerRef" :timeKeyList="[ '3', '4', '5']"></DatePicker>
</el-form-item>
<el-form-item label="电度指标" label-width="80px">
<el-select style="width: 200px" multiple :multiple-limit="3" collapse-tags filterable
@@ -37,7 +37,7 @@
</el-form-item> -->
</template>
<template #operation>
<el-button type="primary" icon="el-icon-Search" @click="init()">查询</el-button>
<el-button type="primary" icon="el-icon-Search" @click="init()" :loading="loading">查询</el-button>
<el-button :type="timeControl ? 'primary' : ''" icon="el-icon-Sort" @click="setTimeControl">
缺失数据
</el-button>

View File

@@ -19,6 +19,7 @@ import { ArrowLeft, Message } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { analyseWave, getFileByEventId } from '@/api/common'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData'
const tableParams: any = ref({})
const refheader = ref()
const view = ref(true)
@@ -38,7 +39,9 @@ const props = defineProps({
default: '0'
}
})
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const tableStore: any = new TableStore({
url: '/cs-device-boot/csGroup/deviceDataByType',
publicHeight: 215,
@@ -52,16 +55,16 @@ const tableStore: any = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'startTime', title: '暂降发生时刻', minWidth: 180, sortable: true },
{ field: 'startTime', title: '发生时刻', minWidth: 180, sortable: true },
{
field: 'featureAmplitude',
title: '暂降(骤升)幅值(%)',
minWidth: 160,
title: '暂降幅值(%)',
minWidth: 120,
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]
}
@@ -76,7 +79,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
},
@@ -85,12 +88,27 @@ const tableStore: any = new TableStore({
title: '相别',
minWidth: 80,
formatter: (row: any) => {
return row.cellValue || '/'
return row.cellValue || '-'
}
},
{ field: 'showName', title: '触发类型', minWidth: 120 },
{
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
{
title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => {
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
}
},
{
title: '操作',
@@ -150,7 +168,7 @@ const tableStore: any = new TableStore({
icon: 'el-icon-DataLine',
render: 'basicButton',
disabled: row => {
return row.wavePath
return row.wavePath
}
},
{
@@ -165,6 +183,7 @@ const tableStore: any = new TableStore({
return !row.wavePath
},
click: row => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
@@ -176,6 +195,9 @@ const tableStore: any = new TableStore({
link.click() //执行下载
document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}
},

View File

@@ -85,25 +85,78 @@ function fillDataFromFirstTime(data, interval = 10) {
return filledData
}
const COLOR_NORMAL = '#2ab914'
const COLOR_INTERRUPT = '#e26257'
/** 分段着色:水平段用当前状态色,切换垂直段用目标状态色 */
const buildColoredStepSeries = (data: any[]) => {
const points = data.map((item: any) => [item.time, item.type == 0 ? 1 : 10])
if (points.length === 0) return []
const series: any[] = []
let i = 0
while (i < points.length) {
const value = points[i][1]
const color = value === 1 ? COLOR_INTERRUPT : COLOR_NORMAL
const segment: any[] = []
// 垂直切换段由目标状态 series 绘制(正常→中断红线,中断→正常绿线)
if (i > 0 && points[i][1] !== points[i - 1][1]) {
segment.push([points[i][0], points[i - 1][1]])
}
segment.push(points[i])
let j = i + 1
while (j < points.length && points[j][1] === value) {
segment.push(points[j])
j++
}
// 仅水平延伸到切换时刻,垂直段由下一段绘制
if (j < points.length) {
segment.push([points[j][0], value])
}
series.push({
name: '运行数据',
type: 'line',
step: 'end',
showSymbol: false,
data: segment,
lineStyle: { color, width: 2 }
})
i = j
}
return series
}
const getStatusText = (val: number | string) => {
if (val === 1 || val === '1') return '中断'
if (val === 10 || val === '10') return '正常'
return ''
}
const init = () => {
options1.value = {
title: {
text: '运行状态'
text: '运行数据'
},
legend: {
show: false
},
tooltip: {
trigger: 'axis',
formatter: function (params: any) {
var res = params[0].data[0] + '<br/>运行状态:'
var texts = ''
if (params[0].data[1] === 1 || params[0].data[1] === '1') {
texts = '中断'
} else if (params[0].data[1] === 10 || params[0].data[1] === '10') {
texts = '正常'
}
res = res + texts
return res
const items = (Array.isArray(params) ? params : [params]).filter(p => p?.data?.[1] != null)
if (!items.length) return ''
// 切换节点取目标状态(后一段 series 的值)
const item = items[items.length - 1]
const val = item.data[1]
return `${item.data[0]}<br/>运行数据:${getStatusText(val)}`
}
},
xAxis: {
@@ -146,16 +199,7 @@ const init = () => {
}
}
},
series: [
{
name: '运行状态',
data: list.value.map((item: any, index: number) => [item.time, item.type == 0 ? 1 : 10]),
type: 'line',
showSymbol: false,
step: 'end'
}
]
series: buildColoredStepSeries(list.value)
}
}
defineExpose({

View File

@@ -1,52 +1,61 @@
<template>
<div class="view" v-loading="loading">
<el-collapse v-model.trim="activeNames">
<el-collapse-item title="基础数据" name="1">
<div class="view_top">
<!-- 左侧仪表盘 -->
<div class="view_top_left">
<div class="left_charts_title">电压有效值({{ voltageUnit }})</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart1" :options="echartsDataV1"></MyEchart>
<div class="charts-panel">
<el-collapse v-model="activeNames" class="charts-collapse">
<el-collapse-item name="1">
<template #title>
<div class="charts-collapse-header">
<div style="font-weight: 700; font-size: 13px; text-align: center">数据时间{{ dataTime || '-' }}</div>
</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart2" :options="echartsDataV2"></MyEchart>
</template>
<template #icon>
<span class="charts-collapse-action">
<span class="charts-collapse-text">{{ chartPanelExpanded ? '收起' : '展开' }}</span>
<el-icon class="charts-collapse-icon" :class="{ 'is-expanded': chartPanelExpanded }">
<ArrowDown />
</el-icon>
</span>
</template>
<div class="view_top mb10">
<div class="view_top_left">
<div class="left_charts_title">电压有效值({{ voltageUnit }})</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart1" :options="echartsDataV1"></MyEchart>
</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart2" :options="echartsDataV2"></MyEchart>
</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart3" :options="echartsDataV3"></MyEchart>
</div>
</div>
<div class="left_charts">
<MyEchart :pieInterVal="true" ref="pieChart3" :options="echartsDataV3"></MyEchart>
<div class="view_top_mid">
<div class="mid_charts_title">基波电压/电流幅值(相位)</div>
<div class="mid_charts">
<MyEchart :pieInterVal="true" :options="echartsData1"></MyEchart>
</div>
</div>
<div class="view_top_right">
<div class="right_charts_title">电流有效值(A)</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart4" :options="echartsDataA1"></MyEchart>
</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart5" :options="echartsDataA2"></MyEchart>
</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart6" :options="echartsDataA3"></MyEchart>
</div>
</div>
</div>
<div class="view_top_mid">
<div class="mid_charts_title">基波电压/电流幅值(相位)</div>
<div class="mid_charts">
<MyEchart :pieInterVal="true" :options="echartsData1"></MyEchart>
</div>
</div>
<!-- 右侧仪表盘 -->
<div class="view_top_right">
<div class="right_charts_title">电流有效值(A)</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart4" :options="echartsDataA1"></MyEchart>
</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart5" :options="echartsDataA2"></MyEchart>
</div>
<div class="right_charts">
<MyEchart :pieInterVal="true" ref="pieChart6" :options="echartsDataA3"></MyEchart>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-collapse-item>
</el-collapse>
</div>
<div class="view_bot">
<vxe-table
border
height=""
:data="realList"
:column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }"
>
<vxe-table border height="" :data="realList" :column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }">
<vxe-colgroup align="center" :title="`电压有效值(${voltageUnit})`">
<vxe-column align="center" field="vRmsA" title="A相"></vxe-column>
<vxe-column align="center" field="vRmsB" title="B相"></vxe-column>
@@ -69,13 +78,8 @@
</vxe-colgroup>
</vxe-table>
<br />
<vxe-table
border
height=""
:data="realList"
:column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }"
>
<vxe-table border height="" :data="realList" :column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }">
<vxe-column align="center" field="freq" width="140" title="频率(Hz)"></vxe-column>
<vxe-column align="center" field="freqDev" width="120" title="频率偏差(Hz)"></vxe-column>
<vxe-column align="center" width="180" field="vUnbalance" title="电压不平衡度(%)"></vxe-column>
@@ -92,13 +96,8 @@
</vxe-colgroup>
</vxe-table>
<br />
<vxe-table
border
height=""
:data="realList"
:column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }"
>
<vxe-table border height="" :data="realList" :column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }">
<vxe-colgroup align="center" title="电压偏差(%)">
<vxe-column align="center" field="vDevA" title="A相"></vxe-column>
<vxe-column align="center" field="vDevB" title="B相"></vxe-column>
@@ -122,13 +121,8 @@
</vxe-colgroup>
</vxe-table>
<br />
<vxe-table
border
height=""
:data="realList"
:column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }"
>
<vxe-table border height="" :data="realList" :column-config="{ resizable: true, tooltip: true }"
:tooltip-config="{ enterable: true }">
<vxe-colgroup align="center" :title="`无功功率(${reactivePowerUnit})`">
<vxe-column align="center" field="qA" title="A相"></vxe-column>
<vxe-column align="center" field="qB" title="B相"></vxe-column>
@@ -162,18 +156,23 @@
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import { ArrowDown } from '@element-plus/icons-vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { getRealTimeTableList } from '@/api/cs-device-boot/EquipmentDelivery'
const pieChartRef: any = ref()
const activeNames = ref(['1'])
const props = defineProps({
dataTime: String
})
const activeNames = ref<string[]>(['1'])
const chartPanelExpanded = computed(() => activeNames.value.includes('1'))
const pieChart1: any = ref()
const pieChart2: any = ref()
const pieChart3: any = ref()
const pieChart4: any = ref()
const pieChart5: any = ref()
const pieChart6: any = ref()
const echartsData: any = ref({})
const echartsData1: any = ref({})
//电压有效值
const echartsDataV1: any = ref({})
@@ -187,7 +186,170 @@ const echartsDataA3: any = ref({})
const currentDataLevel = ref('Primary')
const previousDataLevel = ref('')
//渲染中间相角图
const PHASE_COLORS = {
A: '#DAA520',
B: '#2E8B57',
C: '#A52a2a',
}
const PHASE_RING_COLOR = '#9D322D'
type GaugeAlign = 'left' | 'right'
const createPhaseGaugeData = (name: string, value: number, color: string) => ({
name,
value,
itemStyle: { color, shadowBlur: 0, shadowColor: 'transparent' },
})
const createRmsGaugeOptions = (name: string, color: string, align: GaugeAlign) => {
const isLeft = align === 'left'
return {
options: {
tooltip: {},
toolbox: { show: false },
series: [
{
type: 'gauge',
startAngle: isLeft ? 180 : 90,
endAngle: isLeft ? 90 : 0,
min: 0,
max: 12,
radius: '180%',
center: isLeft ? ['70%', '95%'] : ['30%', '95%'],
splitNumber: 2,
axisLine: {
show: true,
lineStyle: {
width: 5,
shadowBlur: 0,
color: [[1, color]],
},
},
axisTick: {
show: true,
distance: -5,
length: 12,
lineStyle: {
color,
width: 2,
type: 'solid',
},
},
axisLabel: {
show: true,
distance: 12,
color,
fontSize: 12,
},
splitLine: {
show: true,
distance: -5,
length: 20,
lineStyle: {
color,
width: 3,
type: 'solid',
},
},
title: {
show: true,
fontWeight: 'bolder',
fontSize: 12,
color,
offsetCenter: isLeft ? ['-130%', '-20%'] : ['130%', '-20%'],
},
detail: {
fontSize: 14,
color,
valueAnimation: true,
formatter: '{value}',
offsetCenter: isLeft ? ['50%', '-35%'] : ['-45%', '-35%'],
},
pointer: {
length: '80%',
width: 3,
shadowBlur: 0,
shadowColor: 'transparent',
itemStyle: {
color,
shadowBlur: 0,
shadowColor: 'transparent',
},
},
itemStyle: {
color,
shadowBlur: 0,
shadowColor: 'transparent',
},
data: [createPhaseGaugeData(name, 0, color)],
},
],
},
}
}
const applyGaugePhaseColor = (options: any, color: string) => {
const series = options.series[0]
series.axisLine.lineStyle.color = [[1, color]]
series.itemStyle = { color, shadowBlur: 0, shadowColor: 'transparent' }
series.pointer = {
...series.pointer,
itemStyle: { color, shadowBlur: 0, shadowColor: 'transparent' },
shadowBlur: 0,
shadowColor: 'transparent',
}
series.axisTick.lineStyle.color = color
series.splitLine.lineStyle.color = color
series.axisLabel.color = color
series.title.color = color
series.detail.color = color
if (series.data?.[0]) {
series.data[0].itemStyle = { color, shadowBlur: 0, shadowColor: 'transparent' }
}
}
const buildPhaseLegendGraphic = () => {
const phases = [
{ key: 'A', label: 'A相', color: PHASE_COLORS.A },
{ key: 'B', label: 'B相', color: PHASE_COLORS.B },
{ key: 'C', label: 'C相', color: PHASE_COLORS.C },
]
const voltageValues = [
realData.value.v1AngA ?? 0,
realData.value.v1AngB ?? 0,
realData.value.v1AngC ?? 0,
]
const currentValues = [
realData.value.i1AngA ?? 0,
realData.value.i1AngB ?? 0,
realData.value.i1AngC ?? 0,
]
return [
{
type: 'group',
right: 20,
bottom: 20,
// children,
},
]
}
const updateRmsGaugeChart = (
chartRef: any,
chartData: any,
name: string,
value: number,
color: string
) => {
chartData.value.options.series[0].max = value == 0 ? 1 : Math.ceil(value * 1.2)
chartData.value.options.series[0].data = [createPhaseGaugeData(name, value || 0, color)]
applyGaugePhaseColor(chartData.value.options, color)
chartRef.value?.initChart()
}
const initRadioCharts = () => {
echartsData1.value = {
options: {
@@ -234,34 +396,34 @@ const initRadioCharts = () => {
show: true,
lineStyle: {
color: [
[0.25, '#9D322D'],
[0.5, '#9D322D'],
[0.75, '#9D322D'],
[1, '#9D322D']
[0.25, PHASE_RING_COLOR],
[0.5, PHASE_RING_COLOR],
[0.75, PHASE_RING_COLOR],
[1, PHASE_RING_COLOR],
],
width: 2
}
width: 2,
},
},
// 表盘细分数
axisTick: {
show: true,
splitNumber: 5,
distance: 4,
length: 4,
lineStyle: {
color: '#9D322D',
color: PHASE_RING_COLOR,
width: 1,
type: 'solid'
}
type: 'solid',
},
},
// 分割线
splitLine: {
show: true,
distance: 4,
length: 10,
lineStyle: {
color: '#9D322D',
color: PHASE_RING_COLOR,
width: 2,
type: 'solid'
}
type: 'solid',
},
},
// 分割线标识
axisLabel: {
@@ -287,7 +449,9 @@ const initRadioCharts = () => {
icon: 'path://m368.01136,209.80637l173.00807,-193.72679c19.14653,-21.43943 50.16392,-21.43943 69.31045,0l172.93149,193.72679c1.22537,1.37213 1.22537,3.51607 0,4.8882l-47.63657,53.34133c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-113.65381,-127.26452c-1.91465,-2.14395 -5.20785,-0.60031 -5.20785,2.40122l0,731.94254c0,1.88667 -1.37855,3.43031 -3.06345,3.43031l-67.39579,0c-1.6849,0 -3.06345,-1.54364 -3.06345,-3.43031l0,-731.94254c0,-3.08728 -3.2932,-4.54517 -5.20785,-2.40122l-113.65381,127.26452c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-47.63657,-53.34133c-1.22537,-1.37213 -1.22537,-3.51607 0,-4.88819l0,-0.00001M539,861.23064h73v800h-73z',
length: '90%',
width: 15,
opacity: 1
opacity: 1,
shadowBlur: 0,
shadowColor: 'transparent',
},
detail: {
show: false
@@ -297,21 +461,21 @@ const initRadioCharts = () => {
value: 0,
name: 'A相',
itemStyle: {
color: '#DAA520'
color: PHASE_COLORS.A
}
},
{
value: 0,
name: 'B相',
itemStyle: {
color: '#2E8B57'
color: PHASE_COLORS.B
}
},
{
value: 0,
name: 'C相',
itemStyle: {
color: '#A52a2a'
color: PHASE_COLORS.C
}
}
]
@@ -341,34 +505,34 @@ const initRadioCharts = () => {
show: true,
lineStyle: {
color: [
[0.25, '#9D322D'],
[0.5, '#9D322D'],
[0.75, '#9D322D'],
[1, '#9D322D']
[0.25, PHASE_RING_COLOR],
[0.5, PHASE_RING_COLOR],
[0.75, PHASE_RING_COLOR],
[1, PHASE_RING_COLOR],
],
width: 1.5
}
width: 1.5,
},
},
// 表盘细分数
axisTick: {
show: true,
splitNumber: 5,
distance: 5,
length: 6,
lineStyle: {
color: '#9D322D',
color: PHASE_RING_COLOR,
width: 1,
type: 'solid'
}
type: 'solid',
},
},
// 分割线
splitLine: {
show: true,
distance: 5,
length: 10,
lineStyle: {
color: '#9D322D',
color: PHASE_RING_COLOR,
width: 2,
type: 'solid'
}
type: 'solid',
},
},
// 分割线标识
axisLabel: {
@@ -394,7 +558,9 @@ const initRadioCharts = () => {
icon: 'path://m368.01136,209.80637l173.00807,-193.72679c19.14653,-21.43943 50.16392,-21.43943 69.31045,0l172.93149,193.72679c1.22537,1.37213 1.22537,3.51607 0,4.8882l-47.63657,53.34133c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-113.65381,-127.26452c-1.91465,-2.14395 -5.20785,-0.60031 -5.20785,2.40122l0,731.94254c0,1.88667 -1.37855,3.43031 -3.06345,3.43031l-67.39579,0c-1.6849,0 -3.06345,-1.54364 -3.06345,-3.43031l0,-731.94254c0,-3.08728 -3.2932,-4.54517 -5.20785,-2.40122l-113.65381,127.26452c-1.22538,1.37213 -3.14003,1.37213 -4.36541,0l-47.63657,-53.34133c-1.22537,-1.37213 -1.22537,-3.51607 0,-4.88819l0,-0.00001M539,861.23064h73v800h-73z',
length: '90%',
width: 15,
opacity: 1
opacity: 1,
shadowBlur: 0,
shadowColor: 'transparent',
},
detail: {
show: false
@@ -404,21 +570,21 @@ const initRadioCharts = () => {
value: 0,
name: 'A相',
itemStyle: {
color: '#DAA520'
color: PHASE_COLORS.A
}
},
{
value: 0,
name: 'B相',
itemStyle: {
color: '#2E8B57'
color: PHASE_COLORS.B
}
},
{
value: 0,
name: 'C相',
itemStyle: {
color: '#A52a2a'
color: PHASE_COLORS.C
}
}
]
@@ -459,75 +625,17 @@ const initRadioCharts = () => {
echartsData1.value.options.series[i].endAngle = -360
echartsData1.value.options.series[i].center = ['50%', '50%']
}
echartsData1.value.options.graphic = buildPhaseLegendGraphic()
}
//渲染echarts
const init = () => {
echartsData.value = {
options: {
tooltip: {},
toolbox: {
show: false
},
series: [
{
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 450,
radius: '150%',
center: ['55%', '76%'],
splitNumber: 2, //刻度数量
axisLine: {
show: true,
lineStyle: {
width: 10,
shadowBlur: 0,
color: [
[0.3, '#91c7a1'],
[0.7, '#63869e'],
[1, '#002B6A']
]
}
},
itemStyle: {
normal: {
shadowBlur: 10
}
},
//标题位置
title: {
fontWeight: 'bolder',
fontSize: 12,
offsetCenter: ['-130%', '-20%']
},
//数值位置
detail: {
fontSize: 12,
valueAnimation: true,
formatter: '{value}',
offsetCenter: ['0%', '25%']
},
data: [
{
value: 0,
name: 'A相'
}
]
}
]
}
}
echartsDataV1.value = JSON.parse(JSON.stringify(echartsData.value))
echartsDataV2.value = JSON.parse(JSON.stringify(echartsData.value))
echartsDataV3.value = JSON.parse(JSON.stringify(echartsData.value))
echartsDataV1.value.options.series[0].max = 12
echartsDataV2.value.options.series[0].max = 12
echartsDataV3.value.options.series[0].max = 12
echartsDataA1.value = echartsData.value
echartsDataA2.value = echartsData.value
echartsDataA3.value = echartsData.value
echartsDataV1.value = createRmsGaugeOptions('A相', PHASE_COLORS.A, 'left')
echartsDataV2.value = createRmsGaugeOptions('B相', PHASE_COLORS.B, 'left')
echartsDataV3.value = createRmsGaugeOptions('C相', PHASE_COLORS.C, 'left')
echartsDataA1.value = createRmsGaugeOptions('A相', PHASE_COLORS.A, 'right')
echartsDataA2.value = createRmsGaugeOptions('B相', PHASE_COLORS.B, 'right')
echartsDataA3.value = createRmsGaugeOptions('C相', PHASE_COLORS.C, 'right')
}
//接收父组件传递的table数据
const dataList: any = ref([])
@@ -603,57 +711,12 @@ const setRealData = (val: any, dataLevel: string) => {
realData.value = val
realList.value = [val]
initRadioCharts()
//新的
echartsDataV1.value.options.series[0].max = realData.value.vRmsA == 0 ? 1 : Math.ceil(realData.value.vRmsA * 1.2)
// console.log("🚀 ~ setRealData ~ realData.value.vRmsA:", realData.value.vRmsA)
echartsDataV1.value.options.series[0].data = [
{
name: 'A相',
value: realData.value.vRmsA || 0
}
]
pieChart1.value.initChart()
echartsDataV2.value.options.series[0].max = realData.value.vRmsB == 0 ? 1 : Math.ceil(realData.value.vRmsB * 1.2)
echartsDataV2.value.options.series[0].data = [
{
name: 'B相',
value: realData.value.vRmsB || 0
}
]
pieChart2.value.initChart()
echartsDataV3.value.options.series[0].max = realData.value.vRmsC == 0 ? 1 : Math.ceil(realData.value.vRmsC * 1.2)
echartsDataV3.value.options.series[0].data = [
{
name: 'C相',
value: realData.value.vRmsC || 0
}
]
pieChart3.value.initChart()
//新的电流有效值
echartsDataA1.value.options.series[0].max = realData.value.iRmsA == 0 ? 1 : Math.ceil(realData.value.iRmsA * 1.2)
echartsDataA1.value.options.series[0].data = [
{
name: 'A相',
value: realData.value.iRmsA || 0
}
]
pieChart4.value.initChart()
echartsDataA2.value.options.series[0].max = realData.value.iRmsB == 0 ? 1 : Math.ceil(realData.value.iRmsB * 1.2)
echartsDataA2.value.options.series[0].data = [
{
name: 'B相',
value: realData.value.iRmsB || 0
}
]
pieChart5.value.initChart()
echartsDataA3.value.options.series[0].max = realData.value.iRmsC == 0 ? 1 : Math.ceil(realData.value.iRmsC * 1.2)
echartsDataA3.value.options.series[0].data = [
{
name: 'C相',
value: realData.value.iRmsC || 0
}
]
pieChart6.value.initChart()
updateRmsGaugeChart(pieChart1, echartsDataV1, 'A相', realData.value.vRmsA, PHASE_COLORS.A)
updateRmsGaugeChart(pieChart2, echartsDataV2, 'B相', realData.value.vRmsB, PHASE_COLORS.B)
updateRmsGaugeChart(pieChart3, echartsDataV3, 'C相', realData.value.vRmsC, PHASE_COLORS.C)
updateRmsGaugeChart(pieChart4, echartsDataA1, 'A相', realData.value.iRmsA, PHASE_COLORS.A)
updateRmsGaugeChart(pieChart5, echartsDataA2, 'B相', realData.value.iRmsB, PHASE_COLORS.B)
updateRmsGaugeChart(pieChart6, echartsDataA3, 'C相', realData.value.iRmsC, PHASE_COLORS.C)
}
defineExpose({ setRealData })
onMounted(() => {
@@ -662,6 +725,87 @@ onMounted(() => {
})
</script>
<style lang="scss" scoped>
.charts-panel {
position: relative;
width: 100%;
min-height: 32px;
flex-shrink: 0;
}
.charts-collapse {
border-top: none;
border-bottom: none;
:deep(.el-collapse-item) {
position: relative;
}
:deep(.el-collapse-item__header) {
// position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
width: 100%;
height: 32px !important;
min-height: 32px !important;
border: none;
background: transparent;
padding: 0 12px;
font-size: 13px !important;
line-height: 30px;
font-weight: normal !important;
}
:deep(.el-collapse-item__wrap) {
border: none;
}
:deep(.el-collapse-item__arrow) {
display: none;
}
}
.charts-collapse-header {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 32px;
pointer-events: none;
}
.charts-collapse-action {
display: inline-flex;
align-items: center;
justify-content: flex-end;
gap: 4px;
min-width: 56px;
color: var(--el-color-primary);
font-size: 13px;
line-height: 32px;
flex-shrink: 0;
}
.charts-collapse-text {
display: inline-block;
min-width: 28px;
text-align: right;
}
.charts-collapse-icon {
font-size: 12px;
transition: transform 0.2s ease;
}
.charts-collapse-icon.is-expanded {
transform: rotate(180deg);
}
.view {
width: 100%;
height: 100%;
@@ -676,7 +820,7 @@ onMounted(() => {
justify-content: space-between;
//border: 1px solid #eee;
// padding: 10px;
margin-top: 10px;
// margin-top: 10px;
.view_top_left,
.view_top_right {
@@ -734,7 +878,6 @@ onMounted(() => {
.view_bot {
// min-height: 300px;
margin: 10px 0 0 0;
overflow: auto !important;
flex: 1 !important;
// padding-bottom: 200px !important;
@@ -835,6 +978,7 @@ onMounted(() => {
}
:deep(.view_bot) {
.vxe-table--render-default .vxe-body--column:not(.col--ellipsis),
.vxe-table--render-default .vxe-footer--column:not(.col--ellipsis),
.vxe-table--render-default .vxe-header--column:not(.col--ellipsis) {
@@ -846,8 +990,7 @@ onMounted(() => {
}
}
::v-deep .el-collapse-item__header {
font-size: 16px !important;
font-weight: 800 !important;
:deep(.vxe-table--render-default) {
font-size: 13px !important;
}
</style>

View File

@@ -5,44 +5,25 @@
<TableHeader ref="tableHeaderRef" :showSearch="false" @selectChange="selectChange">
<template v-slot:select>
<el-form-item>
<DatePicker ref="datePickerRef"></DatePicker>
<DatePicker ref="datePickerRef" :timeKeyList="[ '3', '4', '5']"></DatePicker>
</el-form-item>
<el-form-item label="统计指标" label-width="80px">
<el-select
style="width: 200px"
multiple
:multiple-limit="3"
collapse-tags
filterable
collapse-tags-tooltip
v-model="searchForm.index"
placeholder="请选择统计指标"
@change="onIndexChange($event)"
>
<el-option
v-for="item in indexOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-select style="width: 200px" multiple :multiple-limit="3" collapse-tags filterable
collapse-tags-tooltip v-model="searchForm.index" placeholder="请选择统计指标"
@change="onIndexChange($event)">
<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>
<el-radio-group
v-model="searchForm.dataLevel"
:disabled="props?.TrendList?.lineType != 1"
@change="init()"
>
<el-radio-group v-model="searchForm.dataLevel" :disabled="props?.TrendList?.lineType != 1"
@change="init()">
<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="searchForm.valueType"
>
<el-select style="min-width: 120px !important" placeholder="请选择" v-model="searchForm.valueType">
<el-option value="max" label="最大值"></el-option>
<el-option value="min" label="最小值"></el-option>
<el-option value="avg" label="平均值"></el-option>
@@ -50,34 +31,21 @@
</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"
>
<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">
<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>
</template>
<template #operation>
<el-button type="primary" icon="el-icon-Search" @click="init()">查询</el-button>
<el-button type="primary" icon="el-icon-Search" @click="init()" :loading="loading">查询</el-button>
<el-button :type="timeControl ? 'primary' : ''" icon="el-icon-Sort" @click="setTimeControl">
缺失数据
</el-button>
@@ -163,8 +131,8 @@ queryByCode(
props?.TrendList?.lineType == 0
? 'apf-harmonic'
: props?.TrendList?.conType == 1
? 'portable-harmonic-jx'
: 'portable-harmonic'
? 'portable-harmonic-jx'
: 'portable-harmonic'
).then(res => {
queryCsDictTree(res.data.id).then(item => {
//排序
@@ -479,10 +447,10 @@ const setEchart = () => {
return item.anotherName == '电压负序分量'
? '电压不平衡'
: item.anotherName == '电压正序分量'
? '电压不平衡'
: item.anotherName == '电压零序分量'
? '电压不平衡'
: item.anotherName
? '电压不平衡'
: item.anotherName == '电压零序分量'
? '电压不平衡'
: item.anotherName
})
)
]
@@ -516,10 +484,10 @@ const setEchart = () => {
(kk[0].anotherName == '电压负序分量'
? '电压不平衡'
: kk[0].anotherName == '电压正序分量'
? '电压不平衡'
: kk[0].anotherName == '电压零序分量'
? '电压不平衡'
: kk[0].anotherName)
? '电压不平衡'
: kk[0].anotherName == '电压零序分量'
? '电压不平衡'
: kk[0].anotherName)
)
let seriesList: any = []
@@ -600,8 +568,10 @@ const handleExport = async () => {
cell8 = obj.capacitySi,
cell9 = obj.capacitySscb,
cell10 = obj.capacitySt,
cell11 = obj.pt && obj.pt1 ? obj.pt / obj.pt1 + '\b' : '/',
cell12 = obj.ct && obj.ct1 ? obj.ct / obj.ct1 + '\b' : '/',
cell11 = obj.pt || 1 + ':' + obj.pt1 || 1,
cell12 = obj.ct || 1 + ':' + obj.ct1 || 1,
// cell11 = obj.pt && obj.pt1 ? obj.pt / obj.pt1 + '\b' : '/',
// cell12 = obj.ct && obj.ct1 ? obj.ct / obj.ct1 + '\b' : '/',
cell13 = obj.startTime ? obj.startTime : '/',
cell14 = obj.endTime ? obj.endTime : '/',
cell15 = obj.location
@@ -867,6 +837,7 @@ defineExpose({ getTrendRequest })
min-width: 100px;
}
}
:deep(.el-select__selected-item) {
.is-closable {
width: 100px !important;

View File

@@ -33,9 +33,9 @@
class="cn-operate-dialog"
:close-on-click-modal="false"
>
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="filterText"
icon="el-icon-Search"
placeholder="请输入内容"
@@ -82,7 +82,7 @@ import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const pageHeight = mainHeight(60)
const loading = ref(true)
const loading = ref(false)
const tableHeight = mainHeight(173)
const user: any = ref({})

View File

@@ -2,18 +2,14 @@
<template>
<div class="default-main main" :style="{ height: pageHeight.height }">
<div class="main_left">
<DeviceTree @node-click="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
<DeviceTree @node-click="nodeClick" @deviceTypeChange="deviceTypeChange" @init="nodeClick"></DeviceTree>
</div>
<div class="main_right" v-loading="loading">
<div class="right_nav">
<div class="menu" v-if="activePathList.length != 0">
<el-breadcrumb separator="/">
<el-breadcrumb-item
v-for="(item, index) in activePathList"
style="cursor: pointer"
:key="index"
@click="handleIntoByPath(item)"
>
<el-breadcrumb-item v-for="(item, index) in activePathList" style="cursor: pointer" :key="index"
@click="handleIntoByPath(item)">
<span>{{ outPutPath(item, index) }}</span>
</el-breadcrumb-item>
</el-breadcrumb>
@@ -24,27 +20,14 @@
</div>
<div class="filter" v-if="activePathList.length != 0">
<el-input
maxlength="32"
show-word-limit
style="width: 240px; height: 32px"
placeholder="请输入文件或文件夹名称"
clearable
v-model.trim="filterText"
type="text"
></el-input>
<el-input maxlength="32" show-word-limit style="width: 240px; height: 32px" placeholder="请输入文件或文件夹名称"
clearable v-model.trim="filterText" type="text"></el-input>
<el-button type="primary" @click="handleSearch" :icon="Search">搜索</el-button>
<!-- <el-button @click="handleRefresh" :icon="Refresh">重置</el-button> -->
<el-upload
v-if="activePath != '/'"
action=""
:auto-upload="false"
:show-file-list="false"
:on-change="(file: any, fileList: any) => {
<el-upload v-if="activePath != '/'" action="" :auto-upload="false" :show-file-list="false" :on-change="(file: any, fileList: any) => {
handleUpload(file, fileList, activePath)
}
"
>
">
<el-button>
文件上传
<el-icon class="el-icon--right">
@@ -62,29 +45,19 @@
</div>
<!-- 以列表形式展示 -->
<div :style="tableHeight">
<vxe-table
style="margin-top: 10px"
border
auto-resize
height="auto"
:data="dirList"
v-bind="defaultAttribute"
>
<vxe-table style="margin-top: 10px" border auto-resize height="auto" :data="dirList"
v-bind="defaultAttribute">
<vxe-column type="seq" title="序号" width="80"></vxe-column>
<vxe-column field="prjDataPath" align="center" title="名称" minWidth="180" #default="{ row }">
<span
style="cursor: pointer; color: #551a8b"
:style="{
'text-decoration': row.type == 'dir' ? 'underline' : 'none',
color: row.type == 'dir' ? '#551a8b' : '#000'
}"
@click="handleIntoDir(row)"
>
<span style="cursor: pointer; color: #551a8b" :style="{
'text-decoration': row.type == 'dir' ? 'underline' : 'none',
color: row.type == 'dir' ? '#551a8b' : '#000'
}" @click="handleIntoDir(row)">
{{
row &&
row?.prjDataPath &&
row?.prjDataPath.includes(activePath) &&
row?.prjDataPath.length > activePath.length
row?.prjDataPath &&
row?.prjDataPath.includes(activePath) &&
row?.prjDataPath.length > activePath.length
? row?.prjDataPath.replace(activePath, ' ').replace('/', ' ')
: row?.prjDataPath.replace('/', ' ')
}}
@@ -110,13 +83,8 @@
<vxe-column title="操作" width="120px" fixed="right">
<template #default="{ row }">
<el-button link size="small" type="danger" @click="handleDelDirOrFile(row)">删除</el-button>
<el-button
v-if="row.type == 'file'"
link
size="small"
type="primary"
@click="handleDownLoad(row)"
>
<el-button v-if="row.type == 'file'" link size="small" type="primary" :loading="row.loading"
@click="handleDownLoad(row)">
下载
</el-button>
</template>
@@ -127,13 +95,8 @@
<div class="list" v-if="dirList.length != 0 && !loading" style="display: none">
<div class="list_item" v-for="(item, index) in dirList" :key="index">
<div class="item_download">
<el-button
v-if="activePath && activePath != '/'"
type="danger"
size="small"
@click="handleDelDirOrFile(item)"
circle
>
<el-button v-if="activePath && activePath != '/'" type="danger" size="small"
@click="handleDelDirOrFile(item)" circle>
<el-icon>
<Delete />
</el-icon>
@@ -145,19 +108,15 @@
</el-button>
</div>
<img v-if="item?.type == 'dir'" @click="handleIntoDir(item)" src="@/assets/img/wenjianjia.svg" />
<img
class="img_file"
@click="handleIntoDir(item)"
v-if="item?.type == 'file'"
src="@/assets/img/wenjian.svg"
/>
<img class="img_file" @click="handleIntoDir(item)" v-if="item?.type == 'file'"
src="@/assets/img/wenjian.svg" />
<!-- <span v-if="!item.type">暂无数据</span> -->
<p>
{{
item &&
item?.prjDataPath &&
item?.prjDataPath.includes(activePath) &&
item?.prjDataPath.length > activePath.length
item?.prjDataPath &&
item?.prjDataPath.includes(activePath) &&
item?.prjDataPath.length > activePath.length
? item?.prjDataPath.replace(activePath, ' ').replace('/', ' ')
: item?.prjDataPath.replace('/', ' ')
}}
@@ -167,18 +126,9 @@
<!-- <el-empty v-if="dirList.length === 0" /> -->
</div>
<popup ref="fileRef"></popup>
<el-dialog
v-model.trim="addDeviceDirOpen"
:destroy-on-close="true"
title="新建文件夹目录"
width="500"
@closed="close"
>
<el-form
ref="formRef"
:model="form"
:rules="{ path: [{ required: true, message: '请输入文件夹名称', trigger: 'blur' }] }"
>
<el-dialog v-model.trim="addDeviceDirOpen" :destroy-on-close="true" title="新建文件夹目录" width="500" @closed="close">
<el-form ref="formRef" :model="form"
:rules="{ path: [{ required: true, message: '请输入文件夹名称', trigger: 'blur' }] }">
<el-form-item label="文件夹名称" prop="path">
<el-input maxlength="32" show-word-limit v-model.trim="form.path" placeholder="请输入文件夹名称" />
</el-form-item>
@@ -235,6 +185,7 @@ const isRoot = ref<boolean>(true)
const activePathList: any = ref([])
const devConType = ref<string>('')
const deviceTypeChange = (val: any, obj: any) => {
nodeClick(obj)
}
const nodeClick = (e: any) => {
@@ -650,15 +601,19 @@ const changeType = ref<any>('')
const fileRef = ref()
const handleDownLoad = async (row: any) => {
if (devConType.value == 'CLD') {
row.loading = true
ElMessage.info('下载中,请稍等...')
downloadFileFromFrontr({
devId: devId.value,
filePath: row.prjDataPath
}).then(res => {
downLoadFile(row.name, row.name, res)
row.loading = false
}).catch(e => {
row.loading = false
})
} else {
;(await nDid.value) && fileRef.value && fileRef.value.open(row, nDid.value)
; (await nDid.value) && fileRef.value && fileRef.value.open(row, nDid.value)
// fileName.value = row?.prjDataPath.split('/')[row?.prjDataPath.split('/').length - 1]
// localStorage.setItem('fileName', fileName.value)
changeType.value = 'download'

View File

@@ -31,7 +31,7 @@
</el-descriptions-item>
<el-descriptions-item label="接入方式">
{{ deviceData.devAccessMethod || '/' }}
{{ deviceData.devAccessMethod =='CLD'?'1056协议':deviceData.devAccessMethod || '/' }}
</el-descriptions-item>
<el-descriptions-item label="网络设备ID">
{{ deviceData.ndid || '/' }}
@@ -75,7 +75,7 @@
style="width: 100%"
>
<vxe-column type="seq" title="序号" width="80"></vxe-column>
<vxe-column field="name" title="数据名称"></vxe-column>
<vxe-column field="name" title="指标名称"></vxe-column>
<vxe-column field="phasic" title="相别"></vxe-column>
<vxe-column field="type" title="数据类型"></vxe-column>
<vxe-column field="unit" title="单位"></vxe-column>

View File

@@ -1,154 +1,154 @@
<template>
<el-dialog class="cn-operate-dialog device-manage-popup" v-model.trim="dialogVisible" title="设备模版分组">
<el-descriptions class="mb10" :column="3" border>
<el-descriptions-item label="名称">
{{ popupData.deviceData.name }}
</el-descriptions-item>
<el-descriptions-item label="网络设备ID">
{{ popupData.deviceData.ndid }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions :title="'数据集名称: ' + popupData.dataSetName" size="small" :column="2" border>
<template #extra>
<div v-if="newGroupVisible" style="display: flex; align-items: center">
<el-input maxlength="32" show-word-limit v-model.trim="groupName" autocomplete="off" clearable
placeholder="请输入分组名称"></el-input>
<el-button class="ml10" ctype="primary" size="small" @click="newGroupVisible = false">取消</el-button>
<el-button class="ml10" type="primary" size="small" @click="addNewGroup">完成</el-button>
</div>
<template v-else>
<el-button type="primary" size="small" @click="selectGroup">
{{ selectAll ? '取消全选' : '全选' }}
</el-button>
<el-button type="primary" size="small" @click="newGroupVisible = true">新增分组</el-button>
</template>
</template>
</el-descriptions>
<div style="flex: 1; overflow: auto">
<el-tree :data="popupData.tree" node-key="id" draggable :allow-drop="allowDrop" :allow-drag="allowDrag">
<template #default="{ node, data }">
<div class="group-tree-node">
<span>{{ node.label }}</span>
<div v-if="node.level == 1" style="display: flex; align-items: center">
<el-popconfirm :title="'确定删除' + node.label + '吗?'" @confirm="deleteGroup(node)">
<template #reference>
<Icon name="el-icon-Delete"></Icon>
</template>
</el-popconfirm>
<el-checkbox v-model.trim="data.isShow" @click.stop.native="" class="ml5"></el-checkbox>
</div>
</div>
</template>
</el-tree>
</div>
<template #footer="">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submit"> </el-button>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { updateGroArr } from '@/api/cs-device-boot/csGroArr'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type { AllowDropType } from 'element-plus/es/components/tree/src/tree.type'
const newGroupVisible = ref(false)
const dialogVisible = ref(false)
const selectAll = ref(false)
const popupData = ref<any>([])
const groupName = ref('')
const submit = () => {
let data = JSON.parse(JSON.stringify(popupData.value.tree))
const call = (data: any) => {
data.forEach((item: any) => {
item.isShow = item.isShow ? 1 : 0
delete item.label
if (item.children && item.children.length > 0) {
call(item.children)
}
})
}
call(data)
data.forEach((item: any) => {
item.children = item.children.map((item: any) => item.id)
})
updateGroArr({
setId: popupData.value.dataSet,
data: data
}).then((res: any) => {
ElMessage.success('保存成功')
dialogVisible.value = false
})
}
const deleteGroup = (node: any) => {
if (node.data.children && node.data.children.length > 0) {
ElMessage.error('该分组下有指标,不能删除')
return
}
let index = popupData.value.tree.findIndex((item: any) => item.id === node.data.id || item.name === node.data.name)
popupData.value.tree.splice(index, 1)
}
const selectGroup = () => {
if (selectAll.value) {
popupData.value.tree.forEach((item: any) => {
item.isShow = false
})
selectAll.value = false
} else {
popupData.value.tree.forEach((item: any) => {
item.isShow = true
})
selectAll.value = true
}
}
const addNewGroup = () => {
if (groupName.value === '') {
ElMessage.warning('请输入分组名称')
return
}
if (popupData.value.tree.some((item: any) => item.name === groupName.value)) {
ElMessage.warning('分组名称已存在')
return
}
newGroupVisible.value = false
popupData.value.tree.splice(0, 0, {
id: '',
label: groupName.value,
name: groupName.value,
isShow: true,
children: []
})
groupName.value = ''
}
const allowDrop = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {
return (dropNode.level === 2 && type !== 'inner') || (dropNode.level === 1 && type === 'inner')
}
const allowDrag = (draggingNode: Node) => {
return draggingNode.level === 2
}
const open = (data: any[]) => {
dialogVisible.value = true
popupData.value = data
}
defineExpose({ open })
</script>
<style lang="scss">
.device-manage-popup {
.group-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-dialog__body {
display: flex;
flex-direction: column;
}
}
</style>
<template>
<el-dialog class="cn-operate-dialog device-manage-popup" v-model.trim="dialogVisible" title="设备模版分组">
<el-descriptions class="mb10" :column="3" border>
<el-descriptions-item label="名称">
{{ popupData.deviceData.name }}
</el-descriptions-item>
<el-descriptions-item label="网络设备ID">
{{ popupData.deviceData.ndid }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions :title="'数据集名称: ' + popupData.dataSetName" size="small" :column="2" border>
<template #extra>
<div v-if="newGroupVisible" style="display: flex; align-items: center">
<el-input maxlength="32" show-word-limit v-model.trim="groupName" autocomplete="off" clearable
placeholder="请输入分组名称"></el-input>
<el-button class="ml10" ctype="primary" size="small" @click="newGroupVisible = false">取消</el-button>
<el-button class="ml10" type="primary" size="small" @click="addNewGroup">完成</el-button>
</div>
<template v-else>
<el-button type="primary" size="small" @click="selectGroup">
{{ selectAll ? '取消全选' : '全选' }}
</el-button>
<el-button type="primary" size="small" @click="newGroupVisible = true">新增分组</el-button>
</template>
</template>
</el-descriptions>
<div style="flex: 1; overflow: auto">
<el-tree :data="popupData.tree" node-key="id" draggable :allow-drop="allowDrop" :allow-drag="allowDrag">
<template #default="{ node, data }">
<div class="group-tree-node">
<span>{{ node.label }}</span>
<div v-if="node.level == 1" style="display: flex; align-items: center">
<el-popconfirm :title="'确定删除' + node.label + '吗?'" @confirm="deleteGroup(node)">
<template #reference>
<Icon name="el-icon-Delete"></Icon>
</template>
</el-popconfirm>
<el-checkbox v-model.trim="data.isShow" @click.stop.native="" class="ml5"></el-checkbox>
</div>
</div>
</template>
</el-tree>
</div>
<template #footer="">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="submit"> </el-button>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { updateGroArr } from '@/api/cs-device-boot/csGroArr'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type { AllowDropType } from 'element-plus/es/components/tree/src/tree.type'
const newGroupVisible = ref(false)
const dialogVisible = ref(false)
const selectAll = ref(false)
const popupData = ref<any>([])
const groupName = ref('')
const submit = () => {
let data = JSON.parse(JSON.stringify(popupData.value.tree))
const call = (data: any) => {
data.forEach((item: any) => {
item.isShow = item.isShow ? 1 : 0
delete item.label
if (item.children && item.children.length > 0) {
call(item.children)
}
})
}
call(data)
data.forEach((item: any) => {
item.children = item.children.map((item: any) => item.id)
})
updateGroArr({
setId: popupData.value.dataSet,
data: data
}).then((res: any) => {
ElMessage.success('保存成功')
dialogVisible.value = false
})
}
const deleteGroup = (node: any) => {
if (node.data.children && node.data.children.length > 0) {
ElMessage.error('该分组下有指标,不能删除')
return
}
let index = popupData.value.tree.findIndex((item: any) => item.id === node.data.id || item.name === node.data.name)
popupData.value.tree.splice(index, 1)
}
const selectGroup = () => {
if (selectAll.value) {
popupData.value.tree.forEach((item: any) => {
item.isShow = false
})
selectAll.value = false
} else {
popupData.value.tree.forEach((item: any) => {
item.isShow = true
})
selectAll.value = true
}
}
const addNewGroup = () => {
if (groupName.value === '') {
ElMessage.warning('请输入分组名称')
return
}
if (popupData.value.tree.some((item: any) => item.name === groupName.value)) {
ElMessage.warning('分组名称已存在')
return
}
newGroupVisible.value = false
popupData.value.tree.splice(0, 0, {
id: '',
label: groupName.value,
name: groupName.value,
isShow: true,
children: []
})
groupName.value = ''
}
const allowDrop = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {
return (dropNode.level === 2 && type !== 'inner') || (dropNode.level === 1 && type === 'inner')
}
const allowDrag = (draggingNode: Node) => {
return draggingNode.level === 2
}
const open = (data: any[]) => {
dialogVisible.value = true
popupData.value = data
}
defineExpose({ open })
</script>
<style lang="scss">
.device-manage-popup {
.group-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-dialog__body {
display: flex;
flex-direction: column;
}
}
</style>

View File

@@ -62,9 +62,9 @@
<div class="dialog-section">
<div class="section-header">
<span>工程列表</span>
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="filterText"
placeholder="搜索工程"
clearable
@@ -94,9 +94,9 @@
<div class="dialog-section">
<div class="section-header">
<span>设备列表</span>
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="deviceFilterText"
placeholder="搜索设备"
clearable
@@ -154,7 +154,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
const pageHeight = mainHeight(60)
const pageHeight1 = mainHeight(125)
const loading = ref(true)
const loading = ref(false)
const user: any = ref({})
const tableData = ref([])

View File

@@ -5,10 +5,10 @@
<el-form :model="form" scroll-to-error label-width="140px" :rules="rules1" ref="ruleFormRef1" class="form-one"
v-if="popupType == 0 || popupType == 1">
<el-form-item label="方案名称:" prop="itemName">
<el-input maxlength="32" show-word-limit v-model.trim="form.itemName" placeholder="请输入方案名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.itemName" placeholder="请输入方案名称" />
</el-form-item>
<el-form-item label="方案描述:" prop="describe">
<el-input maxlength="300" show-word-limit type="textarea" v-model.trim="form.describe"
<el-input show-word-limit maxlength="300" type="textarea" v-model.trim="form.describe"
placeholder="请输入方案描述" />
</el-form-item>
</el-form>
@@ -18,7 +18,7 @@
<el-form :model="form1" ref="ruleFormRef2" scroll-to-error class="form-two" label-width="140px"
:rules="rules2">
<el-form-item label="测试项名称:" prop="itemName">
<el-input maxlength="32" show-word-limit v-model.trim="form1.itemName" placeholder="请输入测试项名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form1.itemName" placeholder="请输入测试项名称" />
</el-form-item>
<el-form-item label="测量间隔:" prop="statisticalInterval">
<el-select v-model.trim="form1.statisticalInterval" placeholder="请选择测量间隔" clearable
@@ -41,7 +41,7 @@
</el-select>
</el-form-item>
<el-form-item label="最小短路容量:" prop="capacitySscmin">
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySscmin" oninput="value=value.replace(/[^\-?\d.]/g,'')
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySscmin" oninput="value=value.replace(/[^\-?\d.]/g,'')
.replace(/^\./g,'').replace('.','$#$').replace(/\./g,'').replace('$#$','.')
.replace('-','$#$').replace(/\-/g,'').replace('$#$','-')" autocomplete="off" placeholder="请选择最小短路容量">
<template #append>MVA</template>
@@ -49,7 +49,7 @@
</el-form-item>
<el-form-item label="用户协议容量:" prop="capacitySi">
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySi" autocomplete="off"
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySi" autocomplete="off"
oninput="value=value.replace(/[^\-?\d.]/g,'')
.replace(/^\./g,'').replace('.','$#$').replace(/\./g,'').replace('$#$','.')
.replace('-','$#$').replace(/\-/g,'').replace('$#$','-')" placeholder="请输入用户协议容量">
@@ -58,7 +58,7 @@
</el-form-item>
<el-form-item label="基准短路容量:" prop="capacitySscb">
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySscb" oninput="value=value.replace(/[^\-?\d.]/g,'')
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySscb" oninput="value=value.replace(/[^\-?\d.]/g,'')
.replace(/^\./g,'').replace('.','$#$').replace(/\./g,'').replace('$#$','.')
.replace('-','$#$').replace(/\-/g,'').replace('$#$','-')" placeholder="请输入基准短路容量">
<template #append>MVA</template>
@@ -66,26 +66,26 @@
</el-form-item>
<el-form-item label="供电设备容量:" prop="capacitySt">
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySt" oninput="value=value.replace(/[^\-?\d.]/g,'')
<el-input maxlength="32" show-word-limit v-model.trim="form1.capacitySt" oninput="value=value.replace(/[^\-?\d.]/g,'')
.replace(/^\./g,'').replace('.','$#$').replace(/\./g,'').replace('$#$','.')
.replace('-','$#$').replace(/\-/g,'').replace('$#$','-')" placeholder="请输入供电设备容量">
<template #append>MVA</template>
</el-input>
</el-form-item>
<el-form-item label="PT变比" prop="pt">
<el-input maxlength="32" show-word-limit style="width: 48%" v-model.number.trim="form1.pt"
<el-input maxlength="32" show-word-limit style="width: 48%" v-model.number.trim="form1.pt"
autocomplete="off" placeholder="请输入PT变比" oninput="value=value.replace(/[^0-9.]/g,'')" />
<el-input maxlength="32" show-word-limit style="width: 48%" v-model.number.trim="form1.pt1"
<el-input maxlength="32" show-word-limit style="width: 48%" v-model.number.trim="form1.pt1"
autocomplete="off" placeholder="请输入PT变比" oninput="value=value.replace(/[^0-9.]/g,'')" />
</el-form-item>
<el-form-item label="CT变比" prop="ct">
<el-input maxlength="32" show-word-limit v-model.number.trim="form1.ct" style="width: 48%"
<el-input maxlength="32" show-word-limit v-model.number.trim="form1.ct" style="width: 48%"
autocomplete="off" oninput="value=value.replace(/[^0-9.]/g,'')" placeholder="请输入CT变比" />
<el-input maxlength="32" show-word-limit v-model.number.trim="form1.ct1" style="width: 48%"
<el-input maxlength="32" show-word-limit v-model.number.trim="form1.ct1" style="width: 48%"
autocomplete="off" oninput="value=value.replace(/[^0-9.]/g,'')" placeholder="请输入CT变比" />
</el-form-item>
<el-form-item label="监测位置:" prop="location" style="width: 100%">
<el-input maxlength="300" show-word-limit type="textarea" v-model.trim="form1.location"
<el-input show-word-limit maxlength="300" type="textarea" v-model.trim="form1.location"
placeholder="请输入监测位置" />
</el-form-item>
</el-form>

View File

@@ -6,7 +6,7 @@
style="cursor: pointer" />
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }">
<div style="display: flex; align-items: center" class="mb10">
<el-input maxlength="32" v-model.trim="filterText" placeholder="请输入内容" clearable>
<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>

View File

@@ -1,6 +1,8 @@
<template>
<div>
<Table ref="tableRef" v-show="!isWaveCharts" />
<div v-show="!isWaveCharts">
<Table ref="tableRef" />
</div>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" />
</div>
@@ -22,11 +24,15 @@ import waveFormAnalysis from '@/views/govern/device/control/tabs/components/wave
import { analyseWave } from '@/api/common'
import { mainHeight } from '@/utils/layout'
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
import { useDictData } from '@/stores/dictData'
const props = defineProps({
activeName: String,
activeColName: [Object, String]
})
const loading = ref(false)
const dictData = useDictData()
const ReasonList: any = dictData.getBasicData('Event_Reason')
const EventTypeList: any = dictData.getBasicData('Event_Type')
const waveFormAnalysisRef = ref()
const isWaveCharts = ref(false)
const boxoList: any = ref([])
@@ -44,11 +50,11 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'startTime', title: '暂降发生时刻', minWidth: 170, sortable: true },
{ field: 'startTime', title: '发生时刻', minWidth: 170, sortable: true },
{
field: 'featureAmplitude',
title: '暂降(骤升)幅值(%)',
minWidth: 160,
title: '暂降幅值(%)',
minWidth: 120,
formatter: (row: any) => {
row.cellValue = row.cellValue + '' ? row.cellValue.toFixed(2) : '/'
if (String(row.cellValue).split('.')[1] == '00') {
@@ -78,6 +84,25 @@ const tableStore = new TableStore({
{ field: 'showName', title: '触发类型', minWidth: 170 },
{
title: '暂降原因', field: 'advanceReason', minWidth: 100, align: 'center', formatter: (row: any) => {
return ReasonList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{
title: '暂降类型', field: 'advanceType', minWidth: 100, align: 'center', formatter: (row: any) => {
return EventTypeList.find((item: any) => item.value == row.cellValue)?.label || '未知'
}
},
{ title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
{ title: '电压等级(kV)', field: 'lineVoltage', minWidth: 120, align: 'center', sortable: true, },
{ title: '设备名称', field: 'devName', minWidth: 120, align: 'center' },
{
title: '发生位置', field: 'sagSource', minWidth: 120, align: 'center', formatter: (row: any) => {
return row.cellValue == 1 ? '上游' : row.cellValue == 2 ? '下游' : '未知'
}
},
{
title: '操作', fixed: 'right',
width: 180,
@@ -105,6 +130,8 @@ const tableStore = new TableStore({
row.loading1 = false
if (res != undefined) {
boxoList.value = row
boxoList.value.featureAmplitude =
row.featureAmplitude != '-' ? (row.featureAmplitude - 0) / 100 : null
boxoList.value.systemType = 'YPT'
wp.value = res.data
@@ -145,6 +172,7 @@ const tableStore = new TableStore({
return !row.wavePath
},
click: row => {
row.loading2 = true
ElMessage.info('下载中......')
getFileZip({ eventId: row.id }).then(res => {
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
@@ -156,6 +184,9 @@ const tableStore = new TableStore({
link.click() //执行下载
document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
row.loading2 = false
}).catch(() => {
row.loading2 = false
})
}

View File

@@ -1,291 +1,292 @@
<template>
<div class="transferTreeBox" v-if="fromData.length != 0 || toData.length != 0">
<!-- 左侧待选内容 -->
<div class="SelectBox">
<div class="boxTitle" @click="clickAllSelect">未绑定数据</div>
<div class="boxCenter">
<el-input maxlength="32" show-word-limit v-model.trim="filterText" :suffix-icon="Search"
style="width: 100%" placeholder="请输入搜索内容" clearable></el-input>
<el-tree ref="leftTree" default-expand-all :data="leftData" :props="defaultProps" show-checkbox
node-key="id" :filter-node-method="filterNode">
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
v-if="data.icon" />
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
<!-- 中间穿梭按钮 -->
<div class="transferBtn">
<el-button type="primary" @click="towardsRight">
绑定
<el-icon>
<ArrowRight />
</el-icon>
</el-button>
<el-button type="primary" @click="towardsLeft">
<el-icon>
<ArrowLeft />
</el-icon>
解绑</el-button>
</div>
<!-- 右侧已选内容 -->
<div class="SelectBox">
<div class="boxTitle" @click="clickCancelAllSelect">已绑定数据</div>
<div class="boxCenter">
<el-input maxlength="32" show-word-limit v-model.trim="filterText1" :suffix-icon="Search"
style="width: 100%" placeholder="请输入搜索内容" clearable></el-input>
<el-tree ref="rightTree" default-expand-all :data="rightData" :props="defaultProps" show-checkbox
node-key="id" :filter-node-method="filterNode">
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
v-if="data.icon" />
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
</div>
</template>
<script setup>
import { Search, ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
</script>
<script>
import { useConfig } from '@/stores/config'
export default {
layout: 'login',
props: ['fromData', 'toData'],
data() {
return {
defaultProps: {
children: 'children',
label: 'name'
},
leftData: [],
rightData: [],
filterText: '',
filterText1: ''
}
},
watch: {
fromData(val, oldVal) {
if (val) {
this.leftData = val
const config = useConfig()
this.leftData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
})
})
})
}
},
toData(val, oldVal) {
if (val) {
this.rightData = val
const config = useConfig()
this.$emit('getData', this.rightData)
this.rightData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
})
})
})
}
},
filterText(val) {
this.$refs.leftTree.filter(val)
},
filterText1(val) {
this.$refs.rightTree.filter(val)
}
},
mounted() {
this.$forceUpdate()
this.$emit('getData', this.rightData)
},
methods: {
// 点击向右穿梭
towardsRight() {
// (leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数,
// 1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false
this.$nextTick(() => {
const checkedNodes = this.$refs.leftTree.getCheckedNodes(false, true) // 包含半选
const checkedKeys = this.$refs.leftTree.getCheckedKeys(false)
const copyNodes = JSON.parse(JSON.stringify(checkedNodes))
copyNodes.forEach(x => {
x.children = []
if (!this.$refs.rightTree.getNode(x.id)) {
this.$refs.rightTree.append(x, x.pid)
}
})
checkedKeys.forEach(x => {
this.$refs.leftTree.remove(x)
})
this.afterToward()
this.$forceUpdate()
})
},
// 点击向左穿梭
towardsLeft() {
this.$nextTick(() => {
const checkedNodes = this.$refs.rightTree.getCheckedNodes(false, true) // 包含半选
const checkedKeys = this.$refs.rightTree.getCheckedKeys(false)
const copyNodes = JSON.parse(JSON.stringify(checkedNodes))
copyNodes.forEach(x => {
x.children = []
if (!this.$refs.leftTree.getNode(x.id)) {
this.$refs.leftTree.append(x, x.pid)
}
})
checkedKeys.forEach(x => {
this.$refs.rightTree.remove(x)
})
this.$forceUpdate()
this.afterToward()
})
},
// 点击全选
clickAllSelect() {
return;
this.$refs.leftTree.setCheckedNodes(this.leftData)
this.towardsRight()
},
// 点击取消全选
clickCancelAllSelect() {
return;
this.$refs.rightTree.setCheckedNodes(this.rightData)
this.towardsLeft()
},
// 数据穿梭后
afterToward() {
this.$refs.leftTree.setCheckedKeys([])
this.$refs.rightTree.setCheckedKeys([])
this.$emit('getData', this.rightData)
this.$forceUpdate()
},
//组件搜索
filterNode(value, data, node) {
if (!value) return true
// return data.name.includes(value)
if (data.name) {
return this.chooseNode(value, data, node)
}
},
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配则返回该节点以及其下的所有子节点如果参数是子节点则返回该节点的父节点。name是中文字符enName是英文字符.
chooseNode(value, data, node) {
if (data.name.indexOf(value) !== -1) {
return true
}
const level = node.level
// 如果传入的节点本身就是一级节点就不用校验了
if (level === 1) {
return false
}
// 先取当前节点的父节点
let parentData = node.parent
// 遍历当前节点的父节点
let index = 0
while (index < level - 1) {
// 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤
if (parentData.data.name.indexOf(value) !== -1) {
return true
}
// 否则的话再往上一层做匹配
parentData = parentData.parent
index++
}
// 没匹配到返回false
return false
}
}
}
</script>
<style lang="scss" scoped>
.transferTreeBox {
display: flex;
width: 100%;
justify-content: space-around;
padding: 10px 10px 50px 10px;
.SelectBox {
height: 400px;
width: 40%;
color: #fff;
position: relative;
.boxTitle {
background: var(--el-color-primary);
height: 40px;
line-height: 40px;
text-align: center;
border-bottom: 2px solid #eee;
cursor: pointer;
border: 2px solid var(--el-color-primary);
}
.boxCenter {
height: 100%;
width: 100%;
overflow-y: scroll;
background: #fff;
padding: 0 10px;
border: 1px solid #eee;
.el-input {
margin: 10px 0;
}
}
}
.transferBtn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100px;
flex: 1;
.pickBtn {
height: 40px;
width: 40px;
background: var(--el-color-primary);
color: #fff;
font-weight: 700;
font-size: 20px;
border-radius: 5px;
margin-top: 10px;
text-align: center;
line-height: 40px;
cursor: pointer;
}
}
}
</style>
<template>
<div class="transferTreeBox" v-if="fromData.length != 0 || toData.length != 0">
<!-- 左侧待选内容 -->
<div class="SelectBox">
<div class="boxTitle" @click="clickAllSelect">未绑定数据</div>
<div class="boxCenter">
<el-input maxlength="32" show-word-limit v-model.trim="filterText" :suffix-icon="Search"
style="width: 100%" placeholder="请输入搜索内容" clearable></el-input>
<el-tree ref="leftTree" default-expand-all :data="leftData" :props="defaultProps" show-checkbox
node-key="id" :filter-node-method="filterNode">
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
v-if="data.icon" />
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
<!-- 中间穿梭按钮 -->
<div class="transferBtn">
<el-button type="primary" @click="towardsRight">
绑定
<el-icon>
<ArrowRight />
</el-icon>
</el-button>
<el-button type="primary" class="ml0 mt10" @click="towardsLeft">
<el-icon>
<ArrowLeft />
</el-icon>
解绑</el-button>
</div>
<!-- 右侧已选内容 -->
<div class="SelectBox">
<div class="boxTitle" @click="clickCancelAllSelect">已绑定数据</div>
<div class="boxCenter">
<el-input maxlength="32" show-word-limit v-model.trim="filterText1" :suffix-icon="Search"
style="width: 100%" placeholder="请输入搜索内容" clearable></el-input>
<el-tree ref="rightTree" default-expand-all :data="rightData" :props="defaultProps" show-checkbox
node-key="id" :filter-node-method="filterNode">
<template #default="{ node, data }">
<span class="custom-tree-node">
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
v-if="data.icon" />
<span style="margin-left: 4px">{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
</div>
</template>
<script setup>
import { Search, ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
</script>
<script>
import { useConfig } from '@/stores/config'
export default {
layout: 'login',
props: ['fromData', 'toData'],
data() {
return {
defaultProps: {
children: 'children',
label: 'name'
},
leftData: [],
rightData: [],
filterText: '',
filterText1: ''
}
},
watch: {
fromData(val, oldVal) {
if (val) {
this.leftData = val
const config = useConfig()
this.leftData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
})
})
})
}
},
toData(val, oldVal) {
if (val) {
this.rightData = val
const config = useConfig()
this.$emit('getData', this.rightData)
this.rightData.forEach(item => {
item.icon = 'el-icon-HomeFilled'
item.color = config.getColorVal('elementUiPrimary')
item.children.forEach(item2 => {
item2.icon = 'el-icon-List'
item2.color = config.getColorVal('elementUiPrimary')
item2.children.forEach(item3 => {
item3.icon = 'el-icon-Platform'
item3.color = config.getColorVal('elementUiPrimary')
if (item3.comFlag === 1) {
item3.color = '#e26257 !important'
}
})
})
})
}
},
filterText(val) {
this.$refs.leftTree.filter(val)
},
filterText1(val) {
this.$refs.rightTree.filter(val)
}
},
mounted() {
this.$forceUpdate()
this.$emit('getData', this.rightData)
},
methods: {
// 点击向右穿梭
towardsRight() {
// (leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数,
// 1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false
this.$nextTick(() => {
const checkedNodes = this.$refs.leftTree.getCheckedNodes(false, true) // 包含半选
const checkedKeys = this.$refs.leftTree.getCheckedKeys(false)
const copyNodes = JSON.parse(JSON.stringify(checkedNodes))
copyNodes.forEach(x => {
x.children = []
if (!this.$refs.rightTree.getNode(x.id)) {
this.$refs.rightTree.append(x, x.pid)
}
})
checkedKeys.forEach(x => {
this.$refs.leftTree.remove(x)
})
this.afterToward()
this.$forceUpdate()
})
},
// 点击向左穿梭
towardsLeft() {
this.$nextTick(() => {
const checkedNodes = this.$refs.rightTree.getCheckedNodes(false, true) // 包含半选
const checkedKeys = this.$refs.rightTree.getCheckedKeys(false)
const copyNodes = JSON.parse(JSON.stringify(checkedNodes))
copyNodes.forEach(x => {
x.children = []
if (!this.$refs.leftTree.getNode(x.id)) {
this.$refs.leftTree.append(x, x.pid)
}
})
checkedKeys.forEach(x => {
this.$refs.rightTree.remove(x)
})
this.$forceUpdate()
this.afterToward()
})
},
// 点击全选
clickAllSelect() {
return;
this.$refs.leftTree.setCheckedNodes(this.leftData)
this.towardsRight()
},
// 点击取消全选
clickCancelAllSelect() {
return;
this.$refs.rightTree.setCheckedNodes(this.rightData)
this.towardsLeft()
},
// 数据穿梭后
afterToward() {
this.$refs.leftTree.setCheckedKeys([])
this.$refs.rightTree.setCheckedKeys([])
this.$emit('getData', this.rightData)
this.$forceUpdate()
},
//组件搜索
filterNode(value, data, node) {
if (!value) return true
// return data.name.includes(value)
if (data.name) {
return this.chooseNode(value, data, node)
}
},
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配则返回该节点以及其下的所有子节点如果参数是子节点则返回该节点的父节点。name是中文字符enName是英文字符.
chooseNode(value, data, node) {
if (data.name.indexOf(value) !== -1) {
return true
}
const level = node.level
// 如果传入的节点本身就是一级节点就不用校验了
if (level === 1) {
return false
}
// 先取当前节点的父节点
let parentData = node.parent
// 遍历当前节点的父节点
let index = 0
while (index < level - 1) {
// 如果匹配到直接返回此处name值是中文字符enName是英文字符。判断匹配中英文过滤
if (parentData.data.name.indexOf(value) !== -1) {
return true
}
// 否则的话再往上一层做匹配
parentData = parentData.parent
index++
}
// 没匹配到返回false
return false
}
}
}
</script>
<style lang="scss" scoped>
.transferTreeBox {
display: flex;
width: 100%;
justify-content: space-around;
padding: 10px 10px 50px 10px;
.SelectBox {
height: 400px;
width: 43%;
color: #fff;
position: relative;
.boxTitle {
background: var(--el-color-primary);
height: 40px;
line-height: 40px;
text-align: center;
border-bottom: 2px solid #eee;
cursor: pointer;
border: 2px solid var(--el-color-primary);
}
.boxCenter {
height: 100%;
width: 100%;
overflow-y: scroll;
background: #fff;
padding: 0 10px;
border: 1px solid #eee;
.el-input {
margin: 10px 0;
}
}
}
.transferBtn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
// width: 100px;
flex-direction: column;
flex: 1;
.pickBtn {
height: 40px;
width: 40px;
background: var(--el-color-primary);
color: #fff;
font-weight: 700;
font-size: 20px;
border-radius: 5px;
margin-top: 10px;
text-align: center;
line-height: 40px;
cursor: pointer;
}
}
}
</style>

View File

@@ -1,14 +1,8 @@
<template>
<div class="default-main device-manage" :style="{ height: pageHeight.height }">
<!-- @node-change="nodeClick" -->
<schemeTree
@node-change="nodeClick"
@node-click="nodeClick"
@init="nodeClick"
@onAdd="onAdd"
@bind="bind"
ref="schemeTreeRef"
></schemeTree>
<schemeTree @node-change="nodeClick" @node-click="nodeClick" @init="nodeClick" @onAdd="onAdd" @bind="bind"
ref="schemeTreeRef"></schemeTree>
<div class="device-manage-right" v-if="deviceData">
<el-descriptions title="方案信息" :column="2" border>
<!-- <template #extra>
@@ -28,12 +22,8 @@
<p>测试项信息</p>
</div> -->
<el-tabs v-model.trim="activeName" type="border-card" @tab-change="handleClickTabs">
<el-tab-pane
v-for="(item, index) in deviceData?.records"
:label="item.itemName"
:name="item.id"
:key="index"
>
<el-tab-pane v-for="(item, index) in deviceData?.records" :label="item.itemName"
:name="item.id" :key="index">
<template #label>
<span class="custom-tabs-label">
<el-icon>
@@ -76,19 +66,23 @@
{{ item.capacitySt }}MVA
</el-descriptions-item>
<el-descriptions-item label="PT变比" width="160">
{{ item.pt && item.pt1 ? item.pt + '/' + item.pt1 : '/' }}
<!-- {{ item.pt && item.pt1 ? item.pt + '/' + item.pt1 : '/' }} -->
{{ item.pt || 1 }} :
{{ item.pt1 || 1 }}
</el-descriptions-item>
<el-descriptions-item label="CT变比" width="160">
{{ item.ct && item.ct1 ? item.ct + '/' + item.ct1 : '/' }}
<!-- {{ item.ct && item.ct1 ? item.ct + '/' + item.ct1 : '/' }} -->
{{ item.ct || 1 }} :
{{ item.ct1 || 1 }}
</el-descriptions-item>
<el-descriptions-item label="起始时间" width="160">
<span style="width: 140px; overflow: hidden; display: block">
{{ item.startTime ||'/' }}
{{ item.startTime || '/' }}
</span>
</el-descriptions-item>
<el-descriptions-item label="结束时间" width="160">
<span style="width: 140px; overflow: hidden; display: block">
{{ item.endTime||'/' }}
{{ item.endTime || '/' }}
</span>
</el-descriptions-item>
<el-descriptions-item label="监测位置" width="160">
@@ -119,23 +113,12 @@
<TableHeader :showSearch="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select :key="num">
<el-form-item for="-" label="统计指标">
<el-select
style="min-width: 240px"
filterable
collapse-tags
collapse-tags-tooltip
v-model.trim="searchForm.index"
placeholder="请选择统计指标"
@change="onIndexChange($event)"
multiple
:multiple-limit="3"
>
<el-option
v-for="item in indexOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-select style="min-width: 240px" filterable collapse-tags
collapse-tags-tooltip v-model.trim="searchForm.index"
placeholder="请选择统计指标" @change="onIndexChange($event)" multiple
:multiple-limit="3">
<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>
@@ -149,50 +132,29 @@
</el-radio-group>
</el-form-item>
<el-form-item for="-" label="统计类型" label-width="80px">
<el-select
style="width: 120px"
v-model.trim="searchForm.type"
placeholder="请选择值类型"
>
<el-option
v-for="item in typeOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-select style="width: 120px" v-model.trim="searchForm.type"
placeholder="请选择值类型">
<el-option v-for="item in typeOptions" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<div
for="-"
v-for="(item, index) in countData"
:key="index"
v-show="item.countOptions.length != 0"
>
<div for="-" v-for="(item, index) in countData" :key="index"
v-show="item.countOptions.length != 0">
<span class="mr12">
{{
item.name.includes('次数')
? item.name
: item.name.includes('幅值')
? item.name.slice(0, -2) + '次数'
: item.name + '谐波次数'
? item.name.slice(0, -2) + '次数'
: item.name + '谐波次数'
}}
</span>
<el-select
v-model.trim="item.count"
class="mr20"
collapse-tags
collapse-tags-tooltip
placeholder="请选择谐波次数"
style="width: 120px"
>
<el-option
v-for="vv in item.countOptions"
:key="vv"
:label="vv"
:value="vv"
></el-option>
<el-select v-model.trim="item.count" class="mr20" collapse-tags
collapse-tags-tooltip placeholder="请选择谐波次数" style="width: 120px">
<el-option v-for="vv in item.countOptions" :key="vv" :label="vv"
:value="vv"></el-option>
</el-select>
</div>
</el-form-item>
@@ -217,22 +179,13 @@
<!-- <div class="history_title">
<p>{{ chartTitle }}</p>
</div> -->
<div
class="history_chart mt5"
v-loading="loading"
:style="EcharHeight"
:key="EcharHeight.height"
ref="chartRef"
>
<MyEchart
ref="historyChart"
v-if="echartsData"
:isExport="true"
:options="echartsData"
/>
<div class="history_chart mt5" v-loading="loading" :style="EcharHeight"
:key="EcharHeight.height" ref="chartRef">
<MyEchart ref="historyChart" v-if="echartsData" :isExport="true"
:options="echartsData" />
</div>
</div>
<el-empty :style="EcharHeight" v-else description="未绑定数据" />
<el-empty :style="emptyHeight" v-else description="未绑定数据" />
</el-tab-pane>
<el-tab-pane label="暂态数据" name="1">
<transient :activeName="activeName" ref="transientRef" :activeColName="activeColName" />
@@ -286,6 +239,7 @@ const volConTypeList = dictData.getBasicData('Dev_Connect')
//值类型
const pageHeight = mainHeight(20)
const EcharHeight = ref(mainHeight(436))
const emptyHeight = ref(mainHeight(374))
const loading = ref(false)
const searchForm: any = ref({})
const typeOptions = [
@@ -714,9 +668,8 @@ const setEchart = () => {
} 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] ? el.value[1] + ' ' + (el.value[2] || '') : '-'
}<br>`
str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] ? el.value[1] + ' ' + (el.value[2] || '') : '-'
}<br>`
})
return str
}
@@ -770,10 +723,10 @@ const setEchart = () => {
return item.anotherName == '电压负序分量'
? '电压不平衡'
: item.anotherName == '电压正序分量'
? '电压不平衡'
: item.anotherName == '电压零序分量'
? '电压不平衡'
: item.anotherName
? '电压不平衡'
: item.anotherName == '电压零序分量'
? '电压不平衡'
: item.anotherName
})
)
]
@@ -806,10 +759,10 @@ const setEchart = () => {
(kk[0].anotherName == '电压负序分量'
? '电压不平衡'
: kk[0].anotherName == '电压正序分量'
? '电压不平衡'
: kk[0].anotherName == '电压零序分量'
? '电压不平衡'
: kk[0].anotherName)
? '电压不平衡'
: kk[0].anotherName == '电压零序分量'
? '电压不平衡'
: kk[0].anotherName)
)
let seriesList: any = []
kk.forEach((cc: any) => {

View File

@@ -24,7 +24,7 @@ import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { debounce } from 'lodash-es'
const pageHeight = mainHeight(60)
const loading = ref(true)
const loading = ref(false)
const defaultCheckedKeys: any = ref([])
const tableData = ref([])
const treeRef = ref(null)

View File

@@ -3,9 +3,9 @@
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="设备名称">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入设备名称"
/>

View File

@@ -17,7 +17,7 @@
</el-select>
</el-form-item>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
placeholder="数据名称、别名、展示名称" clearable></el-input>
</el-form-item>
</template>

View File

@@ -16,27 +16,27 @@
</el-select>
</el-form-item>
<el-form-item label="数据名称:" prop="name">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.name"
autocomplete="off"
placeholder="请输入数据名称"
></el-input>
</el-form-item>
<el-form-item label="别名:" prop="otherName">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.otherName"
autocomplete="off"
placeholder="请输入别名"
></el-input>
</el-form-item>
<el-form-item label="展示名称:" prop="showName">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.showName"
autocomplete="off"
placeholder="请输入展示名称"
@@ -53,18 +53,18 @@
</el-select>
</el-form-item>
<el-form-item label="单位:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.unit"
autocomplete="off"
placeholder="请输入单位"
></el-input>
</el-form-item>
<el-form-item label="基础数据类型:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.type"
autocomplete="off"
placeholder="请输入基础数据类型"
@@ -112,9 +112,9 @@
</el-select>
</el-form-item>
<el-form-item label="序号:" prop="sort">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="number"
v-model.trim="form.sort"
autocomplete="off"
@@ -122,9 +122,9 @@
></el-input>
</el-form-item>
<el-form-item label="限值名称:" prop="limitName">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="text"
v-model.trim="form.limitName"
autocomplete="off"
@@ -132,9 +132,9 @@
></el-input>
</el-form-item>
<el-form-item label="限值表名:" prop="limitTable">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="text"
v-model.trim="form.limitTable"
autocomplete="off"
@@ -157,18 +157,18 @@
</div>
<el-form class="form-two" :model="form" label-width="130px" ref="formRef2">
<el-form-item label="告警码(缺省值):">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.defaultValue"
autocomplete="off"
placeholder="请输入告警码(缺省值)"
></el-input>
</el-form-item>
<el-form-item label="事件类别:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.eventType"
autocomplete="off"
placeholder="请输入事件类别"
@@ -176,9 +176,9 @@
</el-form-item>
<el-form-item label="设置最大值:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="number"
v-model.trim="form.maxNum"
autocomplete="off"
@@ -186,9 +186,9 @@
></el-input>
</el-form-item>
<el-form-item label="设置最小值:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="number"
v-model.trim="form.minNum"
autocomplete="off"
@@ -196,18 +196,18 @@
></el-input>
</el-form-item>
<el-form-item label="枚举序列:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.setValue"
autocomplete="off"
placeholder="请输入枚举序列"
></el-input>
</el-form-item>
<el-form-item label="字符串长度上限:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
type="number"
v-model.trim="form.strlen"
autocomplete="off"
@@ -215,9 +215,9 @@
></el-input>
</el-form-item>
<el-form-item label="上送规则:">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.tranRule"
autocomplete="off"
placeholder="请输入上送规则"

View File

@@ -21,15 +21,15 @@
</el-select>
</el-form-item>
<el-form-item label="版本号:" prop="versionNo">
<el-input maxlength="32" show-word-limit v-model.trim="form.versionNo" autocomplete="off"
<el-input maxlength="32" show-word-limit v-model.trim="form.versionNo" autocomplete="off"
placeholder="请输入版本号"></el-input>
</el-form-item>
<el-form-item label="版本类型:" prop="versionType">
<el-input maxlength="32" show-word-limit v-model.trim="form.versionType" autocomplete="off"
<el-input maxlength="32" show-word-limit v-model.trim="form.versionType" autocomplete="off"
placeholder="填写特殊类型(不填默认通用类型)"></el-input>
</el-form-item>
<el-form-item label="协议版本:" prop="versionAgreement">
<el-input maxlength="32" show-word-limit v-model.trim="form.versionAgreement" autocomplete="off"
<el-input maxlength="32" show-word-limit v-model.trim="form.versionAgreement" autocomplete="off"
placeholder="请输入协议版本"></el-input>
</el-form-item>
<el-form-item label="版本日期:" prop="versionDate">
@@ -38,13 +38,13 @@
</el-form-item>
<el-form-item label="CRC校验:" prop="crc">
<el-input maxlength="32" show-word-limit v-model.trim="form.crc" autocomplete="off"
<el-input maxlength="32" show-word-limit v-model.trim="form.crc" autocomplete="off"
placeholder="请输入CRC校验"></el-input>
</el-form-item>
</el-form>
<el-form :model="form" label-width="100px" class="mt10 form-one">
<el-form-item label="描述:" prop="description">
<el-input maxlength="300" :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" show-word-limit
<el-input show-word-limit maxlength="300" :autosize="{ minRows: 2, maxRows: 4 }" type="textarea"
v-model.trim="form.description" autocomplete="off" placeholder="请输入描述"></el-input>
</el-form-item>
<el-form-item label="升级文件:" prop="file">

View File

@@ -3,9 +3,9 @@
<TableHeader ref="tableHeaderRef">
<template #select>
<el-form-item label="模版名称">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.name"
clearable
placeholder="请输入名称"
@@ -140,6 +140,7 @@ const chooseFile = (e: any) => {
}
// 下载报告
const downloadTheReport = (filePath: string,name:string) => {
ElMessage.info('下载中......')
downLoadFile(filePath).then(res => {
let blob = new Blob([res], {
type: ' application/json'

View File

@@ -179,6 +179,7 @@ queryByCode('Device_Type').then(res => {
})
// 下载报告
const downloadTheReport = (name: string) => {
ElMessage.info('下载中......')
getFileUrl(name).then((res: any) => {
downLoadFile(res.data).then(res => {
let blob = new Blob([res], {

View File

@@ -42,10 +42,10 @@
<el-dialog v-model="dialogVisible" title="编辑" width="500">
<el-form :model="form" ref="ruleFormRef" label-width="80px" :rules="rules">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" clearable placeholder="请输入名称" />
<el-input maxlength="32" show-word-limit v-model="form.name" clearable placeholder="请输入名称" />
</el-form-item>
<el-form-item label="区域" :prop="form.pid == '0' ? 'city' : 'area'">
<!-- <el-input v-model="form.city" clearable placeholder="请输入区域" /> -->
<!-- <el-input maxlength="32" show-word-limit v-model="form.city" clearable placeholder="请输入区域" /> -->
<el-cascader
v-model="form.city"
v-if="form.pid == '0'"
@@ -56,10 +56,10 @@
filterable
placeholder="请输入区域"
/>
<el-input v-else v-model="form.area" clearable placeholder="请输入区域" />
<el-input maxlength="32" show-word-limit v-else v-model="form.area" clearable placeholder="请输入区域" />
</el-form-item>
<el-form-item label="备注" prop="description">
<el-input v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>

View File

@@ -2,7 +2,7 @@
<el-dialog draggable class="cn-operate-dialog" width="540px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" ref="formRef" :model="form" label-width="auto" class="form-one" :rules="rules">
<el-form-item label="项目名称" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入项目名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入项目名称" />
</el-form-item>
<el-form-item label="所属工程" prop="engineeringId">
<el-select v-model="form.engineeringId" filterable placeholder="请选择工程" clearable>
@@ -15,13 +15,13 @@
</el-select>
</el-form-item>
<el-form-item label="区域" prop="area">
<el-input v-model="form.area" clearable placeholder="请输入区域" />
<el-input maxlength="32" show-word-limit v-model="form.area" clearable placeholder="请输入区域" />
</el-form-item>
<el-form-item label="备注" prop="description">
<el-input v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />
</el-form-item>
<el-form-item label="拓扑图">
<div class="image-radio-group">

View File

@@ -2,7 +2,7 @@
<el-dialog draggable class="cn-operate-dialog" width="500px" v-model.trim="dialogVisible" :title="title">
<el-form :inline="false" ref="formRef" :model="form" label-width="auto" class="form-one" :rules="rules">
<el-form-item label="工程名称" prop="name">
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入工程名称" />
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入工程名称" />
</el-form-item>
<el-form-item label="区域" prop="city">
<el-cascader
@@ -16,10 +16,10 @@
/>
</el-form-item>
<el-form-item label="备注" prop="description">
<el-input v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
<el-input maxlength="32" show-word-limit v-model="form.description" :rows="2" type="textarea" clearable placeholder="请输入备注" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
<el-input-number v-model.number="form.sort" style="width: 100%;" :min="0" />
</el-form-item>
</el-form>

View File

@@ -5,7 +5,7 @@
<div class="custom-table-header">
<div class="title">
工程列表
<el-input
<el-input maxlength="32" show-word-limit
class="ml10"
v-model="searchValue"
placeholder="请输入工程名称"

View File

@@ -3,7 +3,7 @@
<TableHeader>
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
style="width: 200px"
clearable
@@ -120,9 +120,9 @@
>
<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
<el-input maxlength="32" show-word-limit
v-model.trim="form.name"
autocomplete="off"
clearable
@@ -130,9 +130,9 @@
></el-input>
</el-form-item>
<el-form-item label="网络设备ID:" prop="ndid" class="top">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.ndid"
autocomplete="off"
placeholder="请输入"
@@ -174,11 +174,11 @@
/>
</el-form-item>
<el-form-item label="排序:" class="top" prop="sort">
<el-input maxlength="32" show-word-limit-number v-model.trim="form.sort" :min="0" />
<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-input maxlength="32" show-word-limit v-model.trim="form.cntractNo" autocomplete="off" placeholder="请输入"></el-input>
</el-form-item> -->
</el-form>
<template #footer>

View File

@@ -3,7 +3,7 @@
<TableHeader datePicker>
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
clearable placeholder="请输入关键字筛选" />
</el-form-item>
</template>

View File

@@ -3,7 +3,7 @@
<TableHeader datePicker ref="refheader">
<template v-slot:select>
<el-form-item label="关键字筛选">
<el-input
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.name"
placeholder="请输入关键字"
clearable

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog v-model="dialogVisible" :title="title" width="800" :close-on-click-modal="false"
<el-dialog draggable v-model="dialogVisible" :title="title" width="900" :close-on-click-modal="false"
:before-close="handleClose">
<!-- 表格区域 -->

View File

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

View File

@@ -3,7 +3,7 @@
<TableHeader ref="tableHeaderRef" showExport>
<template #select>
<el-form-item label="关键字筛选">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入设备名称"
<el-input maxlength="32" show-word-limit v-model="tableStore.table.params.searchValue" clearable placeholder="请输入设备名称"
style="width:200px" />
</el-form-item>
<el-form-item label="通讯状态">
@@ -84,7 +84,7 @@ const tableStore: any = new TableStore({
{ title: '协议版本', field: 'protocolVersion', minWidth: '100', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '版本日期', field: 'versionDate', minWidth: '150', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '设备型号', field: 'devModelName', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: 'icd模型', field: 'icd', minWidth: '120', formatter: (row: any) => { return icdList.value.filter(item => item.id == row.cellValue)[0]?.name || '/' } },
{ title: 'icd模型', field: 'icd', minWidth: '120', formatter: (row: any) => { return icdList.value.filter((item: any) => item.id == row.cellValue)[0]?.name || '/' } },
{ title: '所属工程', field: 'associatedEngineering', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{ title: '所属项目', field: 'associatedProject', minWidth: '120', formatter: (row: any) => { return row.cellValue || '/' } },
{

View File

@@ -9,9 +9,9 @@
>
<el-form ref="formRef" :rules="rules" :model="form" label-width="90px" class="form-one">
<el-form-item label="项目名称:" prop="name">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="form.name"
placeholder="请输入项目名称"
></el-input>
@@ -41,13 +41,13 @@
/>
</el-form-item>
<el-form-item label="项目排序:" prop="orderBy">
<el-input maxlength="32" show-word-limit-number v-model.number="form.orderBy" style="width: 100%" />
<el-input-number v-model.number="form.orderBy" style="width: 100%" />
</el-form-item>
<el-form-item label="备注:" class="top">
<el-input
<el-input show-word-limit
maxlength="300"
show-word-limit
type="textarea"
:rows="2"
placeholder="请输入内容"

View File

@@ -3,9 +3,9 @@
<TableHeader>
<template v-slot:select>
<el-form-item label="项目名称">
<el-input
maxlength="32"
show-word-limit
<el-input maxlength="32" show-word-limit
v-model.trim="tableStore.table.params.searchValue"
placeholder="请输入项目名称"
></el-input>

View File

@@ -1,16 +1,15 @@
<template>
<div class="default-main" :style="height">
<splitpanes style="height: 100%" id="navigation-splitpanes">
<pane :size="size">
<pointTree
ref="TerminalRef"
@node-click="handleNodeClick"
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</pane>
<pane style="background: #fff" :style="height">
<TableHeader ref="TableHeaderRef" date-picker :show-search="false">
<div class="default-main line-report-page" :style="height">
<div class="line-report-sidebar">
<pointTree
ref="TerminalRef"
@node-click="handleNodeClick"
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</div>
<div class="line-report-main">
<TableHeader ref="TableHeaderRef" date-picker :show-search="false">
<template v-slot:select>
<!-- <el-form-item label=" 模板策略">
<el-select v-model="value" placeholder="请选择" @change="changeFn" clearable>
@@ -25,7 +24,7 @@
</template>
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent" :loading="loading">
导出
报告生成
</el-button>
</template>
</TableHeader>
@@ -83,7 +82,7 @@
</div>
</el-col>
<el-col :span="12" class="mTop">
<el-checkbox v-model="formd.glfbfz">暂降(骤升)幅值</el-checkbox>
<el-checkbox v-model="formd.glfbfz">暂降幅值</el-checkbox>
<el-checkbox v-model="formd.glfbsj">持续时间</el-checkbox>
</el-col>
</el-row>
@@ -125,15 +124,12 @@
</el-row>
<el-divider></el-divider>
</div>
</pane>
</splitpanes>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import 'splitpanes/dist/splitpanes.css'
import { Splitpanes, Pane } from 'splitpanes'
import { ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
@@ -150,7 +146,6 @@ defineOptions({
})
const loading = ref(false)
const height = mainHeight(20)
const size = ref(0)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
@@ -184,13 +179,6 @@ const tableStore = new TableStore({
})
provide('tableStore', tableStore)
onMounted(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 100)
}
})
const pointTypeChange = (val: any, obj: any) => {
handleNodeClick(obj)
}
@@ -260,9 +248,29 @@ const exportEvent = () => {
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
.line-report-page {
display: flex;
overflow: hidden;
}
.line-report-sidebar {
width: 280px;
flex-shrink: 0;
min-height: 0;
overflow: hidden;
background: #eaeef1;
}
.line-report-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.grid-content {
text-align: center;
}
@@ -277,8 +285,7 @@ const exportEvent = () => {
}
}
.box {
padding: 10px;
// margin-top: 10px;
padding: 10px 0;
overflow-y: auto;
font-size: 15px;
}

View File

@@ -1,184 +1,11 @@
<template>
<div class="default-main" :style="height" style="display: flex; overflow: hidden">
<div style="width: 280px; flex-shrink: 0; height: 100%; overflow: hidden">
<pointTree
ref="TerminalRef"
@node-click="handleNodeClick"
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</div>
<div style="flex: 1; min-width: 0; background: #fff" :style="height">
<TableHeader ref="TableHeaderRef" datePicker :show-search="false">
<template v-slot:select>
<el-form-item label="客户名称">
<el-input
v-model="tableStore.table.params.crmName"
maxlength="32"
show-word-limit
clearable
placeholder="请输入客户名称"
/>
</el-form-item>
<el-form-item label="报表编号">
<el-input
v-model="tableStore.table.params.reportNumber"
clearable
placeholder="请输入报表编号"
maxlength="12"
show-word-limit
/>
</el-form-item>
</template>
<template #operation>
<el-upload
:show-file-list="false"
ref="uploadRef"
action=""
accept=".png,.jpg"
:on-change="choose"
:auto-upload="false"
>
<template #trigger>
<el-button icon="el-icon-Upload" type="primary" class="mr10 ml10">上传接线图</el-button>
</template>
</el-upload>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent" :loading="loading">
导出
</el-button>
</template>
</TableHeader>
<div class="box">
<div id="luckysheet">
<img
width="100%"
:style="`height: calc(${tableStore.table.height} + 40px)`"
src="@/assets/img/jss.png"
/>
</div>
</div>
</div>
</div>
<IndexYpt default-tab="report" />
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import IndexYpt from '../statisticsWx/index_ypt.vue'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { genFileId, ElMessage, ElNotification } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus'
import pointTree from '@/components/tree/govern/pointTree.vue'
import { exportModel } from '@/api/cs-harmonic-boot/datatrend'
import { isReportMonitorPoint } from '@/components/tree/govern/lineTreeUtils'
defineOptions({
name: 'harmonic-boot/report/word'
name: 'harmonic-boot/report/word',
})
const height = mainHeight(20)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
const Template: any = ref({})
const uploadList: any = ref([])
const loading = ref(false)
const tableStore = new TableStore({
url: '',
method: 'POST',
column: [],
beforeSearchFun: () => {},
loadCallback: () => {}
})
provide('tableStore', tableStore)
const pointTypeChange = (val: any, obj: any) => {
handleNodeClick(obj)
}
const handleNodeClick = (data: any) => {
if (isReportMonitorPoint(data)) {
dotList.value = data
}
}
// 上传
const choose = (files: any) => {
const isJPG = files.raw.type === 'image/jpg'
const isJPEG = files.raw.type === 'image/jpeg'
const isPNG = files.raw.type === 'image/png'
if (!isJPG && !isPNG && !isJPEG) {
ElMessage.warning('上传文件只能是 jpg/png 格式!')
return false
}
uploadList.value = files
ElMessage.success('上传成功')
}
// 生成
const exportEvent = () => {
console.log('🚀 ~ exportEvent ~ dotList.value:', dotList.value)
if (!isReportMonitorPoint(dotList.value)) {
return ElMessage.warning('请选择监测点进行报告生成!')
}
let form = new FormData()
form.append('lineIndex', dotList.value.id)
form.append('name', dotList.value.name)
form.append('crmName', tableStore.table.params.crmName || '')
form.append('reportNumber', tableStore.table.params.reportNumber || '')
form.append('type', '0')
form.append('startTime', TableHeaderRef.value.datePickerRef.timeValue[0])
form.append('endTime', TableHeaderRef.value.datePickerRef.timeValue[1])
console.log('🚀 ~ exportEvent ~ uploadList.value:', uploadList.value?.raw)
form.append('file', uploadList.value?.raw || '')
// 特殊字符正则表达式
const specialCharRegex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/
if (
specialCharRegex.test(tableStore.table.params.crmName) ||
specialCharRegex.test(tableStore.table.params.reportNumber)
) {
ElNotification({
type: 'error',
message: '包含特殊字符,请注意修改!'
})
} else {
loading.value = true
ElMessage('生成报告中...')
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')}`
exportModel(form)
.then((res: any) => {
if (res == undefined) {
loading.value = false
return
}
let blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8'
})
// createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a') // 创建a标签
link.href = url
link.download = dotList.value.name + formattedDate // 设置下载的文件名
document.body.appendChild(link)
link.click() //执行下载
document.body.removeChild(link)
loading.value = false
})
.catch(() => {
loading.value = false
})
}
}
</script>
<style lang="scss">
.box {
padding: 10px;
}
</style>

View File

@@ -0,0 +1,226 @@
<template>
<div class="report-word-panel">
<TableHeader ref="tableHeaderRef" datePicker :show-search="false">
<template #select>
<!-- <el-form-item label="客户名称">
<el-input v-model="tableStore.table.params.crmName" maxlength="32" show-word-limit clearable
placeholder="请输入客户名称" />
</el-form-item>
<el-form-item label="报表编号">
<el-input v-model="tableStore.table.params.reportNumber" maxlength="32" show-word-limit clearable
placeholder="请输入报表编号" />
</el-form-item> -->
</template>
<template #operation>
<!-- <el-upload :show-file-list="false" ref="uploadRef" action="" accept=".png,.jpg,.jpeg"
:on-change="choose" :auto-upload="false">
<template #trigger>
<el-button icon="el-icon-Upload" type="primary" class="mr10 ml10">上传接线图</el-button>
</template>
</el-upload> -->
<el-button icon="el-icon-Download" type="primary" @click="exportEvent" :loading="loading">
报告生成
</el-button>
</template>
</TableHeader>
<div v-if="lineFlag" class="box">
<el-form label-width="auto" class="form-one">
<el-form-item label="报表编号">
<el-input v-model="tableStore.table.params.reportNumber" style="width: 240px;" maxlength="32" show-word-limit clearable
placeholder="请输入报表编号" />
</el-form-item>
<el-form-item label="客户名称">
<el-input v-model="tableStore.table.params.crmName" style="width: 240px;" maxlength="32" show-word-limit clearable
placeholder="请输入客户名称" />
</el-form-item>
<el-form-item label="上传接线图">
<el-upload :show-file-list="false" ref="uploadRef" action="" accept=".png,.jpg,.jpeg"
:on-change="choose" :auto-upload="false">
<template #trigger>
<el-button icon="el-icon-Upload" type="primary" >上传接线图</el-button>
</template>
</el-upload>
</el-form-item>
</el-form>
<div v-if="previewUrl" class="upload-preview">
<div class="upload-preview-header">
<span class="upload-preview-title">接线图预览</span>
<el-button type="danger" link @click="removeImage">删除</el-button>
</div>
<img :src="previewUrl" alt="接线图" class="upload-preview-image" />
</div>
</div>
<el-empty v-else class="report-zl-sheet" description="治理监测点暂不支持出报告" />
</div>
</template>
<script setup lang="ts">
import { ref, provide, onUnmounted } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
import { ElMessage, ElNotification } from 'element-plus'
import { exportModel } from '@/api/cs-harmonic-boot/datatrend'
import { isReportMonitorPoint } from '@/components/tree/govern/lineTreeUtils'
defineOptions({
name: 'ReportWord',
})
const props = defineProps<{
dotList: Record<string, any>
lineFlag: boolean
}>()
const tableHeaderRef = ref()
const uploadRef = ref()
const uploadList = ref<any>(null)
const previewUrl = ref('')
const loading = ref(false)
const tableStore = new TableStore({
url: '',
method: 'POST',
column: [],
beforeSearchFun: () => { },
loadCallback: () => { },
})
provide('tableStore', tableStore)
const revokePreviewUrl = () => {
if (previewUrl.value) {
URL.revokeObjectURL(previewUrl.value)
previewUrl.value = ''
}
}
const choose = (files: any) => {
const isJPG = files.raw.type === 'image/jpg'
const isJPEG = files.raw.type === 'image/jpeg'
const isPNG = files.raw.type === 'image/png'
if (!isJPG && !isPNG && !isJPEG) {
ElMessage.warning('上传文件只能是 jpg/png 格式!')
return false
}
revokePreviewUrl()
uploadList.value = files
previewUrl.value = URL.createObjectURL(files.raw)
ElMessage.success('上传成功')
}
const removeImage = () => {
revokePreviewUrl()
uploadList.value = null
uploadRef.value?.clearFiles()
}
onUnmounted(() => {
revokePreviewUrl()
})
const exportEvent = () => {
if (!isReportMonitorPoint(props.dotList)) {
return ElMessage.warning('请选择监测点进行报告生成!')
}
if (!props.lineFlag) {
return ElMessage.warning('治理监测点暂不支持出报告!')
}
const specialCharRegex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/
if (
specialCharRegex.test(tableStore.table.params.crmName) ||
specialCharRegex.test(tableStore.table.params.reportNumber)
) {
ElNotification({
type: 'error',
message: '包含特殊字符,请注意修改!',
})
return
}
const form = new FormData()
form.append('lineIndex', props.dotList.id)
form.append('name', props.dotList.name)
form.append('crmName', tableStore.table.params.crmName || '')
form.append('reportNumber', tableStore.table.params.reportNumber || '')
form.append('type', '0')
form.append('startTime', tableHeaderRef.value.datePickerRef.timeValue[0])
form.append('endTime', tableHeaderRef.value.datePickerRef.timeValue[1])
form.append('file', uploadList.value?.raw || '')
loading.value = true
ElMessage('生成报告中...')
const now = new Date()
const formattedDate = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`
exportModel(form)
.then((res: any) => {
if (res == undefined) return
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8',
})
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = props.dotList.name + formattedDate
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
.finally(() => {
loading.value = false
})
}
</script>
<style lang="scss" scoped>
.report-word-panel {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
flex: 1;
min-height: 0;
padding: 10px;
overflow: auto;
}
.report-zl-sheet {
height: 100%;
}
.upload-preview {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #eee;
}
.upload-preview-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.upload-preview-title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.upload-preview-image {
display: block;
height: 200px;
max-width: 100%;
object-fit: contain;
border: 1px solid #eee;
border-radius: 4px;
background: #fafafa;
}
</style>

View File

@@ -0,0 +1,197 @@
<template>
<div class="report-sheet-panel">
<TableHeader ref="tableHeaderRef" datePicker :show-reset="false">
<template #select>
<el-form-item label="模板策略">
<el-select
v-model="template"
placeholder="请选择模版"
value-key="id"
@change="changeType"
>
<el-option
v-for="item in templatePolicy"
:key="item.id"
:label="item.excelName"
:value="item"
/>
</el-select>
</el-form-item>
</template>
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">报表生成</el-button>
</template>
</TableHeader>
<div v-if="lineFlag" class="box report-zl-box">
<div class="report-zl-sheet-wrap">
<div
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="暂无数据"
/>
</div>
</div>
<el-empty v-else class="report-zl-sheet" description="治理监测点暂不支持出报表" />
</div>
</template>
<script setup lang="ts">
import { ref, provide, watch, onUnmounted } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
import { destroyLuckysheet, exportLuckysheetFile, renderLuckysheetReport } from '@/utils/luckysheetHelper'
import { ElMessage } from 'element-plus'
defineOptions({
name: 'ReportSheet',
})
const props = defineProps<{
dotList: Record<string, any>
lineFlag: boolean
templatePolicy: any[]
active: boolean
}>()
const tableHeaderRef = ref()
const template = ref<any>({})
const reportForm = ref('')
const exportName = ref('')
const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getCustomReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = template.value.id
tableStore.table.params.lineId = props.dotList.id
destroyLuckysheet()
delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag
},
loadCallback: () => {
exportName.value = props.dotList.name
if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
} else {
destroyLuckysheet()
}
},
})
provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = null
const changeType = (val: any) => {
reportForm.value = val.reportForm
}
const loadSheetData = () => {
if (!props.active || !props.lineFlag || !props.dotList?.id || !template.value?.id) {
tableStore.table.loading = false
return
}
setTimeout(() => {
tableStore.index()
}, 500)
}
const exportEvent = () => {
if (!props.lineFlag) {
return ElMessage.warning('治理监测点暂不支持出报表!')
}
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportLuckysheetFile(exportName.value + formattedDate, tableStore.table.data.length > 0)
}
watch(
() => props.templatePolicy,
(list) => {
if (!list?.length) return
template.value = list[0]
reportForm.value = list[0]?.excelType
loadSheetData()
},
{ immediate: true }
)
watch(
() => [props.dotList?.id, props.lineFlag, props.active, template.value?.id] as const,
() => {
if (!props.active) {
destroyLuckysheet()
return
}
loadSheetData()
}
)
watch(
() => props.active,
(active) => {
if (!active) {
destroyLuckysheet()
return
}
if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
}
}
)
onUnmounted(() => {
destroyLuckysheet()
})
</script>
<style lang="scss" scoped>
.report-sheet-panel {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
flex: 1;
min-height: 0;
padding: 10px 0;
overflow: hidden;
}
.report-zl-box {
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;
}
</style>

View File

@@ -22,10 +22,20 @@
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">导出</el-button>
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div id="luckysheet" class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"></div>
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
<div class="box report-zl-box">
<div class="report-zl-sheet-wrap">
<div
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="暂无数据"
/>
</div>
</div>
</div>
</div>
@@ -68,13 +78,18 @@ const tableStore = new TableStore({
}
tableStore.table.params.startTime = datePickerRef.value.timeValue[0]
tableStore.table.params.endTime = datePickerRef.value.timeValue[1]
destroyLuckysheet()
delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag
},
loadCallback: () => {
name.value = dotList.value.name
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
} else {
destroyLuckysheet()
}
}
})
provide('tableStore', tableStore)
@@ -146,11 +161,29 @@ onUnmounted(() => {
.box {
flex: 1;
min-height: 0;
padding: 10px;
padding: 10px 0;
overflow: hidden;
}
.report-zl-box {
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;
}
</style>

View File

@@ -1,143 +1,70 @@
<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> -->
<pointTree
ref="TerminalRef"
template
@Policy="stencil"
@node-click="handleNodeClick"
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</div>
<div class="report-zl-main">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<!-- <el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> -->
<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>
</el-form-item>
</template>
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">导出</el-button>
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div
id="luckysheet"
class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"
></div>
<el-empty
class="report-zl-sheet"
v-else
description="暂无数据"
/>
</div>
<pointTree ref="terminalRef" template @Policy="stencil" @node-click="handleNodeClick"
@init="handleNodeClick" @pointTypeChange="pointTypeChange" />
</div>
<el-tabs v-model="activeTab" type="border-card" class="report-zl-tabs">
<el-tab-pane label="报表" name="sheet">
<Sheet :dot-list="dotList" :line-flag="lineFlag" :template-policy="templatePolicy"
:active="activeTab === 'sheet'" />
</el-tab-pane>
<el-tab-pane label="报告" name="report">
<Report :dot-list="dotList" :line-flag="lineFlag" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, provide, onUnmounted } 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 { ref } from '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 pointTree from '@/components/tree/govern/pointTree.vue'
// import data from './123.json'
import Sheet from './components/sheet.vue'
import Report from './components/report.vue'
defineOptions({
name: 'govern/reportCore/statisticsWx/index'
name: 'govern/reportCore/statisticsWx/index',
})
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 name = ref('')
const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getCustomReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
// tableStore.table.params.startTime = datePickerRef.value.timeValue[0],
// tableStore.table.params.endTime = datePickerRef.value.timeValue[1],
delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag
},
loadCallback: () => {
name.value = dotList.value.name
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
const props = withDefaults(
defineProps<{
defaultTab?: 'sheet' | 'report'
}>(),
{
defaultTab: 'sheet',
}
})
provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = null
const flag = ref(true)
)
const stencil = (val: any) => {
const height = mainHeight(20)
const terminalRef = ref()
const activeTab = ref(props.defaultTab)
const dotList = ref<Record<string, any>>({})
const templatePolicy = ref<any[]>([])
const lineFlag = ref(true)
const stencil = (val: any[]) => {
templatePolicy.value = val.filter((item: any) => item.excelType != '4')
Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType
}
const changetype = (val: any) => {
reportForm.value = val.reportForm
}
const pointTypeChange = (val: any, obj: any) => {
const pointTypeChange = (_val: any, obj: any) => {
handleNodeClick(obj)
}
const handleNodeClick = (data: any) => {
if (data.name.includes('治理监测点')) {
lineFlag.value = false
return
}
if (isLineTreeLeaf(data)) {
lineFlag.value = true
dotList.value = data
setTimeout(() => {
tableStore.index()
}, 500)
} else {
tableStore.table.loading = false
}
}
const exportEvent = () => {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportLuckysheetFile(name.value + formattedDate, tableStore.table.data.length > 0)
}
onUnmounted(() => {
destroyLuckysheet()
})
</script>
<style lang="scss">
.report-zl-page {
display: flex;
@@ -151,24 +78,21 @@ onUnmounted(() => {
overflow: hidden;
}
.report-zl-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
.report-zl-tabs {
flex: 1;
min-height: 0;
padding: 10px;
overflow: hidden;
}
.report-zl-sheet {
height: 100%;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
</style>

View File

@@ -31,13 +31,23 @@
</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" v-loading="tableStore.table.loading">
<div id="luckysheet" class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"></div>
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
<div class="box report-zl-box">
<div class="report-zl-sheet-wrap">
<div
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="暂无数据"
/>
</div>
</div>
</div>
</div>
@@ -88,13 +98,18 @@ const tableStore = new TableStore({
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
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
if (tableStore.table.data.length > 0) {
renderLuckysheetReport('luckysheet', tableStore.table.data, { allowEdit: false })
} else {
destroyLuckysheet()
}
}
})
provide('tableStore', tableStore)
@@ -184,7 +199,25 @@ const exportEvent = () => {
overflow: hidden;
}
.report-zl-box {
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;
}
</style>

Some files were not shown because too many files have changed in this diff Show More