修改全局列问题了,联调设备升级mqtt
This commit is contained in:
@@ -13,3 +13,23 @@ export const addDevModel = (file:any) => {
|
|||||||
data: form,
|
data: form,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 重启cld设备
|
||||||
|
**/
|
||||||
|
export const reboot = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/zl-event-boot/device/reboot',
|
||||||
|
method: 'get',
|
||||||
|
params: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 重启Mqtt设备
|
||||||
|
**/
|
||||||
|
export const rebootDevice = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/access-boot/askDeviceData/rebootDevice',
|
||||||
|
method: 'post',
|
||||||
|
params: data
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -102,7 +102,7 @@ export function findByDevTypeId(data: any) {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 装置升级
|
// 装置升级 CLD
|
||||||
export function upgrade(params: any) {
|
export function upgrade(params: any) {
|
||||||
return createAxios({
|
return createAxios({
|
||||||
url: '/zl-event-boot/device/upgrade',
|
url: '/zl-event-boot/device/upgrade',
|
||||||
@@ -110,3 +110,19 @@ export function upgrade(params: any) {
|
|||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 装置升级 mqtt
|
||||||
|
export function uploadUpgradeFile(params: any) {
|
||||||
|
return createAxios({
|
||||||
|
url: '/cs-device-boot/EquipmentDelivery/uploadUpgradeFile',
|
||||||
|
method: 'post',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 装置升级 升级 mqtt
|
||||||
|
export function commitUpgradeFile(params: any) {
|
||||||
|
return createAxios({
|
||||||
|
url: '/cs-device-boot/EquipmentDelivery/commitUpgradeFile',
|
||||||
|
method: 'post',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
<div class="device-control">
|
<div class="device-control">
|
||||||
<!--治理效果报表 -->
|
<!--治理效果报表 -->
|
||||||
<div v-show="fullscreen">
|
<div v-show="fullscreen">
|
||||||
<!-- <PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree> -->
|
<APFTree :height="flag ? 128 : 72" @node-click="handleNodeClick" template @init="handleNodeClick"></APFTree>
|
||||||
<APFTree :height="flag ? 126 : 70" @node-click="handleNodeClick" template @init="handleNodeClick"></APFTree>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" datePicker
|
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" datePicker
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<!--指标越限时间分布
|
<!--指标越限时间分布
|
||||||
-->
|
-->
|
||||||
<div v-show="fullscreen">
|
<div v-show="fullscreen">
|
||||||
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
<PointTree :height="flag ? 128 : 72" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
|
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="device-control">
|
<div class="device-control">
|
||||||
<!--指标越限概率分布 -->
|
<!--指标越限概率分布 -->
|
||||||
<div v-show="fullscreen">
|
<div v-show="fullscreen">
|
||||||
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
<PointTree :height="flag ? 128 : 72" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
|
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="device-control">
|
<div class="device-control">
|
||||||
<!--指标拟合图 -->
|
<!--指标拟合图 -->
|
||||||
<div v-show="fullscreen">
|
<div v-show="fullscreen">
|
||||||
<PointTree :height="flag ? 106 : 50" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
<PointTree :height="flag ? 128 : 72" @node-click="nodeClick" @pointTypeChange="pointTypeChange"></PointTree>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TableHeader datePicker @selectChange="selectChange" v-if="fullscreen" ref="TableHeaderRef"
|
<TableHeader datePicker @selectChange="selectChange" v-if="fullscreen" ref="TableHeaderRef"
|
||||||
|
|||||||
@@ -33,16 +33,28 @@
|
|||||||
<Icon name="el-icon-Search" style="font-size: 16px" />
|
<Icon name="el-icon-Search" style="font-size: 16px" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
</div>
|
||||||
<!-- <Icon
|
<div class="comm-tabs mb10">
|
||||||
@click="onMenuCollapse"
|
<el-tooltip
|
||||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
v-for="tab in commTabs"
|
||||||
:class="menuCollapse ? 'unfold' : ''"
|
:key="tab.key"
|
||||||
size="18px"
|
:content="tab.label"
|
||||||
class="fold ml10 menu-collapse"
|
placement="top"
|
||||||
style="cursor: pointer"
|
:show-after="200"
|
||||||
v-if="props.canExpand"
|
>
|
||||||
/> -->
|
<button
|
||||||
|
type="button"
|
||||||
|
class="comm-tab"
|
||||||
|
:class="[
|
||||||
|
`is-${tab.key}`,
|
||||||
|
{ 'is-active': commFilter === tab.key }
|
||||||
|
]"
|
||||||
|
@click="onCommFilter(tab.key)"
|
||||||
|
>
|
||||||
|
<Icon :name="tab.icon" :size="12" class="comm-tab__icon" />
|
||||||
|
<span class="comm-tab__count">{{ tab.count }}</span>
|
||||||
|
</button>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-collapse
|
<el-collapse
|
||||||
@@ -67,9 +79,10 @@
|
|||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
v-bind="$attrs"
|
v-bind="treeAttrs"
|
||||||
:data="zlDevList"
|
:data="zlDevList"
|
||||||
class="collapse-tree"
|
class="collapse-tree"
|
||||||
|
@node-click="onTreeNodeClick"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
@@ -95,8 +108,9 @@
|
|||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:data="bxsDeviceData"
|
:data="bxsDeviceData"
|
||||||
v-bind="$attrs"
|
v-bind="treeAttrs"
|
||||||
class="collapse-tree"
|
class="collapse-tree"
|
||||||
|
@node-click="onTreeNodeClick"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
@@ -122,8 +136,9 @@
|
|||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:data="frontDeviceData"
|
:data="frontDeviceData"
|
||||||
v-bind="$attrs"
|
v-bind="treeAttrs"
|
||||||
class="collapse-tree"
|
class="collapse-tree"
|
||||||
|
@node-click="onTreeNodeClick"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
@@ -147,10 +162,11 @@
|
|||||||
highlight-current
|
highlight-current
|
||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
v-bind="$attrs"
|
v-bind="treeAttrs"
|
||||||
:data="props.data"
|
:data="props.data"
|
||||||
class="engineering-tree"
|
class="engineering-tree"
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
|
@node-click="onTreeNodeClick"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
@@ -172,7 +188,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||||
import { ElTree, type CollapseModelValue } from 'element-plus'
|
import { ElTree, type CollapseModelValue } from 'element-plus'
|
||||||
import { ref, watch, onMounted, nextTick } from 'vue'
|
import { ref, watch, onMounted, nextTick, computed, useAttrs } from 'vue'
|
||||||
import { collectDeviceLeaves } from './govern/lineTreeUtils'
|
import { collectDeviceLeaves } from './govern/lineTreeUtils'
|
||||||
import { collectDeviceApiLeaves } from './govern/deviceTreeUtils'
|
import { collectDeviceApiLeaves } from './govern/deviceTreeUtils'
|
||||||
|
|
||||||
@@ -194,6 +210,8 @@ interface Props {
|
|||||||
leafMode?: 'line' | 'device'
|
leafMode?: 'line' | 'device'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommFilter = 'all' | 'normal' | 'abnormal'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
width: '280px',
|
width: '280px',
|
||||||
canExpand: true,
|
canExpand: true,
|
||||||
@@ -211,11 +229,20 @@ const options = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const { proxy } = useCurrentInstance()
|
const { proxy } = useCurrentInstance()
|
||||||
|
const attrs = useAttrs()
|
||||||
|
|
||||||
|
/** 去掉 node-click,避免与本地处理重复/覆盖 */
|
||||||
|
const treeAttrs = computed(() => {
|
||||||
|
const { onNodeClick: _onNodeClick, ...rest } = attrs as Record<string, any>
|
||||||
|
return rest
|
||||||
|
})
|
||||||
|
|
||||||
const menuCollapse = ref(false)
|
const menuCollapse = ref(false)
|
||||||
const activeName = ref('0')
|
const activeName = ref('0')
|
||||||
const filterText = ref('')
|
const filterText = ref('')
|
||||||
const process = ref('')
|
const process = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const commFilter = ref<CommFilter>('all')
|
||||||
|
|
||||||
const defaultProps = { label: 'name', value: 'id' }
|
const defaultProps = { label: 'name', value: 'id' }
|
||||||
|
|
||||||
@@ -231,6 +258,67 @@ const treeRef4 = ref<InstanceType<typeof ElTree>>()
|
|||||||
|
|
||||||
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
|
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
|
||||||
|
|
||||||
|
const isCommNormal = (node: any) => Number(node?.comFlag) === 2
|
||||||
|
|
||||||
|
/** 设备叶子:Platform 且无子节点,或 type=device */
|
||||||
|
function isDeviceLeaf(data: any): boolean {
|
||||||
|
if (!data) return false
|
||||||
|
if (data.type === 'device') return true
|
||||||
|
if (data.icon === 'el-icon-Platform' && (!data.children || !data.children.length)) return true
|
||||||
|
if (data.level === 2 && data.icon === 'el-icon-Platform') return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectDevices(nodes: any[], out: any[] = []): any[] {
|
||||||
|
nodes?.forEach(n => {
|
||||||
|
if (isDeviceLeaf(n)) out.push(n)
|
||||||
|
if (n.children?.length) collectDevices(n.children, out)
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const deviceList = computed(() => {
|
||||||
|
if (treeType.value === '2') return collectDevices(props.data || [])
|
||||||
|
const collectLeaves = props.leafMode === 'device' ? collectDeviceApiLeaves : collectDeviceLeaves
|
||||||
|
const { govern, portable, monitor } = collectLeaves(
|
||||||
|
zlDevList.value,
|
||||||
|
bxsDeviceData.value,
|
||||||
|
frontDeviceData.value
|
||||||
|
)
|
||||||
|
return [...govern, ...portable, ...monitor]
|
||||||
|
})
|
||||||
|
|
||||||
|
const deviceStats = computed(() => {
|
||||||
|
const list = deviceList.value
|
||||||
|
const total = list.length
|
||||||
|
const normal = list.filter(isCommNormal).length
|
||||||
|
return { total, normal, abnormal: total - normal }
|
||||||
|
})
|
||||||
|
|
||||||
|
const commTabs = computed(() => [
|
||||||
|
{ key: 'all' as const, label: '全部设备', count: deviceStats.value.total, icon: 'el-icon-Platform' },
|
||||||
|
{ key: 'normal' as const, label: '通讯正常', count: deviceStats.value.normal, icon: 'el-icon-Platform' },
|
||||||
|
{ key: 'abnormal' as const, label: '通讯异常', count: deviceStats.value.abnormal, icon: 'el-icon-Platform' }
|
||||||
|
])
|
||||||
|
|
||||||
|
function applyTreeFilter() {
|
||||||
|
nextTick(() => {
|
||||||
|
const val = filterText.value
|
||||||
|
if (treeType.value === '1') {
|
||||||
|
treeRef1.value?.filter(val)
|
||||||
|
treeRef2.value?.filter(val)
|
||||||
|
treeRef3.value?.filter(val)
|
||||||
|
} else {
|
||||||
|
treeRef4.value?.filter(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCommFilter = (key: CommFilter) => {
|
||||||
|
commFilter.value = key
|
||||||
|
applyTreeFilter()
|
||||||
|
}
|
||||||
|
|
||||||
function splitTreeData(val: any[]) {
|
function splitTreeData(val: any[]) {
|
||||||
zlDeviceData.value = []
|
zlDeviceData.value = []
|
||||||
bxsDeviceData.value = []
|
bxsDeviceData.value = []
|
||||||
@@ -270,6 +358,17 @@ function getActiveTreeRef() {
|
|||||||
return treeRef3.value
|
return treeRef3.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function forwardNodeClick(data: any, node: any, ev?: any) {
|
||||||
|
const handler = (attrs as any).onNodeClick
|
||||||
|
if (!handler) return
|
||||||
|
if (Array.isArray(handler)) handler.forEach((fn: any) => fn?.(data, node, ev))
|
||||||
|
else handler(data, node, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTreeNodeClick(data: any, node: any, ev?: any) {
|
||||||
|
forwardNodeClick(data, node, ev)
|
||||||
|
}
|
||||||
|
|
||||||
function resolveActiveName() {
|
function resolveActiveName() {
|
||||||
if (zlDeviceData.value.length) return '0'
|
if (zlDeviceData.value.length) return '0'
|
||||||
if (bxsDeviceData.value.length) return '1'
|
if (bxsDeviceData.value.length) return '1'
|
||||||
@@ -323,12 +422,13 @@ watch(
|
|||||||
if (treeType.value === '1') {
|
if (treeType.value === '1') {
|
||||||
nextTick(() => setActiveName())
|
nextTick(() => setActiveName())
|
||||||
}
|
}
|
||||||
|
applyTreeFilter()
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true }
|
{ immediate: true, deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(filterText, val => {
|
watch(filterText, () => {
|
||||||
getActiveTreeRef()?.filter(val)
|
applyTreeFilter()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(process, () => {
|
watch(process, () => {
|
||||||
@@ -336,6 +436,11 @@ watch(process, () => {
|
|||||||
if (activeName.value === '0') {
|
if (activeName.value === '0') {
|
||||||
nextTick(() => changeDevice(activeName.value))
|
nextTick(() => changeDevice(activeName.value))
|
||||||
}
|
}
|
||||||
|
applyTreeFilter()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(treeType, () => {
|
||||||
|
applyTreeFilter()
|
||||||
})
|
})
|
||||||
|
|
||||||
const onMenuCollapse = () => {
|
const onMenuCollapse = () => {
|
||||||
@@ -343,10 +448,20 @@ const onMenuCollapse = () => {
|
|||||||
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
|
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterNode = (value: string, data: any, node: any): boolean => {
|
const filterNode = (_value: string, data: any, node: any): boolean => {
|
||||||
if (!value) return true
|
if (filterText.value) {
|
||||||
if (!data.name) return false
|
if (!data.name) return false
|
||||||
return chooseNode(value, data, node)
|
if (!chooseNode(filterText.value, data, node)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commFilter.value === 'all') return true
|
||||||
|
|
||||||
|
if (isDeviceLeaf(data)) {
|
||||||
|
const normal = isCommNormal(data)
|
||||||
|
return commFilter.value === 'normal' ? normal : !normal
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!filterText.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const chooseNode = (value: string, data: any, node: any): boolean => {
|
const chooseNode = (value: string, data: any, node: any): boolean => {
|
||||||
@@ -413,6 +528,115 @@ onMounted(() => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comm-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comm-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 4px 0 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #e5eaf3;
|
||||||
|
background: #fff;
|
||||||
|
color: #4e5969;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-all .comm-tab__icon,
|
||||||
|
&.is-all :deep(.comm-tab__icon),
|
||||||
|
&.is-all :deep(svg),
|
||||||
|
&.is-all :deep(i) {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-normal .comm-tab__icon,
|
||||||
|
&.is-normal :deep(.comm-tab__icon),
|
||||||
|
&.is-normal :deep(svg),
|
||||||
|
&.is-normal :deep(i) {
|
||||||
|
color: #2ab914 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-abnormal .comm-tab__icon,
|
||||||
|
&.is-abnormal :deep(.comm-tab__icon),
|
||||||
|
&.is-abnormal :deep(svg),
|
||||||
|
&.is-abnormal :deep(i) {
|
||||||
|
color: #e26257 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
min-width: 20px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: #1d2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.is-active) {
|
||||||
|
border-color: var(--el-color-primary-light-5);
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.comm-tab__icon,
|
||||||
|
:deep(.comm-tab__icon),
|
||||||
|
:deep(svg),
|
||||||
|
:deep(i) {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-all.is-active {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-normal.is-active {
|
||||||
|
background: #2ab914;
|
||||||
|
border-color: #2ab914;
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: #2ab914;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-abnormal.is-active {
|
||||||
|
background: #e26257;
|
||||||
|
border-color: #e26257;
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: #e26257;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.device-collapse {
|
.device-collapse {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function decorateDeviceTree(
|
|||||||
if (type === '2') {
|
if (type === '2') {
|
||||||
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary() })
|
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary() })
|
||||||
item.children?.forEach((child: any) => {
|
item.children?.forEach((child: any) => {
|
||||||
applyMeta(child, { icon: 'el-icon-List', color: primary() })
|
applyMeta(child, { icon: 'el-icon-Menu', color: primary() })
|
||||||
child.children?.forEach((grand: any) => {
|
child.children?.forEach((grand: any) => {
|
||||||
applyMeta(grand, {
|
applyMeta(grand, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
@@ -29,7 +29,7 @@ export function decorateDeviceTree(
|
|||||||
item.children?.forEach((l1: any) => {
|
item.children?.forEach((l1: any) => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary() })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary() })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
l3.pName = '治理设备'
|
l3.pName = '治理设备'
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
@@ -63,7 +63,7 @@ export function decorateDeviceTree(
|
|||||||
item.children?.forEach((l1: any) => {
|
item.children?.forEach((l1: any) => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary() })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary() })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
l3.pName = '监测设备'
|
l3.pName = '监测设备'
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
@@ -88,7 +88,7 @@ export function decorateDeviceInfoTree(list: any[], decorators: LineTreeDecorato
|
|||||||
list.forEach(item => {
|
list.forEach(item => {
|
||||||
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary() })
|
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary() })
|
||||||
item.children?.forEach((l2: any) => {
|
item.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary() })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary() })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
@@ -107,11 +107,11 @@ export function decorateCloudTree(root: any, decorators: LineTreeDecorators): an
|
|||||||
const { primary, statusColor, applyMeta } = decorators
|
const { primary, statusColor, applyMeta } = decorators
|
||||||
const leaves: any[] = []
|
const leaves: any[] = []
|
||||||
|
|
||||||
applyMeta(root, { icon: 'el-icon-Menu', color: primary() })
|
applyMeta(root, { icon: 'el-icon-ChromeFilled', color: primary() })
|
||||||
root.children?.forEach((l1: any) => {
|
root.children?.forEach((l1: any) => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary() })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary() })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary() })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
@@ -140,7 +140,7 @@ export function decorateObjTree(data: any[], decorators: LineTreeDecorators): an
|
|||||||
data.forEach(l1 => {
|
data.forEach(l1 => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1 })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1 })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary(), level: 2 })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary(), level: 2 })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
applyMeta(l3, { icon: 'el-icon-Platform', color: primary(), level: 3 })
|
applyMeta(l3, { icon: 'el-icon-Platform', color: primary(), level: 3 })
|
||||||
leaves.push(l3)
|
leaves.push(l3)
|
||||||
@@ -165,7 +165,7 @@ export function decorateLedgerLineTree(root: any, decorators: LineTreeDecorators
|
|||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1 })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1 })
|
||||||
if (!Array.isArray(l1.children)) l1.children = []
|
if (!Array.isArray(l1.children)) l1.children = []
|
||||||
l1.children.forEach((l2: any) => {
|
l1.children.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary(), level: 2 })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary(), level: 2 })
|
||||||
if (!Array.isArray(l2.children)) l2.children = []
|
if (!Array.isArray(l2.children)) l2.children = []
|
||||||
l2.children.forEach((l3: any) => {
|
l2.children.forEach((l3: any) => {
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function decorateLineTree(
|
|||||||
if (type === '2') {
|
if (type === '2') {
|
||||||
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary(), ...parentDisabled })
|
applyMeta(item, { icon: 'el-icon-HomeFilled', color: primary(), ...parentDisabled })
|
||||||
item.children?.forEach((child: any) => {
|
item.children?.forEach((child: any) => {
|
||||||
applyMeta(child, { icon: 'el-icon-List', color: primary(), ...parentDisabled })
|
applyMeta(child, { icon: 'el-icon-Menu', color: primary(), ...parentDisabled })
|
||||||
child.children?.forEach((grand: any) => {
|
child.children?.forEach((grand: any) => {
|
||||||
applyMeta(grand, {
|
applyMeta(grand, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
@@ -99,7 +99,7 @@ export function decorateLineTree(
|
|||||||
item.children?.forEach((l1: any) => {
|
item.children?.forEach((l1: any) => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1, ...parentDisabled })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1, ...parentDisabled })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary(), level: 1, ...parentDisabled })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary(), level: 1, ...parentDisabled })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
@@ -139,7 +139,7 @@ export function decorateLineTree(
|
|||||||
item.children?.forEach((l1: any) => {
|
item.children?.forEach((l1: any) => {
|
||||||
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1, ...parentDisabled })
|
applyMeta(l1, { icon: 'el-icon-HomeFilled', color: primary(), level: 1, ...parentDisabled })
|
||||||
l1.children?.forEach((l2: any) => {
|
l1.children?.forEach((l2: any) => {
|
||||||
applyMeta(l2, { icon: 'el-icon-List', color: primary(), level: 1, ...parentDisabled })
|
applyMeta(l2, { icon: 'el-icon-Menu', color: primary(), level: 1, ...parentDisabled })
|
||||||
l2.children?.forEach((l3: any) => {
|
l2.children?.forEach((l3: any) => {
|
||||||
applyMeta(l3, {
|
applyMeta(l3, {
|
||||||
icon: 'el-icon-Platform',
|
icon: 'el-icon-Platform',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<!-- 设备监控使用折叠面板渲染多个tree -->
|
<!-- 设备监控使用折叠面板渲染多个tree -->
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div :style="{ width: menuCollapse ? '40px' : props.width }" class="point-tree-root">
|
<div :style="{ width: menuCollapse ? '40px' : props.width }" class="point-tree-root">
|
||||||
<!-- <Icon
|
<!-- <Icon
|
||||||
v-show="menuCollapse"
|
v-show="menuCollapse"
|
||||||
@@ -11,7 +12,14 @@
|
|||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
v-if="route.path != '/admin/govern/reportCore/statistics/index'"
|
v-if="route.path != '/admin/govern/reportCore/statistics/index'"
|
||||||
/> -->
|
/> -->
|
||||||
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }">
|
<div
|
||||||
|
class="cn-tree"
|
||||||
|
:style="{
|
||||||
|
opacity: menuCollapse ? 0 : 1,
|
||||||
|
display: menuCollapse ? 'none' : '',
|
||||||
|
height: `calc(100vh - 125px - ${props.height || 0}px)`
|
||||||
|
}"
|
||||||
|
>
|
||||||
<div class="tree-search mb10">
|
<div class="tree-search mb10">
|
||||||
<el-input v-model.trim="filterText" placeholder="请输入内容" clearable>
|
<el-input v-model.trim="filterText" placeholder="请输入内容" clearable>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
@@ -24,15 +32,28 @@
|
|||||||
<Icon name="el-icon-Search" style="font-size: 16px" />
|
<Icon name="el-icon-Search" style="font-size: 16px" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<!-- <Icon
|
</div>
|
||||||
@click="onMenuCollapse"
|
<div class="comm-tabs mb10">
|
||||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
<el-tooltip
|
||||||
:class="menuCollapse ? 'unfold' : ''"
|
v-for="tab in commTabs"
|
||||||
size="18px"
|
:key="tab.key"
|
||||||
class="fold ml10 menu-collapse"
|
:content="tab.label"
|
||||||
style="cursor: pointer"
|
placement="top"
|
||||||
v-if="props.canExpand && route.path != '/admin/govern/reportCore/statistics/index'"
|
:show-after="200"
|
||||||
/> -->
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="comm-tab"
|
||||||
|
:class="[
|
||||||
|
`is-${tab.key}`,
|
||||||
|
{ 'is-active': commFilter === tab.key }
|
||||||
|
]"
|
||||||
|
@click="onCommFilter(tab.key)"
|
||||||
|
>
|
||||||
|
<Icon :name="tab.icon" :size="12" class="comm-tab__icon" />
|
||||||
|
<span class="comm-tab__count">{{ tab.count }}</span>
|
||||||
|
</button>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-collapse :accordion="true" v-model="activeName" class="device-collapse" @change="changeDevice"
|
<el-collapse :accordion="true" v-model="activeName" class="device-collapse" @change="changeDevice"
|
||||||
@@ -45,8 +66,8 @@
|
|||||||
<el-option label="正式投运" value="4" />
|
<el-option label="正式投运" value="4" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-tree ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode"
|
<el-tree ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode"
|
||||||
node-key="id" v-bind="$attrs" :data="zlDevList" class="collapse-tree"
|
node-key="id" v-bind="treeAttrs" :data="zlDevList" class="collapse-tree"
|
||||||
:default-expand-all="false">
|
:default-expand-all="false" @node-click="onTreeNodeClick">
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
<Icon :name="nodeData.icon" style="font-size: 16px"
|
<Icon :name="nodeData.icon" style="font-size: 16px"
|
||||||
@@ -60,8 +81,8 @@
|
|||||||
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length">
|
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length">
|
||||||
<div class="collapse-tree-panel">
|
<div class="collapse-tree-panel">
|
||||||
<el-tree ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false"
|
<el-tree ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||||
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="$attrs"
|
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="treeAttrs"
|
||||||
class="collapse-tree">
|
class="collapse-tree" @node-click="onTreeNodeClick">
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
<Icon :name="nodeData.icon" style="font-size: 16px"
|
<Icon :name="nodeData.icon" style="font-size: 16px"
|
||||||
@@ -75,8 +96,8 @@
|
|||||||
<el-collapse-item title="监测设备" name="2" v-if="yqfDeviceData.length">
|
<el-collapse-item title="监测设备" name="2" v-if="yqfDeviceData.length">
|
||||||
<div class="collapse-tree-panel">
|
<div class="collapse-tree-panel">
|
||||||
<el-tree ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false"
|
<el-tree ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||||
:filter-node-method="filterNode" node-key="id" :data="yqfDeviceData" v-bind="$attrs"
|
:filter-node-method="filterNode" node-key="id" :data="yqfDeviceData" v-bind="treeAttrs"
|
||||||
class="collapse-tree">
|
class="collapse-tree" @node-click="onTreeNodeClick">
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
<Icon :name="nodeData.icon" style="font-size: 16px"
|
<Icon :name="nodeData.icon" style="font-size: 16px"
|
||||||
@@ -90,8 +111,8 @@
|
|||||||
</el-collapse>
|
</el-collapse>
|
||||||
<div v-if="treeType == '2'" class="engineering-tree-wrap" v-loading="loading">
|
<div v-if="treeType == '2'" class="engineering-tree-wrap" v-loading="loading">
|
||||||
<el-tree class="engineering-tree pt10 collapse-tree" ref="treeRef4" :props="defaultProps"
|
<el-tree class="engineering-tree pt10 collapse-tree" ref="treeRef4" :props="defaultProps"
|
||||||
highlight-current :filter-node-method="filterNode" node-key="id" v-bind="$attrs" :data="props.data"
|
highlight-current :filter-node-method="filterNode" node-key="id" v-bind="treeAttrs" :data="props.data"
|
||||||
:default-expand-all="false">
|
:default-expand-all="false" @node-click="onTreeNodeClick">
|
||||||
<template #default="{ node, data: nodeData }">
|
<template #default="{ node, data: nodeData }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
<Icon :name="nodeData.icon" style="font-size: 16px" :style="{ color: nodeData.color }"
|
<Icon :name="nodeData.icon" style="font-size: 16px" :style="{ color: nodeData.color }"
|
||||||
@@ -108,9 +129,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||||
import { ElTree, type CollapseModelValue } from 'element-plus'
|
import { ElTree, type CollapseModelValue } from 'element-plus'
|
||||||
import { ref, watch, nextTick } from 'vue'
|
import { ref, watch, nextTick, computed, useAttrs } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { collectDeviceLeaves } from './govern/lineTreeUtils'
|
import { collectDeviceLeaves, isLineTreeLeaf } from './govern/lineTreeUtils'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'govern/point',
|
name: 'govern/point',
|
||||||
@@ -127,6 +148,8 @@ interface Props {
|
|||||||
height?: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommFilter = 'all' | 'normal' | 'abnormal'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
canExpand: true,
|
canExpand: true,
|
||||||
@@ -137,6 +160,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { proxy } = useCurrentInstance()
|
const { proxy } = useCurrentInstance()
|
||||||
|
const attrs = useAttrs()
|
||||||
|
|
||||||
|
/** 去掉 node-click,避免与本地处理重复/覆盖 */
|
||||||
|
const treeAttrs = computed(() => {
|
||||||
|
const { onNodeClick: _onNodeClick, ...rest } = attrs as Record<string, any>
|
||||||
|
return rest
|
||||||
|
})
|
||||||
|
|
||||||
const treeType = ref('2')
|
const treeType = ref('2')
|
||||||
const options = [
|
const options = [
|
||||||
@@ -149,6 +179,7 @@ const activeName = ref('0')
|
|||||||
const filterText = ref('')
|
const filterText = ref('')
|
||||||
const process = ref('')
|
const process = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const commFilter = ref<CommFilter>('all')
|
||||||
|
|
||||||
const defaultProps = { label: 'name', value: 'id' }
|
const defaultProps = { label: 'name', value: 'id' }
|
||||||
|
|
||||||
@@ -164,6 +195,57 @@ const treeRef4 = ref<InstanceType<typeof ElTree>>()
|
|||||||
|
|
||||||
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
|
defineExpose({ treeRef1, treeRef2, treeRef3, treeRef4 })
|
||||||
|
|
||||||
|
const isCommNormal = (node: any) => Number(node?.comFlag) === 2
|
||||||
|
|
||||||
|
function collectPoints(nodes: any[], out: any[] = []): any[] {
|
||||||
|
nodes?.forEach(n => {
|
||||||
|
if (isLineTreeLeaf(n)) out.push(n)
|
||||||
|
if (n.children?.length) collectPoints(n.children, out)
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const pointList = computed(() => {
|
||||||
|
if (treeType.value === '2') return collectPoints(props.data || [])
|
||||||
|
const { govern, portable, monitor } = collectDeviceLeaves(
|
||||||
|
zlDevList.value,
|
||||||
|
bxsDeviceData.value,
|
||||||
|
yqfDeviceData.value
|
||||||
|
)
|
||||||
|
return [...govern, ...portable, ...monitor]
|
||||||
|
})
|
||||||
|
|
||||||
|
const pointStats = computed(() => {
|
||||||
|
const list = pointList.value
|
||||||
|
const total = list.length
|
||||||
|
const normal = list.filter(isCommNormal).length
|
||||||
|
return { total, normal, abnormal: total - normal }
|
||||||
|
})
|
||||||
|
|
||||||
|
const commTabs = computed(() => [
|
||||||
|
{ key: 'all' as const, label: '全部监测点', count: pointStats.value.total, icon: 'local-监测点' },
|
||||||
|
{ key: 'normal' as const, label: '通讯正常', count: pointStats.value.normal, icon: 'local-监测点' },
|
||||||
|
{ key: 'abnormal' as const, label: '通讯异常', count: pointStats.value.abnormal, icon: 'local-监测点' }
|
||||||
|
])
|
||||||
|
|
||||||
|
function applyTreeFilter() {
|
||||||
|
nextTick(() => {
|
||||||
|
const val = filterText.value
|
||||||
|
if (treeType.value === '1') {
|
||||||
|
treeRef1.value?.filter(val)
|
||||||
|
treeRef2.value?.filter(val)
|
||||||
|
treeRef3.value?.filter(val)
|
||||||
|
} else {
|
||||||
|
treeRef4.value?.filter(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCommFilter = (key: CommFilter) => {
|
||||||
|
commFilter.value = key
|
||||||
|
applyTreeFilter()
|
||||||
|
}
|
||||||
|
|
||||||
function splitTreeData(val: any[]) {
|
function splitTreeData(val: any[]) {
|
||||||
zlDeviceData.value = []
|
zlDeviceData.value = []
|
||||||
bxsDeviceData.value = []
|
bxsDeviceData.value = []
|
||||||
@@ -213,6 +295,17 @@ function getActiveTreeRef() {
|
|||||||
return treeRef3.value
|
return treeRef3.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function forwardNodeClick(data: any, node: any, ev?: any) {
|
||||||
|
const handler = (attrs as any).onNodeClick
|
||||||
|
if (!handler) return
|
||||||
|
if (Array.isArray(handler)) handler.forEach((fn: any) => fn?.(data, node, ev))
|
||||||
|
else handler(data, node, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTreeNodeClick(data: any, node: any, ev?: any) {
|
||||||
|
forwardNodeClick(data, node, ev)
|
||||||
|
}
|
||||||
|
|
||||||
function resolveActiveName() {
|
function resolveActiveName() {
|
||||||
if (zlDeviceData.value.length) return '0'
|
if (zlDeviceData.value.length) return '0'
|
||||||
if (bxsDeviceData.value.length) return '1'
|
if (bxsDeviceData.value.length) return '1'
|
||||||
@@ -265,12 +358,13 @@ watch(
|
|||||||
if (treeType.value === '1') {
|
if (treeType.value === '1') {
|
||||||
nextTick(() => setActiveName())
|
nextTick(() => setActiveName())
|
||||||
}
|
}
|
||||||
|
applyTreeFilter()
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true }
|
{ immediate: true, deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(filterText, val => {
|
watch(filterText, () => {
|
||||||
getActiveTreeRef()?.filter(val)
|
applyTreeFilter()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(process, () => {
|
watch(process, () => {
|
||||||
@@ -278,6 +372,11 @@ watch(process, () => {
|
|||||||
if (activeName.value === '0') {
|
if (activeName.value === '0') {
|
||||||
nextTick(() => changeDevice(activeName.value))
|
nextTick(() => changeDevice(activeName.value))
|
||||||
}
|
}
|
||||||
|
applyTreeFilter()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(treeType, () => {
|
||||||
|
applyTreeFilter()
|
||||||
})
|
})
|
||||||
|
|
||||||
const onMenuCollapse = () => {
|
const onMenuCollapse = () => {
|
||||||
@@ -285,10 +384,21 @@ const onMenuCollapse = () => {
|
|||||||
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
|
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterNode = (value: string, data: any, node: any): boolean => {
|
const filterNode = (_value: string, data: any, node: any): boolean => {
|
||||||
if (!value) return true
|
if (filterText.value) {
|
||||||
if (!data.name) return false
|
if (!data.name) return false
|
||||||
return chooseNode(value, data, node)
|
if (!chooseNode(filterText.value, data, node)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commFilter.value === 'all') return true
|
||||||
|
|
||||||
|
if (isLineTreeLeaf(data)) {
|
||||||
|
const normal = isCommNormal(data)
|
||||||
|
return commFilter.value === 'normal' ? normal : !normal
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非叶子:交由子节点可见性决定是否展示父级
|
||||||
|
return !!filterText.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const chooseNode = (value: string, data: any, node: any): boolean => {
|
const chooseNode = (value: string, data: any, node: any): boolean => {
|
||||||
@@ -335,9 +445,7 @@ const changeTreeType = (val: string) => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 100%;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
max-height: 100%;
|
|
||||||
width: 280px;
|
width: 280px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -356,6 +464,115 @@ const changeTreeType = (val: string) => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comm-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comm-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 4px 0 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #e5eaf3;
|
||||||
|
background: #fff;
|
||||||
|
color: #4e5969;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-all .comm-tab__icon,
|
||||||
|
&.is-all :deep(.comm-tab__icon),
|
||||||
|
&.is-all :deep(svg),
|
||||||
|
&.is-all :deep(i) {
|
||||||
|
color: var(--el-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-normal .comm-tab__icon,
|
||||||
|
&.is-normal :deep(.comm-tab__icon),
|
||||||
|
&.is-normal :deep(svg),
|
||||||
|
&.is-normal :deep(i) {
|
||||||
|
color: #2ab914 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-abnormal .comm-tab__icon,
|
||||||
|
&.is-abnormal :deep(.comm-tab__icon),
|
||||||
|
&.is-abnormal :deep(svg),
|
||||||
|
&.is-abnormal :deep(i) {
|
||||||
|
color: #e26257 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
min-width: 20px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: #1d2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.is-active) {
|
||||||
|
border-color: var(--el-color-primary-light-5);
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.comm-tab__icon,
|
||||||
|
:deep(.comm-tab__icon),
|
||||||
|
:deep(svg),
|
||||||
|
:deep(i) {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-all.is-active {
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-normal.is-active {
|
||||||
|
background: #2ab914;
|
||||||
|
border-color: #2ab914;
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: #2ab914;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-abnormal.is-active {
|
||||||
|
background: #e26257;
|
||||||
|
border-color: #e26257;
|
||||||
|
|
||||||
|
.comm-tab__count {
|
||||||
|
color: #e26257;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.device-collapse {
|
.device-collapse {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|||||||
200
src/utils/MQTT.ts
Normal file
200
src/utils/MQTT.ts
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import mqtt, { type MqttClient, type IClientOptions } from 'mqtt'
|
||||||
|
|
||||||
|
export type MqttMessageHandler = (topic: string, payload: any, raw: Uint8Array) => void
|
||||||
|
|
||||||
|
export interface MqttConnectOptions {
|
||||||
|
/** 覆盖 localStorage 中的 MQTTURL */
|
||||||
|
url?: string
|
||||||
|
username?: string
|
||||||
|
password?: string
|
||||||
|
clientId?: string
|
||||||
|
qos?: 0 | 1 | 2
|
||||||
|
connectTimeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS: IClientOptions = {
|
||||||
|
protocolId: 'MQTT',
|
||||||
|
qos: 2,
|
||||||
|
clean: true,
|
||||||
|
connectTimeout: 30 * 1000,
|
||||||
|
username: 't_user',
|
||||||
|
password: 'njcnpqs'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解析项目内常见的 `{key:value,key2:value2}` MQTT 字符串消息 */
|
||||||
|
export function parseMqttPayload(str: string) {
|
||||||
|
const content = String(str).replace(/^{|}$/g, '')
|
||||||
|
const result: Record<string, string | number> = {}
|
||||||
|
const regex = /([^,:]+):([^,]+)(?=,|$)/g
|
||||||
|
let match
|
||||||
|
while ((match = regex.exec(content)) !== null) {
|
||||||
|
const key = match[1].trim()
|
||||||
|
let value = match[2].trim()
|
||||||
|
// 去掉包裹引号 systemCrc:"3477" / version:"V1"
|
||||||
|
if (
|
||||||
|
(value.startsWith('"') && value.endsWith('"')) ||
|
||||||
|
(value.startsWith("'") && value.endsWith("'"))
|
||||||
|
) {
|
||||||
|
value = value.slice(1, -1)
|
||||||
|
}
|
||||||
|
result[key] = isNaN(Number(value)) || value === '' ? value : Number(value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解码 mqtt 二进制消息为对象(兼容 JSON 字符串 / 自定义 key:value 串) */
|
||||||
|
export function decodeMqttMessage(message: Uint8Array) {
|
||||||
|
const text = new TextDecoder().decode(message)
|
||||||
|
let parsed: any = text
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(text)
|
||||||
|
} catch {
|
||||||
|
// 非 JSON,按原文字符串处理
|
||||||
|
}
|
||||||
|
// JSON.parse 后仍可能是内层字符串
|
||||||
|
if (typeof parsed === 'string') {
|
||||||
|
try {
|
||||||
|
const inner = JSON.parse(parsed)
|
||||||
|
parsed = typeof inner === 'string' ? parseMqttPayload(inner) : inner
|
||||||
|
} catch {
|
||||||
|
parsed = parseMqttPayload(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据 nowStep / allStep 计算进度百分比 */
|
||||||
|
export function calcMqttProgress(data: { nowStep?: number | string; allStep?: number | string }) {
|
||||||
|
const allStep = Number(data?.allStep) || 0
|
||||||
|
const nowStep = Number(data?.nowStep) || 0
|
||||||
|
if (!allStep) return 0
|
||||||
|
let percentage = parseInt(String((nowStep / allStep) * 100), 10)
|
||||||
|
if (percentage > 100) percentage = 100
|
||||||
|
if (percentage < 0) percentage = 0
|
||||||
|
return percentage
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MqttService {
|
||||||
|
private client: MqttClient | null = null
|
||||||
|
private messageHandler: MqttMessageHandler | null = null
|
||||||
|
private subscribedTopics = new Set<string>()
|
||||||
|
|
||||||
|
get connected() {
|
||||||
|
return !!this.client?.connected
|
||||||
|
}
|
||||||
|
|
||||||
|
get instance() {
|
||||||
|
return this.client
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 连接 MQTT,默认读取 localStorage.MQTTURL */
|
||||||
|
connect(options: MqttConnectOptions = {}) {
|
||||||
|
const url = options.url || window.localStorage.getItem('MQTTURL')
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('未配置 MQTT 地址')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.client?.connected) {
|
||||||
|
return this.client
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已有未连上的实例则先关掉
|
||||||
|
this.disconnect()
|
||||||
|
|
||||||
|
const clientOptions: IClientOptions = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
clientId: options.clientId || `mqttjs${Math.random()}`,
|
||||||
|
username: options.username ?? DEFAULT_OPTIONS.username,
|
||||||
|
password: options.password ?? DEFAULT_OPTIONS.password,
|
||||||
|
connectTimeout: options.connectTimeout ?? DEFAULT_OPTIONS.connectTimeout,
|
||||||
|
qos: options.qos ?? DEFAULT_OPTIONS.qos
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client = mqtt.connect(url, clientOptions)
|
||||||
|
|
||||||
|
this.client.on('message', (topic, message) => {
|
||||||
|
if (!this.messageHandler) return
|
||||||
|
const payload = decodeMqttMessage(message)
|
||||||
|
this.messageHandler(topic, payload, message)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.client.on('error', error => {
|
||||||
|
console.log('mqtt连接失败...', error)
|
||||||
|
this.disconnect()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.client.on('close', () => {
|
||||||
|
console.log('mqtt客户端已断开连接.....')
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.client
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 连接成功后订阅;若尚未连接则等 connect 事件 */
|
||||||
|
subscribe(topic: string | string[], qos: 0 | 1 | 2 = 2) {
|
||||||
|
const topics = Array.isArray(topic) ? topic : [topic]
|
||||||
|
topics.forEach(t => this.subscribedTopics.add(t))
|
||||||
|
|
||||||
|
const doSubscribe = () => {
|
||||||
|
if (!this.client) return
|
||||||
|
topics.forEach(t => {
|
||||||
|
this.client!.subscribe(t, { qos })
|
||||||
|
console.log('mqtt已订阅:', t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.client?.connected) {
|
||||||
|
doSubscribe()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client?.once('connect', () => {
|
||||||
|
console.log('mqtt客户端已连接....')
|
||||||
|
doSubscribe()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置消息回调(覆盖式) */
|
||||||
|
onMessage(handler: MqttMessageHandler) {
|
||||||
|
this.messageHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取消订阅 */
|
||||||
|
unsubscribe(topic?: string | string[]) {
|
||||||
|
if (!this.client) return
|
||||||
|
const topics = topic
|
||||||
|
? Array.isArray(topic)
|
||||||
|
? topic
|
||||||
|
: [topic]
|
||||||
|
: [...this.subscribedTopics]
|
||||||
|
topics.forEach(t => {
|
||||||
|
this.client?.unsubscribe(t)
|
||||||
|
this.subscribedTopics.delete(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 断开并清理 */
|
||||||
|
disconnect() {
|
||||||
|
if (this.client) {
|
||||||
|
try {
|
||||||
|
this.client.end(true)
|
||||||
|
} catch (e) {}
|
||||||
|
this.client = null
|
||||||
|
}
|
||||||
|
this.subscribedTopics.clear()
|
||||||
|
this.messageHandler = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 快捷创建:连接并订阅指定 topic */
|
||||||
|
export function createMqttClient(
|
||||||
|
topic: string | string[],
|
||||||
|
onMessage: MqttMessageHandler,
|
||||||
|
options?: MqttConnectOptions
|
||||||
|
) {
|
||||||
|
const service = new MqttService()
|
||||||
|
service.connect(options)
|
||||||
|
service.onMessage(onMessage)
|
||||||
|
service.subscribe(topic)
|
||||||
|
return service
|
||||||
|
}
|
||||||
@@ -320,7 +320,7 @@ getCldTree().then(res => {
|
|||||||
item.icon = 'el-icon-HomeFilled'
|
item.icon = 'el-icon-HomeFilled'
|
||||||
item.color = config.getColorVal('elementUiPrimary')
|
item.color = config.getColorVal('elementUiPrimary')
|
||||||
item.children.forEach((item: any) => {
|
item.children.forEach((item: any) => {
|
||||||
item.icon = 'el-icon-List'
|
item.icon = 'el-icon-Menu'
|
||||||
item.color = config.getColorVal('elementUiPrimary')
|
item.color = config.getColorVal('elementUiPrimary')
|
||||||
item.children.forEach((item2: any) => {
|
item.children.forEach((item2: any) => {
|
||||||
item2.icon = 'el-icon-Platform'
|
item2.icon = 'el-icon-Platform'
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
<Table v-if="view" ref="tableRef"></Table>
|
<Table v-if="view" ref="tableRef"></Table>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else description="请选择设备" class="analyze-dvr-right" />
|
<el-empty v-else description="请选择数据" class="analyze-dvr-right" />
|
||||||
|
|
||||||
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
|
<MultiCondition v-model:visible="filterVisible" :params="tableStore.table.params" ref="multiConditionRef"
|
||||||
@confirm="onFilterConfirm" />
|
@confirm="onFilterConfirm" />
|
||||||
|
|||||||
@@ -410,7 +410,6 @@ import {
|
|||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import analysisList from './analysisList/index.vue'
|
import analysisList from './analysisList/index.vue'
|
||||||
import mqtt from 'mqtt'
|
import mqtt from 'mqtt'
|
||||||
import { json } from 'node:stream/consumers'
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'govern/device/control'
|
name: 'govern/device/control'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -75,12 +75,12 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" title="序号" width="80"></vxe-column>
|
<vxe-column type="seq" title="序号" width="80"></vxe-column>
|
||||||
<vxe-column field="name" title="指标名称"></vxe-column>
|
<vxe-column field="name" title="指标名称" minWidth="200"></vxe-column>
|
||||||
<vxe-column field="phasic" title="相别"></vxe-column>
|
<vxe-column field="phasic" title="相别" minWidth="150"></vxe-column>
|
||||||
<vxe-column field="type" title="数据类型"></vxe-column>
|
<vxe-column field="type" title="数据类型" minWidth="100"></vxe-column>
|
||||||
<vxe-column field="unit" title="单位"></vxe-column>
|
<vxe-column field="unit" title="单位" minWidth="100"></vxe-column>
|
||||||
<vxe-column field="startTimes" title="开始次数"></vxe-column>
|
<vxe-column field="startTimes" title="开始次数"minWidth="100"></vxe-column>
|
||||||
<vxe-column field="endTimes" title="结束次数"></vxe-column>
|
<vxe-column field="endTimes" title="结束次数" minWidth="100"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
</div>
|
</div>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export default {
|
|||||||
// item2.color = config.getColorVal('elementUiPrimary')
|
// item2.color = config.getColorVal('elementUiPrimary')
|
||||||
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
|
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
|
||||||
item2.children.forEach(item3 => {
|
item2.children.forEach(item3 => {
|
||||||
item3.icon = 'el-icon-List'
|
item3.icon = 'el-icon-Menu'
|
||||||
item3.color = config.getColorVal('elementUiPrimary')
|
item3.color = config.getColorVal('elementUiPrimary')
|
||||||
// if (item3.runStatus === 1) {
|
// if (item3.runStatus === 1) {
|
||||||
// item3.color = '#e26257 !important'
|
// item3.color = '#e26257 !important'
|
||||||
@@ -110,7 +110,7 @@ export default {
|
|||||||
// item2.color = config.getColorVal('elementUiPrimary')
|
// item2.color = config.getColorVal('elementUiPrimary')
|
||||||
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
|
item2.color = item.runStatus == 2 ? '#2ab914' : "#e26257"
|
||||||
item2.children.forEach(item3 => {
|
item2.children.forEach(item3 => {
|
||||||
item3.icon = 'el-icon-List'
|
item3.icon = 'el-icon-Menu'
|
||||||
item3.color = config.getColorVal('elementUiPrimary')
|
item3.color = config.getColorVal('elementUiPrimary')
|
||||||
// if (item3.runStatus === 1) {
|
// if (item3.runStatus === 1) {
|
||||||
// item3.color = '#e26257 !important'
|
// item3.color = '#e26257 !important'
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const tableStore = new TableStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: '异常时间', field: 'evtTime', align: 'center', sortable: true },
|
{ title: '异常时间', field: 'evtTime', align: 'center', sortable: true },
|
||||||
{ title: '设备名称', field: 'ndid', align: 'center' },
|
{ title: '网络设备ID', field: 'ndid', align: 'center' },
|
||||||
{
|
{
|
||||||
title: '告警代码',
|
title: '告警代码',
|
||||||
field: 'code',
|
field: 'code',
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog draggable class="cn-operate-dialog" width="800px" v-model.trim="dialogVisible" :title="title"
|
<el-dialog
|
||||||
@close="emit('closePopup')">
|
draggable
|
||||||
|
class="cn-operate-dialog"
|
||||||
|
width="800px"
|
||||||
|
v-model.trim="dialogVisible"
|
||||||
|
:title="title"
|
||||||
|
@close="emit('closePopup')"
|
||||||
|
>
|
||||||
<el-form class="form-two" :model="form" label-width="100px" ref="formRef" :rules="rules">
|
<el-form class="form-two" :model="form" label-width="100px" ref="formRef" :rules="rules">
|
||||||
<el-form-item label="设备类型" prop="type">
|
<el-form-item label="设备类型" prop="type">
|
||||||
|
|
||||||
<el-select v-model.trim="form.type" placeholder="请选择设备类型" @change="formDevTypeChange" clearable>
|
<el-select v-model.trim="form.type" placeholder="请选择设备类型" @change="formDevTypeChange" clearable>
|
||||||
<el-option v-for="item in devTypeOptions" :key="item.value" :label="item.label"
|
<el-option
|
||||||
:value="item.value"></el-option>
|
v-for="item in devTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设备型号" prop="devType">
|
<el-form-item label="设备型号" prop="devType">
|
||||||
@@ -14,43 +23,94 @@
|
|||||||
<el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name"
|
<el-option v-for="item in DevTypeOptions" :key="item.id" :label="item.name"
|
||||||
:value="item.id"></el-option>
|
:value="item.id"></el-option>
|
||||||
</el-select> -->
|
</el-select> -->
|
||||||
<el-select v-model.trim="form.devType" filterable placeholder="请选择设备型号" clearable
|
<el-select
|
||||||
@change="devTypeChange">
|
v-model.trim="form.devType"
|
||||||
<el-option v-for="item in formDevModelOptionsFilter" :key="item.value" :label="item.label"
|
filterable
|
||||||
:value="item.value"></el-option>
|
placeholder="请选择设备型号"
|
||||||
|
clearable
|
||||||
|
@change="devTypeChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in formDevModelOptionsFilter"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="版本号" prop="versionNo">
|
<el-form-item label="版本号" prop="versionNo">
|
||||||
<el-input maxlength="32" show-word-limit v-model.trim="form.versionNo" autocomplete="off"
|
<el-input
|
||||||
placeholder="请输入版本号"></el-input>
|
maxlength="32"
|
||||||
|
show-word-limit
|
||||||
|
v-model.trim="form.versionNo"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="请输入版本号"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="版本类型" prop="versionType">
|
<el-form-item label="版本类型" prop="versionType">
|
||||||
<el-input maxlength="32" show-word-limit v-model.trim="form.versionType" autocomplete="off"
|
<el-input
|
||||||
placeholder="填写特殊类型(不填默认通用类型)"></el-input>
|
maxlength="32"
|
||||||
|
show-word-limit
|
||||||
|
v-model.trim="form.versionType"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="填写特殊类型(不填默认通用类型)"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="协议版本" prop="versionAgreement">
|
<el-form-item label="协议版本" prop="versionAgreement">
|
||||||
<el-input maxlength="32" show-word-limit v-model.trim="form.versionAgreement" autocomplete="off"
|
<el-input
|
||||||
placeholder="请输入协议版本"></el-input>
|
maxlength="32"
|
||||||
|
show-word-limit
|
||||||
|
v-model.trim="form.versionAgreement"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="请输入协议版本"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="版本日期" prop="versionDate">
|
<el-form-item label="版本日期" prop="versionDate">
|
||||||
<el-date-picker v-model.trim="form.versionDate" type="date" value-format="YYYY-MM-DD"
|
<el-date-picker
|
||||||
placeholder="选择日期时间" style="width: 100%"></el-date-picker>
|
v-model.trim="form.versionDate"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="选择日期时间"
|
||||||
|
style="width: 100%"
|
||||||
|
></el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="CRC校验" prop="crc">
|
<el-form-item label="CRC校验" prop="crc">
|
||||||
<el-input maxlength="32" show-word-limit v-model.trim="form.crc" autocomplete="off"
|
<el-input
|
||||||
placeholder="请输入CRC校验"></el-input>
|
maxlength="32"
|
||||||
|
show-word-limit
|
||||||
|
v-model.trim="form.crc"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="请输入CRC校验"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-form :model="form" label-width="100px" class="mt10 form-one">
|
<el-form :model="form" label-width="100px" class="mt10 form-one">
|
||||||
<el-form-item label="描述" prop="description">
|
<el-form-item label="描述" prop="description">
|
||||||
<el-input show-word-limit maxlength="300" :autosize="{ minRows: 2, maxRows: 4 }" type="textarea"
|
<el-input
|
||||||
v-model.trim="form.description" autocomplete="off" placeholder="请输入描述"></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>
|
||||||
<el-form-item label="升级文件" prop="file">
|
<el-form-item label="升级文件" prop="file">
|
||||||
<el-upload :limit="1" accept=".bin" :auto-upload="false" :on-change="fileChange" :on-exceed="fileExceed"
|
<el-upload
|
||||||
:on-remove="fileRemove" :file-list="fileList">
|
:limit="1"
|
||||||
|
accept=".bin,.zip,.tar"
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="fileChange"
|
||||||
|
:on-exceed="fileExceed"
|
||||||
|
:on-remove="fileRemove"
|
||||||
|
:file-list="fileList"
|
||||||
|
>
|
||||||
<el-button type="primary">点击上传</el-button>
|
<el-button type="primary">点击上传</el-button>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">支持 .bin 或压缩包(.zip / .tar)</div>
|
||||||
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -86,7 +146,7 @@ const form = reactive<any>({
|
|||||||
devTypeName: null,
|
devTypeName: null,
|
||||||
versionAgreement: null,
|
versionAgreement: null,
|
||||||
versionNo: null,
|
versionNo: null,
|
||||||
versionType: null,
|
versionType: '通用',
|
||||||
crc: null,
|
crc: null,
|
||||||
description: null,
|
description: null,
|
||||||
versionDate: null,
|
versionDate: null,
|
||||||
@@ -170,11 +230,13 @@ const fileExceed = (e: any) => {
|
|||||||
ElMessage.error('只能上传一个文件')
|
ElMessage.error('只能上传一个文件')
|
||||||
}
|
}
|
||||||
// 处理上传前检查
|
// 处理上传前检查
|
||||||
|
const ALLOWED_EXTS = ['.bin', '.zip', '.tar']
|
||||||
const beforeUpload = (file: any) => {
|
const beforeUpload = (file: any) => {
|
||||||
const isWord = file.name.endsWith('.bin')
|
const name = (file?.name || '').toLowerCase()
|
||||||
|
const ok = ALLOWED_EXTS.some(ext => name.endsWith(ext))
|
||||||
|
|
||||||
if (!isWord) {
|
if (!ok) {
|
||||||
ElMessage.error('请上传(.bin)格式文件!')
|
ElMessage.error('请上传 .bin 或压缩包文件(.zip / .tar)!')
|
||||||
fileList.value = []
|
fileList.value = []
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -212,15 +274,16 @@ const open = async (text: string, data?: anyObj) => {
|
|||||||
for (let key in form) {
|
for (let key in form) {
|
||||||
form[key] = null
|
form[key] = null
|
||||||
}
|
}
|
||||||
|
form.versionType = '通用'
|
||||||
|
form.file = []
|
||||||
|
fileList.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
// console.log(formRef)
|
// console.log(formRef)
|
||||||
formRef.value.validate(async (valid: boolean) => {
|
formRef.value.validate(async (valid: boolean) => {
|
||||||
// console.log(valid)
|
// console.log(valid)
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|
||||||
if (fileList.value.length == 0) {
|
if (fileList.value.length == 0) {
|
||||||
ElMessage.warning('请上传升级文件')
|
ElMessage.warning('请上传升级文件')
|
||||||
return
|
return
|
||||||
@@ -231,15 +294,18 @@ const submit = () => {
|
|||||||
submitForm.append(key, form[key])
|
submitForm.append(key, form[key])
|
||||||
}
|
}
|
||||||
if (form.id) {
|
if (form.id) {
|
||||||
await auditEdData(form)
|
await auditEdData(form).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await addEdData(form)
|
await addEdData(form).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
tableStore.index()
|
tableStore.index()
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
emit('closePopup')
|
emit('closePopup')
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="default-main manage-realTime" :style="{ height: pageHeight.height }">
|
<div class="og-realtime">
|
||||||
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange=""></DeviceTree>
|
|
||||||
<div class="manage-realTime-right">
|
|
||||||
<div class="time-container">
|
<div class="time-container">
|
||||||
<div>
|
<div>
|
||||||
<div>系统时间:{{ realTime }}</div>
|
<div>系统时间:{{ realTime }}</div>
|
||||||
@@ -13,7 +11,7 @@
|
|||||||
<MyEchart :options="echartsData" />
|
<MyEchart :options="echartsData" />
|
||||||
</div>
|
</div>
|
||||||
<div class="pl10 pr10">
|
<div class="pl10 pr10">
|
||||||
<el-table :data="tableData" border stripe :show-header="false" class="custom-table">
|
<el-table :data="tableData" border stripe :show-header="false" size="small" class="custom-table">
|
||||||
<el-table-column prop="label1" />
|
<el-table-column prop="label1" />
|
||||||
<el-table-column prop="label2" />
|
<el-table-column prop="label2" />
|
||||||
<el-table-column prop="value1" />
|
<el-table-column prop="value1" />
|
||||||
@@ -24,27 +22,30 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||||
import { mainHeight } from '@/utils/layout'
|
import { mainHeight } from '@/utils/layout'
|
||||||
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
|
|
||||||
import MyEchart from '@/components/echarts/MyEchart.vue'
|
import MyEchart from '@/components/echarts/MyEchart.vue'
|
||||||
import { formatToDateTime } from '@/utils/dateUtil'
|
import { formatToDateTime } from '@/utils/dateUtil'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'manage/realTime'
|
name: 'operationalguidance/realTime'
|
||||||
})
|
})
|
||||||
|
|
||||||
//页面属性
|
const props = defineProps<{
|
||||||
const pageHeight = mainHeight(20)
|
device?: anyObj
|
||||||
const echartHeight = mainHeight(180)
|
}>()
|
||||||
|
|
||||||
|
const echartHeight = mainHeight(185)
|
||||||
const realTime = ref<string>('')
|
const realTime = ref<string>('')
|
||||||
const deviceTime = ref<string>('')
|
const deviceTime = ref<string>('')
|
||||||
const timer = ref<any>(null)
|
const timer = ref<ReturnType<typeof setInterval> | null>(null)
|
||||||
|
|
||||||
const echartsData = ref<any>({
|
const echartsData = ref<any>({
|
||||||
title: {
|
title: {
|
||||||
text: '终端性能'
|
text: '设备性能'
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
@@ -63,7 +64,6 @@ const echartsData = ref<any>({
|
|||||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
legend: {
|
legend: {
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
data: ['CPU1使用率', 'CPU2使用率', '内存使用率', '磁盘使用率']
|
data: ['CPU1使用率', 'CPU2使用率', '内存使用率', '磁盘使用率']
|
||||||
@@ -181,23 +181,23 @@ const tableData = ref([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const nodeClick = async (e: anyObj) => {
|
watch(
|
||||||
console.log('点击设备树节点')
|
() => props.device,
|
||||||
}
|
() => {
|
||||||
|
// 设备切换时预留刷新入口
|
||||||
const handleClose = () => {
|
}
|
||||||
console.log('close')
|
)
|
||||||
}
|
|
||||||
|
|
||||||
const synchronizeTime = async () => {
|
const synchronizeTime = async () => {
|
||||||
console.log('对时')
|
console.log('对时')
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
timer.value = setInterval(() => {
|
timer.value = setInterval(() => {
|
||||||
realTime.value = formatToDateTime(new Date())
|
realTime.value = formatToDateTime(new Date())
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
// 在组件卸载时清除定时器
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (timer.value) {
|
if (timer.value) {
|
||||||
clearInterval(timer.value)
|
clearInterval(timer.value)
|
||||||
@@ -205,23 +205,18 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
|
||||||
.manage-realTime {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&-right {
|
<style scoped lang="scss">
|
||||||
|
.og-realtime {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1;
|
|
||||||
padding: 10px 10px 10px 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border: 1px solid #ebeef5; // 加个边框方便查看效果
|
|
||||||
|
|
||||||
.time-container {
|
.time-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end; // 让内容靠右
|
justify-content: flex-end;
|
||||||
align-items: center; // 让内容垂直居中(避免高度不一致时偏移)
|
align-items: center;
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
@@ -236,7 +231,6 @@ onUnmounted(() => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1. 加深表格边框线条 */
|
|
||||||
:deep(.custom-table.el-table) {
|
:deep(.custom-table.el-table) {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #dcdfe6;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -246,18 +240,9 @@ onUnmounted(() => {
|
|||||||
border: 0.5px solid #dcdfe6;
|
border: 0.5px solid #dcdfe6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. 第 1、2、4、6 列显示深色背景 */
|
|
||||||
:deep(.custom-table .el-table__row .el-table__cell:nth-child(1)) {
|
:deep(.custom-table .el-table__row .el-table__cell:nth-child(1)) {
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 第 3、5、7 列显示白色背景 */
|
|
||||||
:deep(.custom-table .el-table__row .el-table__cell:nth-child(3)),
|
|
||||||
:deep(.custom-table .el-table__row .el-table__cell:nth-child(5)),
|
|
||||||
:deep(.custom-table .el-table__row .el-table__cell:nth-child(7)) {
|
|
||||||
// background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<div class="og-setting">
|
||||||
|
<div class="og-setting__toolbar">
|
||||||
|
<el-button type="primary" icon="el-icon-Tickets" @click="readExternal">读取外部定值</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-Edit" @click="modifySetting">修改定值</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-tabs v-model="activeTab" type="border-card" class="og-setting__tabs" @tab-change="onTabChange">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.name"
|
||||||
|
:label="tab.label"
|
||||||
|
:name="tab.name"
|
||||||
|
>
|
||||||
|
<div class="og-setting__table" v-loading="loading">
|
||||||
|
<vxe-table
|
||||||
|
v-bind="defaultAttribute"
|
||||||
|
:data="tableMap[tab.name]"
|
||||||
|
height="auto"
|
||||||
|
|
||||||
|
>
|
||||||
|
<vxe-column type="seq" title="序号" width="90" sortable />
|
||||||
|
<vxe-column field="name" title="定值名称" min-width="160" />
|
||||||
|
<vxe-column field="value" title="当前定值" min-width="140" />
|
||||||
|
<vxe-column field="range" title="定值范围" min-width="160" />
|
||||||
|
</vxe-table>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { defaultAttribute } from '@/components/table/defaultAttribute'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'operationalguidance/settingManage'
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
device?: anyObj
|
||||||
|
}>()
|
||||||
|
|
||||||
|
type TabName = 'basic' | 'harmonicCurrent' | 'harmonicVoltage' | 'interharmonic'
|
||||||
|
|
||||||
|
const tabs: { label: string; name: TabName }[] = [
|
||||||
|
{ label: '基本定值', name: 'basic' },
|
||||||
|
{ label: '谐波电流', name: 'harmonicCurrent' },
|
||||||
|
{ label: '谐波电压', name: 'harmonicVoltage' },
|
||||||
|
{ label: '间谐波定值', name: 'interharmonic' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const activeTab = ref<TabName>('basic')
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableMap = reactive<Record<TabName, anyObj[]>>({
|
||||||
|
basic: [],
|
||||||
|
harmonicCurrent: [],
|
||||||
|
harmonicVoltage: [],
|
||||||
|
interharmonic: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadData = async (tab: TabName = activeTab.value) => {
|
||||||
|
if (!props.device?.id) {
|
||||||
|
tableMap[tab] = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// TODO: 按设备 + 定值类型请求接口
|
||||||
|
tableMap[tab] = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTabChange = (name: string | number) => {
|
||||||
|
loadData(name as TabName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const readExternal = () => {
|
||||||
|
if (!props.device?.id) {
|
||||||
|
ElMessage.warning('请先选择设备')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ElMessage.info('读取外部定值')
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifySetting = () => {
|
||||||
|
if (!props.device?.id) {
|
||||||
|
ElMessage.warning('请先选择设备')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ElMessage.info('修改定值')
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.device,
|
||||||
|
() => loadData(),
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.og-setting {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tabs {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tab-pane) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__item) {
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__table {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
padding:0 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
72
src/views/govern/manage/operationalguidance/index.vue
Normal file
72
src/views/govern/manage/operationalguidance/index.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="default-main manage-operationalguidance" :style="{ height: pageHeight.height }">
|
||||||
|
<DeviceTree @init="nodeClick" @deviceTypeChange="onDeviceTypeChange" />
|
||||||
|
<div class="manage-operationalguidance-right">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card" class="og-tabs">
|
||||||
|
<el-tab-pane label="实时状态" name="realTime" lazy>
|
||||||
|
<RealTime :device="currentDevice" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="定值管理" name="setting" lazy>
|
||||||
|
<SettingManage :device="currentDevice" />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { mainHeight } from '@/utils/layout'
|
||||||
|
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
|
||||||
|
import RealTime from './comp/realTime.vue'
|
||||||
|
import SettingManage from './comp/settingManage.vue'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'operationalguidance'
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageHeight = mainHeight(20)
|
||||||
|
const activeTab = ref('realTime')
|
||||||
|
const currentDevice = ref<anyObj>()
|
||||||
|
|
||||||
|
const nodeClick = (e?: anyObj) => {
|
||||||
|
if (e) currentDevice.value = e
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDeviceTypeChange = (_type: any, device?: anyObj) => {
|
||||||
|
if (device) currentDevice.value = device
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.manage-operationalguidance {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 10px 10px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.og-tabs {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tab-pane) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,68 +1,135 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
|
<el-dialog
|
||||||
|
draggable
|
||||||
|
width="520px"
|
||||||
|
v-model.trim="dialogVisible"
|
||||||
|
:title="title"
|
||||||
|
:close-on-click-modal="!mqttProgressing"
|
||||||
|
@closed="onClosed"
|
||||||
|
>
|
||||||
<el-form :model="form" class="form-one" label-width="auto" ref="formRef" :rules="rules">
|
<el-form :model="form" class="form-one" label-width="auto" ref="formRef" :rules="rules">
|
||||||
<el-form-item label="版本" prop="edDataId">
|
<el-form-item label="版本" prop="edDataId">
|
||||||
<el-select v-model.trim="form.edDataId" placeholder="请选择版本" @change="selectChange">
|
<el-select
|
||||||
<el-option v-for="item in devList" :disabled="version == item.versionNo"
|
v-model.trim="form.edDataId"
|
||||||
:label="`${item.devTypeName}_${item.versionNo}_${item.versionAgreement}_${item.versionDate}`"
|
placeholder="请选择版本"
|
||||||
:value="item.id"></el-option>
|
:disabled="mqttProgressing"
|
||||||
|
@change="selectChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in devList"
|
||||||
|
:key="item.id"
|
||||||
|
:disabled="version == item.versionNo"
|
||||||
|
:label="formatVersionLabel(item)"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="描述">
|
<el-form-item label="描述">
|
||||||
<el-input maxlength="300" show-word-limit v-model.trim="versionDesc" disabled type="textarea" :rows="3"></el-input>
|
<el-input
|
||||||
|
maxlength="300"
|
||||||
|
show-word-limit
|
||||||
|
v-model.trim="versionDesc"
|
||||||
|
disabled
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 仅 MQTT:上传成功后在输入框下方显示升级进度 -->
|
||||||
|
<div v-if="mqttVisible" class="mqtt-upgrade">
|
||||||
|
<div class="mqtt-upgrade__label">升级版本</div>
|
||||||
|
<el-progress
|
||||||
|
:percentage="mqttProgress"
|
||||||
|
:stroke-width="12"
|
||||||
|
striped
|
||||||
|
striped-flow
|
||||||
|
:status="mqttProgress >= 100 ? 'success' : undefined"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="mqttProgress >= 100">
|
||||||
|
<div class="mqtt-upgrade__codes">
|
||||||
|
<div>升级文件校验码:{{ deviceCrc || '-' }}</div>
|
||||||
|
<div>归档文件校验码:{{ verifyCode || '获取中...' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="codeReady" class="mqtt-upgrade__actions">
|
||||||
|
<template v-if="codeMatched">
|
||||||
|
<el-button type="primary" :loading="confirmLoading" @click="confirmUpgrade">确认升级</el-button>
|
||||||
|
<el-button @click="cancelUpgrade">取消升级</el-button>
|
||||||
|
</template>
|
||||||
|
<el-alert v-else type="error" show-icon :closable="false" title="校验码不一致不能升级" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="handleCancel" :disabled="confirmLoading">取消</el-button>
|
||||||
<el-button type="primary" @click="submit" :loading="loading">确定</el-button>
|
<el-button v-if="!mqttVisible" type="primary" @click="submit" :loading="loading">确定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, inject, reactive } from 'vue'
|
import { ref, reactive, computed, onBeforeUnmount } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { findByDevTypeId, upgrade } from '@/api/system-boot/dictTree'
|
import { findByDevTypeId, upgrade, uploadUpgradeFile, commitUpgradeFile } from '@/api/system-boot/dictTree'
|
||||||
|
import MqttService, { createMqttClient, calcMqttProgress } from '@/utils/MQTT'
|
||||||
|
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const title = ref('程序版本')
|
const title = ref('程序版本')
|
||||||
const devList: any = ref([])
|
const devList: any = ref([])
|
||||||
const versionDesc = ref('')
|
const versionDesc = ref('')
|
||||||
const version = ref('')
|
const version = ref('')
|
||||||
// 表单数据(新增了版本类型、版本描述字段)
|
|
||||||
const form = reactive<any>({
|
const form = reactive<any>({
|
||||||
devId: [],
|
devId: [] as any[],
|
||||||
edDataId: '' // 新增:下拉选择版本
|
edDataId: ''
|
||||||
})
|
})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
// 校验规则(新增两个字段的必填校验)
|
|
||||||
const rules = {
|
const rules = {
|
||||||
// 新增校验
|
|
||||||
edDataId: [{ required: true, message: '请选择版本', trigger: 'change' }]
|
edDataId: [{ required: true, message: '请选择版本', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
|
const row = ref<any>({})
|
||||||
|
|
||||||
|
/** MQTT 升级进度 */
|
||||||
|
const mqttVisible = ref(false)
|
||||||
|
const mqttProgress = ref(0)
|
||||||
|
/** 升级文件校验码 = mqtt.devCrc */
|
||||||
|
const deviceCrc = ref('')
|
||||||
|
/** 归档文件校验码 = mqtt.systemCrc */
|
||||||
|
const verifyCode = ref('')
|
||||||
|
/** mqtt crcCheck 带回的 mid / version,确认升级用 */
|
||||||
|
const mqttMid = ref<string | number>('')
|
||||||
|
const mqttVersion = ref('')
|
||||||
|
const confirmLoading = ref(false)
|
||||||
|
const mqttService = ref<MqttService | null>(null)
|
||||||
|
|
||||||
|
const mqttProgressing = computed(() => mqttVisible.value && mqttProgress.value < 100)
|
||||||
|
const codeReady = computed(() => !!(deviceCrc.value && verifyCode.value))
|
||||||
|
const codeMatched = computed(() => codeReady.value && String(deviceCrc.value) === String(verifyCode.value))
|
||||||
|
|
||||||
|
const getDevIdStr = () => (Array.isArray(form.devId) ? form.devId.join() : String(form.devId || ''))
|
||||||
|
|
||||||
// 打开弹窗
|
|
||||||
const open = (data: any) => {
|
const open = (data: any) => {
|
||||||
|
resetMqttState()
|
||||||
loading.value = false
|
loading.value = false
|
||||||
version.value = data.version
|
version.value = data.version
|
||||||
form.devId = []
|
|
||||||
form.edDataId = ''
|
form.edDataId = ''
|
||||||
versionDesc.value = ''
|
versionDesc.value = ''
|
||||||
|
row.value = data
|
||||||
|
form.devId = Array.isArray(data.id) ? data.id : [data.id]
|
||||||
getFindByDev(data.devModel)
|
getFindByDev(data.devModel)
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
form.devId = data.id
|
|
||||||
}
|
}
|
||||||
// 获取版本
|
|
||||||
const getFindByDev = (devModel: string) => {
|
const getFindByDev = (devModel: string) => {
|
||||||
findByDevTypeId({
|
findByDevTypeId({
|
||||||
devType: devModel, //'45afe3ad9970785090cd7da27f489c37',
|
devType: devModel,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10000
|
pageSize: 10000
|
||||||
}).then((res: any) => {
|
}).then((res: any) => {
|
||||||
@@ -73,42 +140,184 @@ const getFindByDev = (devModel: string) => {
|
|||||||
} else if (devList.value.length == 1 && devList.value[0].versionNo == version.value) {
|
} else if (devList.value.length == 1 && devList.value[0].versionNo == version.value) {
|
||||||
ElMessage.warning('该设备没有可升级的版本!')
|
ElMessage.warning('该设备没有可升级的版本!')
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 提交
|
|
||||||
|
function disconnectMqtt() {
|
||||||
|
mqttService.value?.disconnect()
|
||||||
|
mqttService.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMqttState() {
|
||||||
|
disconnectMqtt()
|
||||||
|
mqttVisible.value = false
|
||||||
|
mqttProgress.value = 0
|
||||||
|
deviceCrc.value = ''
|
||||||
|
verifyCode.value = ''
|
||||||
|
mqttMid.value = ''
|
||||||
|
mqttVersion.value = ''
|
||||||
|
confirmLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectUpgradeMqtt(ndid: string) {
|
||||||
|
try {
|
||||||
|
const topic = `/Web/Progress/UpgradeFile/${ndid}`
|
||||||
|
mqttService.value = createMqttClient(topic, (_topic, data) => {
|
||||||
|
// 进度消息
|
||||||
|
if (data?.allStep != null || data?.nowStep != null) {
|
||||||
|
mqttProgress.value = calcMqttProgress(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type=crcCheck 时取校验码及确认升级参数
|
||||||
|
if (data?.type === 'crcCheck') {
|
||||||
|
mqttProgress.value = 100
|
||||||
|
deviceCrc.value = data.devCrc != null ? String(data.devCrc) : ''
|
||||||
|
verifyCode.value = data.systemCrc != null ? String(data.systemCrc) : ''
|
||||||
|
mqttMid.value = data.mid ?? ''
|
||||||
|
mqttVersion.value = data.version != null ? String(data.version) : ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || 'MQTT 连接失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
formRef.value.validate(async (valid: boolean) => {
|
formRef.value.validate(async (valid: boolean) => {
|
||||||
if (valid) {
|
if (!valid) return
|
||||||
try {
|
try {
|
||||||
ElMessage.info('升级中,请稍等...')
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
if (row.value.accessMethod == 'CLD') {
|
||||||
|
ElMessage.info('升级中,请稍等...')
|
||||||
upgrade({
|
upgrade({
|
||||||
devId: form.devId.join(),
|
devId: getDevIdStr(),
|
||||||
edDataId: form.edDataId
|
edDataId: form.edDataId
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
ElMessage.success(res.message)
|
ElMessage.success(res.message)
|
||||||
loading.value = false
|
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
})
|
})
|
||||||
|
} else if (row.value.accessMethod == 'MQTT') {
|
||||||
|
ElMessage.info('正在上传升级文件...')
|
||||||
|
uploadUpgradeFile({
|
||||||
|
id: getDevIdStr(),
|
||||||
|
ndid: row.value.ndid
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
ElMessage.success(res.message || '上传成功')
|
||||||
|
loading.value = false
|
||||||
|
mqttVisible.value = true
|
||||||
|
mqttProgress.value = 0
|
||||||
|
deviceCrc.value = ''
|
||||||
|
verifyCode.value = ''
|
||||||
|
mqttMid.value = ''
|
||||||
|
mqttVersion.value = ''
|
||||||
|
connectUpgradeMqtt(row.value.ndid)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('未知的接入方式')
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
// ElMessage.error(error.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const selectChange = e => {
|
|
||||||
devList.value.forEach(item => {
|
|
||||||
if (item.id === e) {
|
|
||||||
versionDesc.value = item.description
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const confirmUpgrade = () => {
|
||||||
|
if (!codeMatched.value) {
|
||||||
|
ElMessage.warning('校验码不一致不能升级')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (mqttMid.value === '' || mqttMid.value == null || !mqttVersion.value) {
|
||||||
|
ElMessage.warning('未获取到升级参数,请稍后重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
confirmLoading.value = true
|
||||||
|
commitUpgradeFile({
|
||||||
|
mid: mqttMid.value,
|
||||||
|
version: mqttVersion.value,
|
||||||
|
nDid: row.value.ndid
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
ElMessage.success(res.message || '升级成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
confirmLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelUpgrade = () => {
|
||||||
|
resetMqttState()
|
||||||
|
ElMessage.info('已取消升级')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
if (mqttVisible.value) {
|
||||||
|
cancelUpgrade()
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectChange = (e: string) => {
|
||||||
|
const item = devList.value.find((v: any) => v.id === e)
|
||||||
|
versionDesc.value = item?.description || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatVersionLabel = (item: any) => {
|
||||||
|
const label = `${item.devTypeName}_${item.versionNo}_${item.versionAgreement}_${item.versionDate}`
|
||||||
|
return version.value == item.versionNo ? `${label}(当前版本)` : label
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClosed = () => {
|
||||||
|
resetMqttState()
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
disconnectMqtt()
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mqtt-upgrade {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid var(--el-border-color-lighter);
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1d2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__codes {
|
||||||
|
margin-top: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4e5969;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
margin-top: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<TableHeader ref="tableHeaderRef" showExport showCustomColumn>
|
<TableHeader ref="tableHeaderRef" showExport showCustomColumn>
|
||||||
<template #select>
|
<template #select>
|
||||||
@@ -87,6 +87,8 @@ import { Share, Document } from '@element-plus/icons-vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import upgrade from './comp/upgrade.vue'
|
import upgrade from './comp/upgrade.vue'
|
||||||
import deviceLog from './comp/deviceLog.vue'
|
import deviceLog from './comp/deviceLog.vue'
|
||||||
|
import { reboot, rebootDevice } from '@/api/access-boot/analyzeModel'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'govern/manage/programVersion'
|
name: 'govern/manage/programVersion'
|
||||||
})
|
})
|
||||||
@@ -113,6 +115,7 @@ const tableStore: any = new TableStore({
|
|||||||
},
|
},
|
||||||
|
|
||||||
{ title: '设备名称', field: 'name', minWidth: '150' },
|
{ title: '设备名称', field: 'name', minWidth: '150' },
|
||||||
|
{ title: '网络设备ID', field: 'ndid', minWidth: '150' },
|
||||||
{
|
{
|
||||||
title: '设备型号',
|
title: '设备型号',
|
||||||
field: 'devModelName',
|
field: 'devModelName',
|
||||||
@@ -154,7 +157,6 @@ const tableStore: any = new TableStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title: 'icd模型',
|
title: 'icd模型',
|
||||||
field: 'icd',
|
field: 'icd',
|
||||||
@@ -167,7 +169,7 @@ const tableStore: any = new TableStore({
|
|||||||
{
|
{
|
||||||
title: '所属工程',
|
title: '所属工程',
|
||||||
field: 'associatedEngineering',
|
field: 'associatedEngineering',
|
||||||
minWidth: '120',
|
minWidth: '150',
|
||||||
formatter: (row: any) => {
|
formatter: (row: any) => {
|
||||||
return row.cellValue || '/'
|
return row.cellValue || '/'
|
||||||
}
|
}
|
||||||
@@ -175,7 +177,7 @@ const tableStore: any = new TableStore({
|
|||||||
{
|
{
|
||||||
title: '所属项目',
|
title: '所属项目',
|
||||||
field: 'associatedProject',
|
field: 'associatedProject',
|
||||||
minWidth: '120',
|
minWidth: '150',
|
||||||
formatter: (row: any) => {
|
formatter: (row: any) => {
|
||||||
return row.cellValue || '/'
|
return row.cellValue || '/'
|
||||||
}
|
}
|
||||||
@@ -215,7 +217,7 @@ const tableStore: any = new TableStore({
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: '130',
|
width: '140',
|
||||||
render: 'buttons',
|
render: 'buttons',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
@@ -228,11 +230,7 @@ const tableStore: any = new TableStore({
|
|||||||
},
|
},
|
||||||
render: 'basicButton',
|
render: 'basicButton',
|
||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
upgradeRef.value.open({
|
upgradeRef.value.open(row)
|
||||||
id: [row.id],
|
|
||||||
devModel: row.devModel,
|
|
||||||
version: row.version
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -244,6 +242,38 @@ const tableStore: any = new TableStore({
|
|||||||
click: (row: any) => {
|
click: (row: any) => {
|
||||||
deviceLogRef.value.open(row)
|
deviceLogRef.value.open(row)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'productSetting',
|
||||||
|
title: '重启',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-EditPen',
|
||||||
|
loading: 'loading1',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: (row: any) => {
|
||||||
|
row.loading1 = true
|
||||||
|
if (row.accessMethod == 'CLD') {
|
||||||
|
reboot({
|
||||||
|
devId: row.id
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
ElMessage.success(res.message)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
row.loading1 = false
|
||||||
|
})
|
||||||
|
} else if (row.accessMethod == 'MQTT') {
|
||||||
|
rebootDevice({
|
||||||
|
nDid: row.ndid
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
ElMessage.success(res.message)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
row.loading1 = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -366,7 +366,7 @@ const currentChangeEvent = () => {
|
|||||||
nodeId.value = tableRef.value.getRef().getCurrentRecord().id
|
nodeId.value = tableRef.value.getRef().getCurrentRecord().id
|
||||||
// 检查返回的数据是否存在且不为空
|
// 检查返回的数据是否存在且不为空
|
||||||
if (res.data && res.data.processDeviceList) {
|
if (res.data && res.data.processDeviceList) {
|
||||||
dataSource.value = res.data.processDeviceList.filter(item => (item.name = item.processNo + ''))
|
dataSource.value = res.data.processDeviceList.filter(item => (item.name = item.processNo + '号进程'))
|
||||||
} else {
|
} else {
|
||||||
dataSource.value = []
|
dataSource.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,13 +315,13 @@ onUnmounted(() => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.vgl-layout {
|
.vgl-layout {
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
border-radius: 4px;
|
border-radius: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.vgl-item:not(.vgl-item--placeholder)) {
|
:deep(.vgl-item:not(.vgl-item--placeholder)) {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 4px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,13 +224,13 @@ onUnmounted(() => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.vgl-layout {
|
.vgl-layout {
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
border-radius: 4px;
|
border-radius: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.vgl-item:not(.vgl-item--placeholder)) {
|
:deep(.vgl-item:not(.vgl-item--placeholder)) {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 4px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user