'use strict' var XLSX = require('xlsx'); var fs = require('fs'); var path = require('path'); var options = process.argv; if (options.length < 5) { console.log("需要3个参数(side src out)"); process.exit(); } //console.log(options[2], options[3], options[4]); var side = options[2]; var sideChar = ''; if (side == 'server') sideChar = 's'; else if (side == 'client') sideChar = 'c'; else { console.log("side should be server/client"); process.exit(); } var srcDir = options[3]; var outDir = options[4]; console.log("Xy导表脚本 XyExcelExporter"); console.log("作者: willz[qq3243309346]"); var keys = {}; //var values = []; var attrExport = []; var currentSheet = null; function isJson(obj) { var isJson = typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && !obj.length; return isJson; } function insertKey(list, line) { var obj = keys; for (var i = 0; i < list.length; i++) { if (!obj.hasOwnProperty(list[i])) { obj[list[i]] = {}; } if (i == list.length - 1) obj[list[i]] = line; else obj = obj[list[i]]; } } /* 给String原型链对象添加方法trim */ if (!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }; } function getCell(sheet, row, col) { var ref = XLSX.utils.encode_cell({ c: col, r: row }); var cell = sheet[ref]; return (cell ? cell.v : undefined); } function getMaxRow(sheet) { var range = XLSX.utils.decode_range(sheet['!ref']); //console.log(range); return range.e.r; } function getMaxCol(sheet) { var range = XLSX.utils.decode_range(sheet['!ref']); //console.log(range); return range.e.c; } function insertStr(soure, start, newStr) { return soure.slice(0, start) + newStr + soure.slice(start); } function encodeQuote(str) { if (str[0] != '"' || str[str.length - 1] != '"') return str; var i = 1; var f; while ((f = str.indexOf('"', i)) != -1 && f < str.length - 1) { str = insertStr(str, f, '\\'); //console.log(f, str); i = f + 2; } return str; } function getCellContent(sheet, row, col) { var cell = getCell(sheet, row, col); if (cell == undefined) return '""'; var str = '' + cell; str = str.replace(/\n/g, ' '); if (str.trim().length == 0) return '""'; return str; } function getCellNonEmpty(sheet, row, col) { var cell = getCell(sheet, row, col); if (cell == undefined) return ''; var str = '' + cell; str = str.replace(/\n/g, ' '); if (str.trim().length == 0) return ''; return str; } function handleValues(obj, numAttr, prefix) { var str = ''; for (var key in obj) { str += prefix + '[' + key + '] = {\n'; var val = obj[key]; if (isJson(val)) { str += handleValues(val, numAttr, prefix + '\t'); } else { for (var ai = 0; ai < numAttr; ai++) { if (!attrExport[ai]) continue; var value = getCellContent(currentSheet, 7 + val, 1 + ai); if (value != undefined) str += '\t' + prefix + getCell(currentSheet, 6, 1 + ai) + ' = ' + value + ',\n'; } } str += prefix + '},\n'; } return str; } function mkdirs(dirname, callback) { if (fs.existsSync(dirname)) { if (callback != undefined) callback(); } else { mkdirs(path.dirname(dirname), function () { fs.mkdirSync(dirname); if (callback != undefined) callback(); }); } } function removeDir(p) { if (!fs.existsSync(p)) return; let statObj = fs.statSync(p); // fs.statSync同步读取文件状态,判断是文件目录还是文件。 if (statObj.isDirectory()) { //如果是目录 let dirs = fs.readdirSync(p) //fs.readdirSync()同步的读取目标下的文件 返回一个不包括 '.' 和 '..' 的文件名的数组['b','a'] dirs = dirs.map(dir => path.join(p, dir)) //拼上完整的路径 for (let i = 0; i < dirs.length; i++) { // 深度 先将儿子移除掉 再删除掉自己 removeDir(dirs[i]); } fs.rmdirSync(p); //删除目录 } else { fs.unlinkSync(p); //删除文件 } } var countExport = 0; function handleFile(filePath) { var workbook = XLSX.readFile(filePath); //console.log(workbook); console.log("handle file: " + filePath + " with " + workbook.SheetNames.length + " sheets"); for (var s = 0; s < workbook.SheetNames.length; s++) { var sname = workbook.SheetNames[s]; var sheet = workbook.Sheets[sname]; //console.log(sheet); var exportType = getCell(sheet, 0, 1); if (exportType == 'base') { var exportFile = outDir + '/' + getCell(sheet, 1, 1); var fileHead = getCell(sheet, 0, 4); var fileTail = getCell(sheet, 1, 4); var numKeys = getCell(sheet, 2, 1); //console.log(exportFile, fileHead, fileTail, numKeys); if (sideChar == 'c') { var clientHead = getCellNonEmpty(sheet, 0, 6); if (clientHead != '') fileHead = clientHead; var clientFile = getCellNonEmpty(sheet, 1, 6); if (clientFile != '') exportFile = outDir + '/' + clientFile; } console.log("exporting sheet '" + sname + "' with type " + exportType + " to '" + exportFile + "'" ); exportFile = exportFile.toLowerCase(); mkdirs(path.dirname(exportFile)); var outLua = fileHead + '\n'; try { var data = fs.readFileSync(exportFile, 'utf-8'); var contents = data.split("\n"); contents[contents.length - 1] = ''; outLua = contents.join("\n"); } catch (err) { // 出错了 } var lines = getMaxRow(sheet) - 6; //console.log("line:" + lines); //if(lines == 0) // continue; keys = null; keys = {}; var isBreak = false; for (var li = 0; li < lines; li++) { var list = []; for (var ki = 0; ki < numKeys; ki++) { var str = getCell(sheet, 7 + li, 1 + ki); str = str == undefined ? '' : '' + str; if (str.trim().length == 0) { isBreak = true; break; } list[ki] = str; } if (!isBreak) insertKey(list, li); else isBreak = false; } //console.log(keys); attrExport = []; var numAttr = getMaxCol(sheet); var validAttr = 0; for (var ai = 0; ai < numAttr; ai++) { var exportParam = getCell(sheet, 5, 1 + ai); if (exportParam != undefined && exportParam.includes(sideChar)) { attrExport[ai] = true; validAttr++; } else attrExport[ai] = false; } //console.log(attrExport); //console.log("attr:" + numAttr); currentSheet = sheet; if (validAttr > 0) outLua += handleValues(keys, numAttr, '');; outLua += fileTail; fs.writeFileSync(exportFile, outLua, 'utf-8'); countExport++; } else if (exportType == 'tiny') { var exportFile = outDir + '/' + getCell(sheet, 1, 1); exportFile = exportFile.toLowerCase(); console.log("exporting sheet '" + sname + "' with type " + exportType + " to '" + exportFile + "'" ); var fileHead = getCell(sheet, 0, 4); var fileTail = getCell(sheet, 1, 4); mkdirs(path.dirname(exportFile)); var outLua = fileHead + '\n'; var lines = 0; for (var lines = 0; true;) { if (getCell(sheet, 5 + lines, 1) == undefined) break; lines++; } //console.log("line:" + lines); //if(lines == 0) // continue; for (var li = 0; li < lines; li++) { var exportParam = getCell(sheet, 5 + li, 1); if (!exportParam.includes(sideChar)) continue; var attr = getCell(sheet, 5 + li, 2); var value = getCellContent(sheet, 5 + li, 3); value = encodeQuote(value); if (value.startsWith('--#include')) outLua += value + '\n'; else if (attr != undefined) outLua += '\t' + attr + ' = ' + value + ',\n'; } outLua += fileTail; fs.writeFileSync(exportFile, outLua, 'utf-8'); countExport++; } } } function handleDir(filePath) { //根据文件路径读取文件,返回文件列表 var files = fs.readdirSync(filePath); //遍历读取到的文件列表 files.forEach(function (filename) { //获取当前文件的绝对路径 var filedir = path.join(filePath, filename); //根据文件路径获取文件信息,返回一个fs.Stats对象 var stats = fs.statSync(filedir); var isFile = stats.isFile();//是文件 var isDir = stats.isDirectory();//是文件夹 if (isFile && filedir.endsWith(".xlsx")) { //console.log(filedir); handleFile(filedir); } if (isDir) { handleDir(filedir);//递归,如果是文件夹,就继续遍历该文件夹下面的文件 } }); } removeDir(outDir); handleDir(srcDir); console.log("Total export: " + countExport);