refactor(devices): 简化导入导出模板的CSV字段处理逻辑

移除字段类型提示和必填标记,统一使用displayName作为列名
重构导出逻辑使其与导入模板保持一致,使用动态生成的字段配置
This commit is contained in:
zhang1106
2026-02-09 14:16:01 +08:00
parent fe6bc0adcc
commit 2d7d53283f
+54 -92
View File
@@ -150,15 +150,6 @@ router.get('/import-template', async (req, res) => {
order: [['order', 'ASC']]
});
// 字段类型提示映射
const fieldTypeHints = {
'设备类型': 'server/switch/router/storage/other',
'状态': 'running/maintenance/offline/fault'
};
// 日期字段提示
const dateFields = ['购买日期', '保修到期'];
// 根据字段配置动态生成CSV标题(排除设备ID,由系统自动生成)
// 将rackId字段替换为机房名称+机柜名称,以便唯一定位
const headers = [];
@@ -169,32 +160,17 @@ router.get('/import-template', async (req, res) => {
if (field.fieldName === 'rackId') {
headers.push({
id: '所在机房名称',
title: '所在机房名称(必填)'
title: '所在机房名称'
});
headers.push({
id: '所在机柜名称',
title: '所在机柜名称(必填)'
title: '所在机柜名称'
});
return;
}
let title = field.displayName;
// 添加字段类型提示
if (fieldTypeHints[field.displayName]) {
title = `${field.displayName}(${fieldTypeHints[field.displayName]})`;
} else if (dateFields.includes(field.displayName)) {
title = `${field.displayName}(YYYY-MM-DD)`;
} else if (field.fieldType === 'select' && field.options && field.options.length > 0) {
// 下拉选择字段,显示可选值
const optionValues = field.options.map(opt => opt.value).join('/');
title = `${field.displayName}(${optionValues})`;
} else {
// 显示必填/可选标识
title = `${field.displayName}(${field.required ? '必填' : '可选'})`;
}
headers.push({ id: field.displayName, title });
// 直接使用displayName作为列名,不添加任何后缀
headers.push({ id: field.displayName, title: field.displayName });
});
// 准备示例数据(根据字段配置生成,排除设备ID)
@@ -334,45 +310,44 @@ router.get('/export', async (req, res) => {
return res.status(404).json({ error: '未找到指定的设备' });
}
// 准备CSV数据
// 创建字段名到displayName的映射
const fieldNameToDisplayName = {};
deviceFields.forEach(field => {
fieldNameToDisplayName[field.fieldName] = field.displayName;
});
// 准备CSV数据 - 根据字段配置动态生成,与导入模板保持一致
const csvData = devices.map(device => {
// 创建基础数据对象
const deviceData = {
'设备ID': device.deviceId,
'设备名称': device.name,
'设备类型': device.type,
'型号': device.model,
'序列号': device.serialNumber,
'所在机房ID': device.Rack?.Room?.roomId || '',
'所在机房名称': device.Rack?.Room?.name || '',
'所在机柜ID': device.rackId,
'所在机柜名称': device.Rack?.name || '',
'位置(U)': device.position,
'高度(U)': device.height,
'功率(W)': device.powerConsumption,
'IP地址': device.ipAddress,
'状态': device.status,
'购买日期': device.purchaseDate ? new Date(device.purchaseDate).toLocaleDateString() : '',
'保修到期': device.warrantyExpiry ? new Date(device.warrantyExpiry).toLocaleDateString() : '',
'描述': device.description,
'创建时间': device.createdAt ? new Date(device.createdAt).toLocaleString() : ''
};
const deviceData = {};
// 添加自定义字段(排除基础字段
// 根据字段配置生成数据(排除deviceId
deviceFields
.filter(field => field.visible && field.fieldName !== 'deviceId')
.forEach(field => {
// rackId字段拆分为机房名称和机柜名称
if (field.fieldName === 'rackId') {
deviceData['所在机房名称'] = device.Rack?.Room?.name || '';
deviceData['所在机柜名称'] = device.Rack?.name || '';
return;
}
// 其他字段使用displayName作为列名
const value = device[field.fieldName];
// 日期格式处理
if (field.fieldType === 'date' && value) {
deviceData[field.displayName] = new Date(value).toLocaleDateString();
} else {
deviceData[field.displayName] = value !== undefined && value !== null ? value : '';
}
});
// 添加自定义字段
if (device.customFields) {
// 定义基础字段的显示名称
const baseFieldDisplayNames = [
'设备ID', '设备名称', '设备类型', '型号', '序列号',
'所在机房ID', '所在机房名称', '所在机柜ID', '所在机柜名称', '位置(U)', '高度(U)',
'功率(W)', 'IP地址', '状态', '购买日期', '保修到期', '描述', '创建时间'
];
deviceFields.forEach(field => {
// 只添加未在基础字段中出现的自定义字段
if (field.visible &&
device.customFields[field.fieldName] &&
!baseFieldDisplayNames.includes(field.displayName)) {
deviceData[field.displayName] = device.customFields[field.fieldName];
Object.entries(device.customFields).forEach(([fieldName, value]) => {
const field = deviceFields.find(f => f.fieldName === fieldName);
if (field && field.visible) {
deviceData[field.displayName] = value;
}
});
}
@@ -380,37 +355,24 @@ router.get('/export', async (req, res) => {
return deviceData;
});
// 设置CSV标题(包含自定义字段)
const baseHeaders = [
{ id: '设备ID', title: '设备ID' },
{ id: '设备名称', title: '设备名称' },
{ id: '设备类型', title: '设备类型' },
{ id: '型号', title: '型号' },
{ id: '序列号', title: '序列号' },
{ id: '所在机房ID', title: '所在机房ID' },
{ id: '所在机名称', title: '所在机名称' },
{ id: '所在机柜ID', title: '所在机柜ID' },
{ id: '所在机柜名称', title: '所在机柜名称' },
{ id: '位置(U)', title: '位置(U)' },
{ id: '高度(U)', title: '高度(U)' },
{ id: '功率(W)', title: '功率(W)' },
{ id: 'IP地址', title: 'IP地址' },
{ id: '状态', title: '状态' },
{ id: '购买日期', title: '购买日期' },
{ id: '保修到期', title: '保修到期' },
{ id: '描述', title: '描述' },
{ id: '创建时间', title: '创建时间' }
];
// 添加自定义字段标题(排除与基础字段重复的字段)
const baseFieldTitles = baseHeaders.map(header => header.id);
const customHeaders = deviceFields
.filter(field => field.visible && !baseFieldTitles.includes(field.displayName))
.map(field => ({ id: field.displayName, title: field.displayName }));
// 设置CSV标题 - 与导入模板保持一致
const headers = [];
deviceFields
.filter(field => field.visible && field.fieldName !== 'deviceId')
.forEach(field => {
// rackId拆分为两个列
if (field.fieldName === 'rackId') {
headers.push({ id: '所在机房名称', title: '所在机房名称' });
headers.push({ id: '所在机名称', title: '所在机名称' });
return;
}
headers.push({ id: field.displayName, title: field.displayName });
});
const csvWriter = createObjectCsvWriter({
path: path.join(__dirname, '../temp/devices.csv'),
header: [...baseHeaders, ...customHeaders],
header: headers,
encoding: 'utf8'
});