2025-08-01 15:40:26 +08:00

500 lines
14 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.

<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { Page } from '@vben/common-ui';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { postDeviceInfoPage } from '#/api-client';
import { postTreeModelDeviceDataInfoPage } from '#/api-client';
import { $t } from '#/locales';
import { generateDynamicColumns } from './dynamicColumns';
import { querySchema } from './schema';
defineOptions({
name: 'DeviceData',
});
// 存储设备信息选项的完整数据
const deviceOptions = ref<any[]>([]);
// 当前选中的设备信息
const selectedDeviceInfo = ref<any>(null);
// 获取设备信息的完整数据用于根据设备ID获取设备信息
const fetchDeviceOptions = async () => {
try {
const { data } = await postDeviceInfoPage({
body: {
pageIndex: 1,
pageSize: 1000,
},
});
if (data?.items) {
deviceOptions.value = data.items;
}
} catch (error) {
console.error('获取设备信息失败:', error);
}
};
// 根据设备地址获取设备信息对象
const getDeviceInfoByAddress = (deviceAddress: string) => {
if (!deviceAddress || !deviceOptions.value || deviceOptions.value.length === 0) {
return null;
}
return deviceOptions.value.find((device) => device.deviceAddress === deviceAddress);
};
// 格式化日期为 yyyy-MM-dd HH:mm:ss 格式
const formatDate = (date: Date | string) => {
if (!date) return undefined;
const d = new Date(date);
if (isNaN(d.getTime())) return undefined;
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const route = useRoute();
const { DeviceType, DeviceAddress } = route.query;
// 动态列定义
const dynamicColumns = ref<any[]>([]);
// 固定列定义(始终显示)- 基于 IoTDBTreeModelDeviceDataDto 类型
const fixedColumns = [
{ title: '序号', type: 'seq', width: 50, field: 'seq', slots: {} },
{
field: 'Timestamps',
title: $t('abp.IoTDBBase.Timestamps'),
minWidth: 150,
showOverflow: true,
slots: {},
},
{
field: 'FormattedTimestamps',
title: $t('abp.IoTDBBase.FormattedTimestamps'),
minWidth: 150,
showOverflow: true,
slots: {},
},
{
field: 'DeviceType',
title: $t('abp.IoTDBBase.DeviceType'),
minWidth: 150,
showOverflow: true,
slots: {},
},
{
field: 'IoTDataType',
title: $t('abp.IoTDBBase.IoTDataType'),
minWidth: 150,
showOverflow: true,
slots: {},
},
{
field: 'DeviceAddress',
title: $t('abp.IoTDBBase.DeviceAddress'),
minWidth: 150,
showOverflow: true,
slots: {},
},
];
// 合并固定列和动态列 - 使用计算属性确保响应式
const allColumns = computed(() => {
// 如果表格未初始化,只返回固定列
if (!isGridInitialized.value) {
return [...fixedColumns];
}
const columns = [...fixedColumns];
// 安全地添加动态列
if (dynamicColumns.value && Array.isArray(dynamicColumns.value)) {
const validDynamicColumns = dynamicColumns.value
.filter((col) => col && typeof col === 'object' && col.field && col.title)
.map((col) => ({
...col,
slots: col.slots || {}, // 确保每个列都有slots属性
}));
columns.push(...validDynamicColumns);
}
return columns;
});
// 初始化默认列(防止表格空白)
const initDefaultColumns = () => {
// 确保dynamicColumns是一个有效的数组
if (!Array.isArray(dynamicColumns.value)) {
dynamicColumns.value = [];
}
};
// 表格是否已初始化
const isGridInitialized = ref(false);
// 是否正在初始化,用于防止重复查询
const isInitializing = ref(false);
// 初始化默认列
initDefaultColumns();
const formOptions: VbenFormProps = {
schema: querySchema.value,
initialValues: {
DeviceType: DeviceType ? Number(DeviceType) : undefined,
DeviceAddress: DeviceAddress as string, // 使用DeviceAddress作为初始值
},
// 禁用表单值变化时自动提交,使用自定义处理函数
submitOnChange: false,
// 添加表单值变化的处理函数
handleValuesChange: async (values, changedFields) => {
// 如果正在初始化,不触发查询
if (isInitializing.value) {
return;
}
// 当任何相关字段发生变化时,刷新表格数据
const relevantFields = new Set([
'DeviceAddress',
'DeviceType',
'EndCreationTime',
'IoTDataType',
'StartCreationTime',
]);
const hasRelevantChange = changedFields.some((field) =>
relevantFields.has(field),
);
// 新增DeviceAddress变化时同步selectedDeviceInfo
if (changedFields.includes('DeviceAddress')) {
const deviceAddress = values.DeviceAddress;
if (deviceAddress) {
// 先尝试从 deviceOptions 中查找(备用方案)
let device =
deviceOptions.value.length > 0
? deviceOptions.value.find((d) => d.deviceAddress === deviceAddress)
: null;
// 如果没找到,尝试从 DeviceSelect 组件中获取
if (!device && gridApi?.formApi) {
try {
// 获取 DeviceSelect 组件的实例
const deviceSelectRef =
gridApi.formApi.getFieldComponentRef('DeviceAddress');
if (deviceSelectRef && deviceSelectRef.getSelectedDevice) {
device = deviceSelectRef.getSelectedDevice();
}
} catch {
// 静默处理错误
}
}
if (device) {
selectedDeviceInfo.value = device;
} else {
// 如果还是没找到,尝试延迟获取(组件可能还没完全更新)
setTimeout(() => {
try {
const deviceSelectRef =
gridApi.formApi.getFieldComponentRef('DeviceAddress');
if (deviceSelectRef && deviceSelectRef.getSelectedDevice) {
const delayedDevice = deviceSelectRef.getSelectedDevice();
if (delayedDevice) {
selectedDeviceInfo.value = delayedDevice;
}
}
} catch {
// 静默处理错误
}
}, 100);
}
} else {
selectedDeviceInfo.value = null;
}
}
if (hasRelevantChange) {
// 使用 setTimeout 确保表单值已经完全更新
setTimeout(async () => {
const latestValues = await gridApi.formApi.getValues();
gridApi.reload(latestValues);
}, 0);
}
},
};
const gridOptions: VxeGridProps<any> = {
checkboxConfig: {
highlight: true,
labelField: 'name',
},
columns: fixedColumns, // 初始化时只使用固定列
height: 'auto',
keepSource: true,
// 确保分页功能正常工作
pager: true,
pagerConfig: {
currentPage: 1,
pageSize: 20,
},
toolbarConfig: {
custom: true,
},
customConfig: {
storage: true,
},
// 添加调试配置
showOverflow: true,
showHeaderOverflow: true,
proxyConfig: {
ajax: {
query: async ({ page, sorts, filters }, formValues) => {
// 确保page参数存在
if (!page) {
return { items: [], totalCount: 0 };
}
// 获取当前表单值如果formValues为空则从表单API获取
const currentFormValues =
formValues ||
(gridApi?.formApi ? await gridApi.formApi.getValues() : {}) ||
{};
// 如果表单中没有DeviceAddress但有路由参数使用路由参数
if (!currentFormValues.DeviceAddress && DeviceAddress) {
currentFormValues.DeviceAddress = DeviceAddress as string;
}
// 处理DeviceType和IoTDataType确保传递数字类型
const deviceTypeValue = currentFormValues.DeviceType;
const deviceTypeNumber = deviceTypeValue
? Number(deviceTypeValue)
: undefined;
const ioTDataTypeValue = currentFormValues.IoTDataType;
// 处理日期范围参数
const startCreationTime = currentFormValues.StartCreationTime
? formatDate(currentFormValues.StartCreationTime)
: undefined;
const endCreationTime = currentFormValues.EndCreationTime
? formatDate(currentFormValues.EndCreationTime)
: undefined;
// 获取选中的设备信息
let deviceAddress = currentFormValues.DeviceAddress || '';
// 如果表单中没有DeviceAddress但有路由参数使用路由参数
if (!deviceAddress && DeviceAddress) {
deviceAddress = DeviceAddress as string;
}
// 优先使用选中的设备信息
const deviceInfo =
selectedDeviceInfo.value ||
(deviceAddress && deviceOptions.value.length > 0
? getDeviceInfoByAddress(deviceAddress)
: null);
if (deviceInfo) {
deviceAddress = deviceInfo.deviceAddress || deviceAddress;
}
try {
const { data } = await postTreeModelDeviceDataInfoPage({
body: {
pageIndex: page.currentPage,
pageSize: page.pageSize,
// 使用设备地址作为DeviceAddress
DeviceAddress: deviceAddress,
DeviceType: deviceTypeNumber || undefined,
IoTDataType: ioTDataTypeValue || undefined,
// 添加日期范围参数
StartCreationTime: startCreationTime || undefined,
EndCreationTime: endCreationTime || undefined,
},
});
// 简化处理逻辑,直接使用接口返回的数据
if (data?.items && data.items.length > 0) {
try {
// 动态生成列定义
const generatedColumns = generateDynamicColumns(data.items);
// 更新动态列
dynamicColumns.value = generatedColumns;
// 使用setState更新整个gridOptions确保列定义能够正确更新
await nextTick();
if (gridApi && gridApi.setState) {
gridApi.setState({
gridOptions: {
...gridOptions,
columns: allColumns.value,
// 保持分页配置
pager: true,
pagerConfig: gridOptions.pagerConfig,
},
});
}
} catch (error) {
console.error('更新列定义时出错:', error);
// 如果列更新失败,使用空数组
dynamicColumns.value = [];
}
// 直接使用接口返回的totalCount
const result = {
items: data.items || [],
totalCount: data.totalCount || 0,
};
return result;
}
return {
items: [],
totalCount: 0,
};
} catch (error) {
console.error('API调用出错:', error);
throw error;
}
},
},
},
};
const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
// 监听分页器状态变化
watch(
() => gridApi?.pagerApi?.pageSize,
(newSize, oldSize) => {
if (newSize !== oldSize && oldSize && !isInitializing.value) {
// 重置到第一页
gridApi.pagerApi.currentPage = 1;
// 触发数据重新加载
gridApi.reload();
}
},
);
// 监听当前页变化
watch(
() => gridApi?.pagerApi?.currentPage,
(newPage, oldPage) => {
if (newPage !== oldPage && oldPage && !isInitializing.value) {
// 触发数据重新加载
gridApi.reload();
}
},
);
// 初始化函数
const initializeGrid = async () => {
try {
// 设置初始化标志
isInitializing.value = true;
// 获取设备信息数据
await fetchDeviceOptions();
// 标记表格已初始化
isGridInitialized.value = true;
// 更新表格列定义
if (gridApi && gridApi.setState) {
await nextTick();
gridApi.setState({
gridOptions: {
...gridOptions,
columns: allColumns.value,
// 保持分页配置
pager: true,
pagerConfig: gridOptions.pagerConfig,
},
});
}
// 如果有路由参数,自动触发查询
if (DeviceType || DeviceAddress) {
// 延迟一下确保表格已完全初始化
setTimeout(async () => {
if (gridApi && gridApi.formApi) {
// 确保表单值正确设置
const currentValues = await gridApi.formApi.getValues();
if (!currentValues.DeviceAddress && DeviceAddress) {
await gridApi.formApi.setValues({
...currentValues,
DeviceAddress: DeviceAddress as string,
});
}
// 延迟清除初始化标志,确保只触发一次查询
setTimeout(() => {
isInitializing.value = false;
// 确保表单值正确设置后再触发查询
gridApi.reload();
}, 200);
} else {
isInitializing.value = false;
}
}, 300);
} else {
// 如果没有路由参数,直接清除初始化标志
isInitializing.value = false;
}
} catch (error) {
console.error('初始化表格失败:', error);
isInitializing.value = false;
}
};
// 监听路由参数变化,当有路由参数时自动触发查询
watch(
() => [DeviceType, DeviceAddress],
async (newValues, oldValues) => {
// 如果正在初始化,不触发额外的查询
if (isInitializing.value) {
return;
}
// 如果有路由参数,等待设备信息加载完成后自动触发查询
if (newValues.some(Boolean) && gridApi && isGridInitialized.value) {
// 延迟一下确保表单值已经设置
setTimeout(() => {
gridApi.reload();
}, 100);
}
},
{ immediate: false }, // 改为false避免立即触发
);
// 页面初始化时,延迟初始化表格
onMounted(async () => {
// 延迟初始化确保VXE表格组件已完全挂载
setTimeout(async () => {
await initializeGrid();
}, 100);
});
</script>
<template>
<Page auto-content-height>
<Grid />
</Page>
</template>