Files
pqs-9100_client/frontend/src/views/home/components/realTimeDataAlign.vue
2025-09-25 10:11:15 +08:00

292 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>