|
|
<template>
|
|
|
<div class="table-container">
|
|
|
<el-card class="card" shadow="hover" :body-style="{ padding: '10px' }">
|
|
|
<h2 class="cardHead">运行信息</h2>
|
|
|
<div class="table-wrapper" v-loading="arr.length === 0">
|
|
|
<el-table
|
|
|
:data="arr"
|
|
|
:span-method="objectSpanMethods"
|
|
|
style="width: 100%"
|
|
|
class="full-height-table"
|
|
|
@cell-click="handleCellClick"
|
|
|
>
|
|
|
<el-table-column prop="floorName" label="楼层" />
|
|
|
<el-table-column prop="id" label="设备ID" />
|
|
|
<el-table-column prop="name" label="设备名称" />
|
|
|
|
|
|
<el-table-column label="绑定">
|
|
|
<template #default="{ row }">
|
|
|
<el-tag :type="row.isBinding ? 'primary' : 'info'">
|
|
|
{{ row.isBinding ? '绑定' : '待绑' }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="设备状态">
|
|
|
<template #default="{ row }">
|
|
|
<el-tag :type="row.eState === 0 ? 'success' : 'warning'">
|
|
|
{{ row.eState === 0 ? '正常' : '故障' }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="当前状态">
|
|
|
<template #default="{ row }">
|
|
|
<el-tag v-if="row.currentState === 0" type="success">运行</el-tag>
|
|
|
<el-tag v-else-if="row.currentState === 1" type="info">停止</el-tag>
|
|
|
<el-tag v-else type="error">报警</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="操作">
|
|
|
<template #default="{ row }">
|
|
|
<el-button type="primary" class="operation-btn" v-if="row.operation">
|
|
|
屏蔽
|
|
|
</el-button>
|
|
|
<el-button type="primary" class="operation-btn" v-else> 取消屏蔽 </el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
import type { TableColumnCtx } from 'element-plus';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
import { computed, onMounted, onUnmounted, ref, watch, reactive } from 'vue';
|
|
|
import { modelApi } from '@/utils/request';
|
|
|
import emitter from '@/utils/emitter';
|
|
|
import { type DataItem } from '@/components/mt-edit/store/types';
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
// 导入全局类型定义
|
|
|
/// <reference path="../../../global.d.ts" />
|
|
|
interface RecRuntimeData {
|
|
|
double: number;
|
|
|
node: {
|
|
|
name: string;
|
|
|
};
|
|
|
}
|
|
|
interface RecServiceType {
|
|
|
service: {
|
|
|
node: {
|
|
|
runtimes: Record<string, RecRuntimeData>;
|
|
|
};
|
|
|
};
|
|
|
}
|
|
|
// interface ExtendedParentWindow extends Window {
|
|
|
// Rec?: RecServiceType;
|
|
|
// }
|
|
|
|
|
|
interface ExtendedParentWindow extends Window {
|
|
|
globalData: Map<string, DataItem> | Record<string, DataItem>;
|
|
|
}
|
|
|
|
|
|
interface User {
|
|
|
id: string;
|
|
|
name: string;
|
|
|
amount1: string;
|
|
|
amount2: string;
|
|
|
amount3: number;
|
|
|
}
|
|
|
interface Equipment {
|
|
|
id: string; //设备ID
|
|
|
address?: string; //设备地址
|
|
|
name: string; //设备名称
|
|
|
eState: number; //设备状态 0正常 1故障
|
|
|
currentState: number; //当前状态 0运行 1停止 2报警
|
|
|
operation: boolean; //操作 true屏蔽 false取消屏蔽
|
|
|
}
|
|
|
interface FloorObj {
|
|
|
floorName: string; //地址
|
|
|
equipments: Equipment[];
|
|
|
}
|
|
|
// node楼层节点
|
|
|
interface FloorNode {
|
|
|
roomId: string;
|
|
|
floorName: string;
|
|
|
eqpList: EquipmentNode[];
|
|
|
}
|
|
|
//node
|
|
|
interface EquipmentNode {
|
|
|
id: string; //设备ID
|
|
|
name: string; //设备名称
|
|
|
isBinding: boolean; //是否绑定
|
|
|
eState: number; //设备状态 0正常 1故障
|
|
|
currentState: number; //当前状态 0运行 1停止 2报警
|
|
|
operation: boolean; //操作 true屏蔽 false取消屏蔽
|
|
|
}
|
|
|
|
|
|
function handleCellClick(obj: any) {
|
|
|
console.log('点击:', obj);
|
|
|
emitter.emit('binding-obj-facility-move', obj.id);
|
|
|
}
|
|
|
|
|
|
emitter.on('binding-data-update', () => {
|
|
|
console.log('@@binding-data-update');
|
|
|
dataInit();
|
|
|
});
|
|
|
|
|
|
const props = defineProps({
|
|
|
fontFamily: {
|
|
|
type: String,
|
|
|
default: '黑体'
|
|
|
},
|
|
|
fontSize: {
|
|
|
type: Number,
|
|
|
default: 12
|
|
|
},
|
|
|
testColor: {
|
|
|
type: String,
|
|
|
default: '#ffffff'
|
|
|
},
|
|
|
definitionItemJson: {
|
|
|
type: Object,
|
|
|
default: () => ({})
|
|
|
}
|
|
|
});
|
|
|
|
|
|
console.log('父传子itemjson:', props.definitionItemJson);
|
|
|
|
|
|
// 计算属性——既读取又修改
|
|
|
let computedSize = computed({
|
|
|
// 读取
|
|
|
get() {
|
|
|
return props.fontSize + 'px';
|
|
|
}, // 修改
|
|
|
set(val) {}
|
|
|
});
|
|
|
|
|
|
const arr = ref<any[]>([]);
|
|
|
let targetDataTable: FloorNode[] = [];
|
|
|
|
|
|
let initArrs: any = {};
|
|
|
//数据初始化
|
|
|
async function dataInit() {
|
|
|
// 从 window 获取 globalData(可能是 Map 或对象)
|
|
|
const globalDataRaw = (window as unknown as ExtendedParentWindow).globalData;
|
|
|
|
|
|
// 清空数组
|
|
|
arr.value = [];
|
|
|
targetDataTable = [];
|
|
|
|
|
|
loading.value = true;
|
|
|
const response = await modelApi.oneRoomFor_Equipment_get();
|
|
|
if (response.code == 200 && response.data && response.data.length > 0) {
|
|
|
// console.log('公共:', window.parent.Rec.service.node.runtimes);
|
|
|
response.data.forEach((floor: any) => {
|
|
|
let targetData: FloorNode = {
|
|
|
roomId: floor.roomId,
|
|
|
floorName: floor.roomName,
|
|
|
eqpList: []
|
|
|
};
|
|
|
let eqpList: EquipmentNode[] = [];
|
|
|
floor.eqpList.forEach((eqp: any) => {
|
|
|
let currentState = 1;
|
|
|
// 根据 globalData 类型选择访问方式
|
|
|
const getNodeData = (key: string) => {
|
|
|
if (globalDataRaw instanceof Map) {
|
|
|
return globalDataRaw.get(key);
|
|
|
} else {
|
|
|
return (globalDataRaw as Record<string, any>)[key];
|
|
|
}
|
|
|
};
|
|
|
|
|
|
//故障
|
|
|
if (getNodeData(eqp.nodes[1])?.double != 1) currentState = 1;
|
|
|
else if (getNodeData(eqp.nodes[2])?.double == 1) currentState = 2;
|
|
|
else if (getNodeData(eqp.nodes[1])?.double == 1) currentState = 0;
|
|
|
|
|
|
let eqpNode: EquipmentNode = {
|
|
|
id: eqp.id,
|
|
|
name: getNodeData(eqp.nodes[0])?.node?.name || '',
|
|
|
eState: getNodeData(eqp.nodes[0])?.double || 0,
|
|
|
currentState: currentState,
|
|
|
isBinding: eqp.isBinding,
|
|
|
operation: getNodeData(eqp.nodes[3])?.double == 1
|
|
|
};
|
|
|
eqpList.push(eqpNode);
|
|
|
});
|
|
|
targetData.eqpList = eqpList;
|
|
|
targetDataTable.push(targetData);
|
|
|
});
|
|
|
ElMessage.success('获取接口数据成功');
|
|
|
arr.value = targetDataTable.flatMap((floor) =>
|
|
|
floor.eqpList.map((equipment) => ({
|
|
|
...equipment,
|
|
|
floorName: floor.floorName,
|
|
|
roomId: floor.roomId
|
|
|
}))
|
|
|
);
|
|
|
} else {
|
|
|
ElMessage.error('获取接口数据失败');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 将嵌套数据展开为表格数据
|
|
|
const tableDatas = computed(() => {
|
|
|
return targetDataTable.flatMap((floor) =>
|
|
|
floor.eqpList.map((equipment) => ({
|
|
|
...equipment,
|
|
|
floorName: floor.floorName, // 添加楼层信息到每行
|
|
|
roomId: floor.roomId // 添加设备地址到每行
|
|
|
}))
|
|
|
);
|
|
|
});
|
|
|
|
|
|
// 在 onMounted 中
|
|
|
onMounted(async () => {
|
|
|
await dataInit();
|
|
|
});
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
emitter.off('binding-data-update');
|
|
|
});
|
|
|
|
|
|
// 数据源头
|
|
|
const floorData: FloorObj[] = [
|
|
|
{
|
|
|
floorName: '安全工器具室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector01',
|
|
|
name: '1号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector02',
|
|
|
name: '2号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '蓄电池室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector03',
|
|
|
name: '3号感烟探头',
|
|
|
eState: 1,
|
|
|
currentState: 1,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector04',
|
|
|
name: '4号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '220kV保护室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector05',
|
|
|
name: '5号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 2,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector06',
|
|
|
name: '6号感烟探头',
|
|
|
eState: 1,
|
|
|
currentState: 1,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '380kV配电室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector07',
|
|
|
name: '7号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector08',
|
|
|
name: '8号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 2,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '35kV高压室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector09',
|
|
|
name: '9号感烟探头',
|
|
|
eState: 1,
|
|
|
currentState: 1,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector10',
|
|
|
name: '10号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '2号接地变室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector11',
|
|
|
name: '11号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 2,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector12',
|
|
|
name: '12号感烟探头',
|
|
|
eState: 1,
|
|
|
currentState: 1,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '110kV电缆室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector13',
|
|
|
name: '13号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector14',
|
|
|
name: '14号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '2号站用变室',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector15',
|
|
|
name: '15号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector16',
|
|
|
name: '16号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
floorName: '生产综合楼1楼走廊',
|
|
|
equipments: [
|
|
|
{
|
|
|
id: 'smokeDetector17',
|
|
|
name: '17号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: 'smokeDetector18',
|
|
|
name: '18号感烟探头',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
},
|
|
|
{
|
|
|
id: '10028',
|
|
|
name: '1号声光报警器',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: '10029',
|
|
|
name: '2号声光报警器',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
},
|
|
|
{
|
|
|
id: '10030',
|
|
|
name: '1号手报',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: false
|
|
|
},
|
|
|
{
|
|
|
id: '10031',
|
|
|
name: '2号手报',
|
|
|
eState: 0,
|
|
|
currentState: 0,
|
|
|
operation: true
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
];
|
|
|
|
|
|
interface SpanMethodProps {
|
|
|
row: any;
|
|
|
column: TableColumnCtx<User>;
|
|
|
rowIndex: number;
|
|
|
columnIndex: number;
|
|
|
}
|
|
|
|
|
|
const objectSpanMethods = ({ row, columnIndex }: SpanMethodProps) => {
|
|
|
if (columnIndex === 0) {
|
|
|
// 查找当前行对应的楼层数据
|
|
|
const floor = targetDataTable.find((f) => f.floorName === row.floorName);
|
|
|
|
|
|
if (floor) {
|
|
|
// 找到该楼层下第一个设备的索引
|
|
|
const firstEquipmentIndex = floor.eqpList.findIndex((e) => e.id === row.id);
|
|
|
|
|
|
if (firstEquipmentIndex === 0) {
|
|
|
// 如果是第一个设备,合并该楼层的所有行
|
|
|
return {
|
|
|
rowspan: floor.eqpList.length,
|
|
|
colspan: 1
|
|
|
};
|
|
|
} else {
|
|
|
// 如果不是第一个设备,不显示(被合并)
|
|
|
return {
|
|
|
rowspan: 0,
|
|
|
colspan: 0
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 其他列不合并
|
|
|
return {
|
|
|
rowspan: 1,
|
|
|
colspan: 1
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// 将嵌套数据展开为表格数据
|
|
|
const tableData = computed(() => {
|
|
|
return floorData.flatMap((floor) =>
|
|
|
floor.equipments.map((equipment) => ({
|
|
|
...equipment,
|
|
|
floorName: floor.floorName, // 添加楼层信息到每行
|
|
|
// 如果有设备地址就显示,没有就显示楼层作为地址
|
|
|
address: equipment.address || floor.floorName
|
|
|
}))
|
|
|
);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.table-container {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.card {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
.card :deep(.el-card__body) {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
padding: 10px;
|
|
|
}
|
|
|
|
|
|
.table-wrapper {
|
|
|
flex: 1;
|
|
|
overflow: hidden;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.full-height-table {
|
|
|
flex: 1;
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
.full-height-table :deep(.el-table__body-wrapper) {
|
|
|
flex: 1;
|
|
|
overflow-y: auto;
|
|
|
}
|
|
|
|
|
|
.cardHead {
|
|
|
margin: 0 0 15px 0;
|
|
|
padding: 0;
|
|
|
font-size: 16px;
|
|
|
font-weight: bold;
|
|
|
text-align: center;
|
|
|
color: #ffffff;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.operation-btn {
|
|
|
width: 60px;
|
|
|
min-width: 50px;
|
|
|
padding: 8px 12px;
|
|
|
height: 30px;
|
|
|
}
|
|
|
|
|
|
/* 控制表头文字大小 */
|
|
|
.full-height-table :deep(.el-table__header th) {
|
|
|
font-size: 13px;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
|
|
|
/* 控制表格内容文字大小 */
|
|
|
.full-height-table :deep(.el-table__body td) {
|
|
|
font-size: v-bind('computedSize');
|
|
|
color: v-bind('props.testColor');
|
|
|
}
|
|
|
|
|
|
/* 控制标签文字大小 */
|
|
|
.full-height-table :deep(.el-tag) {
|
|
|
font-size: v-bind('computedSize'); /* 标签内的文字可以稍小一些 */
|
|
|
}
|
|
|
|
|
|
/* 控制按钮文字大小 */
|
|
|
.full-height-table :deep(.el-button) {
|
|
|
font-size: v-bind('computedSize');
|
|
|
color: v-bind('props.testColor');
|
|
|
}
|
|
|
</style>
|