import ExcelJS from 'exceljs'; import fs from 'fs/promises'; import path from 'path'; /** * Flattens the nested JSON data into separate arrays for projects, licenses, and buildings. * @param {Array} data - The nested data from merged_data.json. * @returns {{projects: Array, licenses: Array, buildings: Array}} */ function flattenData(data) { const projects = []; const licenses = []; const buildings = []; data.forEach(project => { // Create a shallow copy to avoid modifying the original object in memory const projectCopy = { ...project }; const licensesData = projectCopy['预售许可证']; // The nested array will not be included in the flattened project data delete projectCopy['预售许可证']; projects.push(projectCopy); if (licensesData && Array.isArray(licensesData)) { licensesData.forEach(license => { const licenseCopy = { ...license }; const buildingsData = licenseCopy['楼幢']; // The nested array will not be included in the flattened license data delete licenseCopy['楼幢']; // Add a key to link back to the parent project licenseCopy['项目名称_key'] = project['项目名称']; licenses.push(licenseCopy); if (buildingsData && Array.isArray(buildingsData)) { buildingsData.forEach(building => { // Add keys to link back to the parent license and project const buildingCopy = { ...building, '许可证号_key': license['许可证号'], '项目名称_key': project['项目名称'], }; buildings.push(buildingCopy); }); } }); } }); return { projects, licenses, buildings }; } /** * Adds a worksheet to the workbook with the given data, headers, and styling. * @param {ExcelJS.Workbook} workbook - The workbook instance. * @param {string} sheetName - The name for the new worksheet. * @param {Array} data - The array of data for the sheet. */ function addSheet(workbook, sheetName, data) { if (data.length === 0) { console.log(`- 注意: 没有数据可用于工作表 '${sheetName}'`); return; } const worksheet = workbook.addWorksheet(sheetName); // Get all unique keys from all objects to form a complete header const allKeys = data.reduce((keys, item) => { if (item) { Object.keys(item).forEach(key => { if (!keys.includes(key)) { keys.push(key); } }); } return keys; }, []); worksheet.columns = allKeys.map(key => ({ header: key, key: key, width: key.includes('地址') || key.includes('链接') || key.includes('简介') ? 50 : 25 })); worksheet.addRows(data); // Style header row worksheet.getRow(1).eachCell(cell => { cell.font = { bold: true }; cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFD3D3D3' } }; cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true }; }); // Add autofilter to the header row worksheet.autoFilter = { from: 'A1', to: { row: 1, column: allKeys.length } }; } /** * Generates a multi-sheet XLSX file from the nested real estate data. * @param {Array} data - The nested data from merged_data.json. * @param {string} filePath - The path to save the XLSX file. */ async function generateXlsx(data, filePath) { if (!data || data.length === 0) { console.log(`没有数据可生成 XLSX 文件 (${filePath})。`); return; } console.log('正在将数据处理成多个工作表...'); const { projects, licenses, buildings } = flattenData(data); console.log(`- 项目: ${projects.length} 条`); console.log(`- 许可证: ${licenses.length} 条`); console.log(`- 楼幢: ${buildings.length} 条`); const workbook = new ExcelJS.Workbook(); workbook.creator = 'Gemini Assistant'; workbook.created = new Date(); workbook.modified = new Date(); console.log('正在创建 Excel 工作表...'); addSheet(workbook, '项目', projects); addSheet(workbook, '预售许可证', licenses); addSheet(workbook, '楼幢', buildings); await workbook.xlsx.writeFile(filePath); console.log(`✅ 成功生成多工作表 Excel 文件: ${filePath}`); } /** * Main function to read data and trigger XLSX generation. */ async function run() { console.log('🚀 开始生成 Excel 分析文件...'); const dataPath = path.join(process.cwd(), 'data', 'merged_data.json'); const outputPath = path.join(process.cwd(), '普宁房产数据分析.xlsx'); try { await fs.access(dataPath); console.log(`读取数据源: ${dataPath}`); const jsonData = JSON.parse(await fs.readFile(dataPath, 'utf-8')); await generateXlsx(jsonData, outputPath); } catch (error) { if (error.code === 'ENOENT') { console.error(`❌ 错误: 未找到数据文件 '${path.basename(dataPath)}'。`); console.error('请先运行 `npm start` 来生成完整的数据文件。'); } else { console.error('❌ 生成 XLSX 文件时发生错误:', error); } process.exit(1); } } run();