292 lines
10 KiB
Vue
292 lines
10 KiB
Vue
<template>
|
||
<el-dialog title="实时数据详情" v-model='dialogVisible' @close="handleClose" v-bind="dialogBig">
|
||
<el-tabs v-model="activeTab" type="card">
|
||
<el-tab-pane
|
||
v-for="(device, deviceName, index) in testDataStructure"
|
||
:key="deviceName"
|
||
:name="`channel${index + 1}`">
|
||
<template #label>
|
||
<span>
|
||
{{ deviceName }}
|
||
<el-icon v-if="tabStatus[deviceName]" style="color: red; margin-left: 5px;">
|
||
<CircleClose />
|
||
</el-icon>
|
||
<el-icon v-else style="color: green; margin-left: 5px;">
|
||
<CircleCheck />
|
||
</el-icon>
|
||
</span>
|
||
</template>
|
||
<div class="table-toolbar">
|
||
<el-form-item label="标准设备通道号" prop="createId">
|
||
<el-select
|
||
v-model="selectedChannels[deviceName]"
|
||
placeholder="选择通道"
|
||
style="width: 150px;"
|
||
@change="() => handleDutChannelChange(deviceName)">
|
||
<el-option
|
||
v-for="channel in device.channelDataList"
|
||
:key="channel.stdDevNum"
|
||
:label="`通道${channel.stdDevNum}`"
|
||
:value="`通道${channel.stdDevNum}`">
|
||
</el-option>
|
||
</el-select>
|
||
<span style="margin-left: 20px; font-size: 14px; color: var(--el-color-primary);">
|
||
标准设备:{{ deviceName }}-{{ selectedChannels[deviceName] }} ---> 被检设备:{{ formatDutChannelLabel(getMappedDutChannel(deviceName, selectedChannels[deviceName])) }}
|
||
</span>
|
||
</el-form-item>
|
||
<el-button type="primary" @click="exportData">导出数据</el-button>
|
||
</div>
|
||
<el-table
|
||
:data="tableDataMap[deviceName]"
|
||
:header-cell-style="{ textAlign: 'center',backgroundColor: 'var(--el-color-primary)',color: '#fff' } "
|
||
:cell-style="{ textAlign: 'center' }"
|
||
style="width: 100%"
|
||
:style="{ height: '400px',maxHeight: '400px',overflow:'hidden'}">
|
||
|
||
<el-table-column :label="`${deviceName}-${selectedChannels[deviceName] || '通道1'}`">
|
||
<el-table-column prop="timeStdDev" label="数据时标" width="200"/>
|
||
<el-table-column prop="uaStdDev" label="A相(V)"/>
|
||
<el-table-column prop="ubStdDev" label="B相(V)"/>
|
||
<el-table-column prop="ucStdDev" label="C相(V)"/>
|
||
</el-table-column>
|
||
<el-table-column :label="formatDutChannelLabel(getMappedDutChannel(deviceName, selectedChannels[deviceName]))">
|
||
<el-table-column prop="timeDev" label="数据时标" width="200"/>
|
||
<el-table-column prop="uaDev" label="A相(V)"/>
|
||
<el-table-column prop="ubDev" label="B相(V)"/>
|
||
<el-table-column prop="ucDev" label="C相(V)"/>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang='tsx' name='realTimeDataAlign'>
|
||
import { dialogBig } from "@/utils/elementBind";
|
||
import { PropType, ref, nextTick } from "vue";
|
||
import { ElMessage } from "element-plus";
|
||
import { CircleCheck, CircleClose } from '@element-plus/icons-vue';
|
||
import {exportAlignData} from "@/api/socket/socket";
|
||
import {useDownload} from "@/hooks/useDownload";
|
||
|
||
const dialogVisible = ref(false);
|
||
const activeTab = ref('channel1');
|
||
|
||
// 在 script setup 中定义接口
|
||
interface ChannelData {
|
||
stdDevNum: string;
|
||
devInfo: string;
|
||
dataList: {
|
||
timeDev: string | null;
|
||
uaDev: number | null;
|
||
ubDev: number | null;
|
||
ucDev: number | null;
|
||
timeStdDev: string | null;
|
||
uaStdDev: number | null;
|
||
ubStdDev: number | null;
|
||
ucStdDev: number | null;
|
||
}[];
|
||
}
|
||
|
||
interface DeviceData {
|
||
stdDevName: string;
|
||
channelDataList: ChannelData[];
|
||
}
|
||
|
||
// 修改 testDataStructure 的类型声明
|
||
const testDataStructure = ref<Record<string, DeviceData>>({});
|
||
|
||
// 每个设备选中的通道
|
||
const selectedChannels = ref<Record<string, string>>({});
|
||
|
||
// 通道映射关系:标准设备通道 -> 被检设备通道
|
||
const channelMapping = ref<Record<string, Record<string, string>>>({});
|
||
|
||
// 每个设备的表格数据
|
||
const tableDataMap = ref<Record<string, any[]>>({});
|
||
|
||
// 每个tab的状态(true表示有不完整数据,false表示数据完整)
|
||
const tabStatus = ref<Record<string, boolean>>({});
|
||
|
||
// 检查设备数据是否有不完整的行(包含null的行)
|
||
const hasIncompleteData = (deviceName: string) => {
|
||
const tableData = tableDataMap.value[deviceName];
|
||
if (!tableData || tableData.length === 0) return false;
|
||
|
||
// 检查每一行是否有缺失数据(包含null的字段)
|
||
return tableData.some(row => {
|
||
return row.uaDev === '/' || row.ubDev === '/' || row.ucDev === '/' ||
|
||
row.uaStdDev === '/' || row.ubStdDev === '/' || row.ucStdDev === '/';
|
||
});
|
||
};
|
||
|
||
// 获取映射的被检设备通道
|
||
const getMappedDutChannel = (deviceName: string, stdChannel: string) => {
|
||
|
||
// 添加安全检查
|
||
if (!channelMapping.value[deviceName]) return '';
|
||
return channelMapping.value[deviceName][stdChannel] || '';
|
||
};
|
||
|
||
// 格式化被检设备通道标签,将设备名称和通道号用"-"连接
|
||
const formatDutChannelLabel = (dutChannel: string) => {
|
||
// 如果是"被检设备X通道Y"格式,则转换为"被检设备X-通道Y"
|
||
if (!dutChannel) return '未映射';
|
||
return dutChannel.replace(/(.+)(通道\d+)/, '$1-$2');
|
||
};
|
||
|
||
// 处理标准设备通道切换
|
||
const handleDutChannelChange = (deviceName: string) => {
|
||
// 更新指定设备的表格数据,但不改变tab图标状态
|
||
updateTableData(deviceName);
|
||
};
|
||
|
||
// 根据 testDataStructure 生成表格数据
|
||
const generateTableData = (deviceName: string, stdChannel: string) => {
|
||
const deviceData = testDataStructure.value[deviceName];
|
||
if (!deviceData) return [];
|
||
|
||
// 根据实际通道编号查找对应的数据
|
||
const channelData = deviceData.channelDataList.find(channel =>
|
||
`通道${channel.stdDevNum}` === stdChannel
|
||
);
|
||
|
||
if (!channelData) return [];
|
||
|
||
// 生成表格数据
|
||
return channelData.dataList.map(dataItem => {
|
||
return {
|
||
timeDev: dataItem.timeDev !== null ? dataItem.timeDev : '/',
|
||
uaDev: dataItem.uaDev !== null ? dataItem.uaDev : '/',
|
||
ubDev: dataItem.ubDev !== null ? dataItem.ubDev : '/',
|
||
ucDev: dataItem.ucDev !== null ? dataItem.ucDev : '/',
|
||
timeStdDev: dataItem.timeStdDev !== null ? dataItem.timeStdDev : '/',
|
||
uaStdDev: dataItem.uaStdDev !== null ? dataItem.uaStdDev : '/',
|
||
ubStdDev: dataItem.ubStdDev !== null ? dataItem.ubStdDev : '/',
|
||
ucStdDev: dataItem.ucStdDev !== null ? dataItem.ucStdDev : '/'
|
||
};
|
||
});
|
||
};
|
||
|
||
// 更新指定设备的表格数据
|
||
const updateTableData = (deviceName: string) => {
|
||
const selectedChannel = selectedChannels.value[deviceName];
|
||
if (selectedChannel) {
|
||
const tableData = generateTableData(deviceName, selectedChannel);
|
||
tableDataMap.value[deviceName] = tableData;
|
||
|
||
}
|
||
};
|
||
|
||
// 初始化所有设备的数据和状态
|
||
const initAllTableData = () => {
|
||
Object.keys(testDataStructure.value).forEach(deviceName => {
|
||
// 确保设备有数据
|
||
if (!testDataStructure.value[deviceName] ||
|
||
!testDataStructure.value[deviceName].channelDataList ||
|
||
testDataStructure.value[deviceName].channelDataList.length === 0) {
|
||
return;
|
||
}
|
||
|
||
// 默认选择第一个可用通道
|
||
const firstChannel = testDataStructure.value[deviceName].channelDataList[0];
|
||
selectedChannels.value[deviceName] = `通道${firstChannel.stdDevNum}`;
|
||
|
||
// 生成表格数据
|
||
updateTableData(deviceName);
|
||
|
||
// 初始化tab状态(只在初始化时设置一次)
|
||
if (tabStatus.value[deviceName] === undefined) {
|
||
tabStatus.value[deviceName] = hasIncompleteData(deviceName);
|
||
}
|
||
});
|
||
};
|
||
|
||
const open = async (mapping : Record<string, Record<string, string>>,data : any) => {
|
||
let parsedData = data;
|
||
|
||
// 如果 data 是字符串,先解析为对象
|
||
if (typeof data === 'string') {
|
||
try {
|
||
parsedData = JSON.parse(data);
|
||
} catch (error) {
|
||
console.error('数据解析失败:', error);
|
||
ElMessage.error('数据格式错误');
|
||
return;
|
||
}
|
||
}
|
||
// 转换数据格式以匹配组件期望的格式
|
||
const convertedData: Record<string, DeviceData> = {};
|
||
|
||
// 假设传入的数据是一个数组,需要转换为以设备名为键的对象
|
||
if (Array.isArray(parsedData)) {
|
||
|
||
parsedData.forEach((deviceItem: any) => {
|
||
const deviceName = deviceItem.stdDevName;
|
||
convertedData[deviceName] = {
|
||
stdDevName: deviceName,
|
||
channelDataList: deviceItem.channelDataList?.map((channel: any) => ({
|
||
stdDevNum: channel.stdDevNum,
|
||
devInfo: channel.devInfo,
|
||
dataList: channel.dataList?.map((dataItem: any) => ({
|
||
timeDev: dataItem.timeDev,
|
||
uaDev: dataItem.uaDev,
|
||
ubDev: dataItem.ubDev,
|
||
ucDev: dataItem.ucDev,
|
||
timeStdDev: dataItem.timeStdDev,
|
||
uaStdDev: dataItem.uaStdDev,
|
||
ubStdDev: dataItem.ubStdDev,
|
||
ucStdDev: dataItem.ucStdDev
|
||
})) || []
|
||
})) || []
|
||
};
|
||
});
|
||
} else if (parsedData && typeof parsedData === 'object') {
|
||
|
||
// 如果已经是期望的格式,直接使用
|
||
Object.assign(convertedData, parsedData);
|
||
}
|
||
|
||
testDataStructure.value = convertedData;
|
||
|
||
channelMapping.value = mapping;
|
||
dialogVisible.value = true;
|
||
// 使用 nextTick 确保 DOM 更新后再初始化数据
|
||
await nextTick();
|
||
// 初始化数据和状态
|
||
initAllTableData();
|
||
|
||
// 设置默认激活的 tab
|
||
activeTab.value = 'channel1';
|
||
};
|
||
|
||
// 导出数据
|
||
const exportData =async () => {
|
||
useDownload(exportAlignData, '原始数据', null, false, '.xlsx')
|
||
ElMessage.success('数据导出成功');
|
||
// 这里可以添加实际的数据导出逻辑
|
||
};
|
||
|
||
// 关闭弹窗
|
||
const handleClose = () => {
|
||
dialogVisible.value = false;
|
||
// 清空数据
|
||
testDataStructure.value = {};
|
||
tableDataMap.value = {};
|
||
selectedChannels.value = {};
|
||
tabStatus.value = {};
|
||
};
|
||
|
||
defineExpose({ open });
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.table-toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
:deep(.el-dialog__body) {
|
||
padding: 10px 20px 20px 20px;
|
||
}
|
||
</style> |