Files
svn/tech/tools/Excel2Json.js
2025-08-04 10:46:00 +08:00

401 lines
13 KiB
JavaScript
Raw Permalink 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.
// Excel2Json.js
//
const fs = require('fs');
const path = require('path');
const nodeXlsx = require('node-xlsx');
const typeFile = 'D:\\Jenkins\\fishClient\\fishClient\\config.d.ts'
let FileTarget = {
General: 0,
Client: 1,
Server: 2
};
let MainRun = {
sourceFile: process.argv[2],
clientFile: process.argv[3],
serverFile: process.argv[4],
options: process.argv[5],
sourceDir: '',
typeMap: {},
start() {
// 非绝对路径则加上当前目录
if (this.sourceFile && !path.isAbsolute(this.sourceFile)) {
this.sourceFile = path.join(__dirname, this.sourceFile);
}
if (this.clientFile && !path.isAbsolute(this.clientFile)) {
this.clientFile = path.join(__dirname, this.clientFile);
}
if (this.serverFile && !path.isAbsolute(this.serverFile)) {
this.serverFile = path.join(__dirname, this.serverFile);
}
if (!this.options || this.options != '-add') {
this.delAllFiles(this.clientFile);
this.delAllFiles(this.serverFile);
}
this.traversalDir(this.sourceFile, this.clientFile, this.serverFile);
},
traversalDir(srcDir, clientDir, serverDir) {
if (!srcDir || !fs.existsSync(srcDir)) {
console.error("invalid srcDir=" + srcDir);
return;
}
this.sourceDir = srcDir;
let files = fs.readdirSync(srcDir);
for (let i = 0, len = files.length; i < len; i++) {
let file = files[i];
if (file.indexOf('~$') === 0 || (file.indexOf('.xlsx') < 0 && file.indexOf('.xlsm') < 0)) {
continue; // 排除无效文件
}
let curPath = path.join(srcDir, file);
let stats = fs.statSync(curPath);
if (stats.isDirectory()) {
this.traversalDir(curPath, clientDir, serverDir);
continue;
}
this.readExcelFile(curPath, clientDir, serverDir);
}
if (fs.existsSync(typeFile)) {
let str = JSON.stringify(this.typeMap)
str = str.replace('{', 'declare type ')
str = str.replace(/-/g, '')
str = str.replace(/"/g, '')
str = str.replace(/(int)/g, 'number')
str = str.replace(/(float)/g, 'number')
str = str.replace(/},/g, '}\ndeclare type ')
str = str.replace(/(:{)/g, ' = {')
str = str.substr(0, str.length - 1)
fs.writeFileSync(typeFile, str)
}
},
// 一个Excel文件可能包含N张表
readExcelFile(srcFile, clientDir, serverDir) {
if (!srcFile || !fs.existsSync(srcFile)) {
console.error('readExcelFile: invalid file path');
return;
}
let sheetArr = nodeXlsx.parse(srcFile);
for (let i = 0, len = sheetArr.length; i < len; i++) {
let sheet = sheetArr[i];
let sheetName = this.getSheetName(sheet);
if (!sheetName || sheetName.length <= 0) {
console.error('sheet is invalid. name=' + sheet.name);
continue;
}
try {
let outData = {};
let csArr = this.lowerArray(sheet.data[0]);
let typeArr = this.lowerArray(sheet.data[1]);
let keyArr = sheet.data[2];
if (csArr && csArr[0] === 'key-value') { // key-value表
outData = this.jsonObjectFromSheet(sheet, csArr, typeArr, keyArr);
} else if (csArr && csArr[0] === 'language') { // 多语言js文件特殊处理
let content = this.i18nJsFromSheet(sheet, csArr, typeArr, keyArr);
let fullPath = path.join(this.sourceDir, 'zh.js');
if (fullPath && content && content.length > 0) {
this.writeFile(fullPath, content);
}
continue;
} else { // 普通表
outData = this.jsonArrayFromSheet(sheet, csArr, typeArr, keyArr);
}
// 输出客户端配置文件
if (!!clientDir && clientDir.length > 0) {
let clientFile = path.join(clientDir, sheetName);
if (outData && outData.clientStr && outData.clientStr.length > 0) {
let outStr = outData.clientStr.replace(/\\\\n/g, "\\n");
this.writeFile(clientFile, outStr);
}
}
// 输出服务器配置文件
if (!!serverDir && serverDir.length > 0) {
let serverFile = path.join(serverDir, sheetName);
if (outData && outData.serverStr && outData.serverStr.length > 0) {
this.writeFile(serverFile, outData.serverStr);
}
}
} catch (err) {
console.error('error to convert sheet, name=' + sheet.name + ', err=' + err.message);
}
}
},
// 返回Array格式的字符串
jsonArrayFromSheet(sheet, csArr, typeArr, keyArr) {
if (!sheet || !csArr || !typeArr || !keyArr) {
return null;
}
let clientArr = [];
let serverArr = [];
let objKey = this.getSheetName(sheet).replace('Conf', '').replace('.json', '')
// console.log("-------------sheet----------" + objKey)
let obj = {}
let typesArr = sheet.data[0]
let valueTypeArr = sheet.data[1]
let keysArr = sheet.data[2]
keysArr[0] = keysArr[0].toLowerCase()
for (let i = 0; i < typeArr.length; i++) {
let type = typesArr[i]
if ('c' == type || 'cs' == type) {
obj[keysArr[i]] = valueTypeArr[i]
this.typeMap[objKey] = obj
}
}
for (let i = 4, len = sheet.data.length; i < len; i++) {
let data = sheet.data[i];
if (data.length <= 0 || data[0] == undefined) {
break; // 碰到空行则直接跳出
}
let clientData = {};
let serverData = {};
for (let j = 0, len2 = csArr.length; j < len2; j++) {
if (!this.checkColumnIsValid(csArr[j], typeArr[j], keyArr[j])) {
break;
}
// 第一列表头转换为小写(兼容以前格式)
let keyName = keyArr[j];
if (j === 0) {
keyName = keyName.toLowerCase();
}
let value = this.formatValueByType(data[j], typeArr[j]);
let target = this.getFileTarget(csArr[j]);
if (FileTarget.Client === target) {
clientData[keyName] = value;
} else if (FileTarget.Server === target) {
serverData[keyName] = value;
} else {
clientData[keyName] = value;
serverData[keyName] = value;
}
}
let keys = Object.keys(clientData);
if (keys.length > 0) {
clientArr.push(clientData);
}
keys = Object.keys(serverData);
if (keys.length > 0) {
serverArr.push(serverData);
}
}
let clientStr = '';
let serverStr = '';
if (clientArr && clientArr.length > 0) {
clientStr = JSON.stringify(clientArr, null, '\t'); // 格式化Json输出
}
if (serverArr && serverArr.length > 0) {
serverStr = JSON.stringify(serverArr, null, '\t'); // 格式化Json输出
}
return { clientStr, serverStr };
},
// 返回Object格式的字符串
jsonObjectFromSheet(sheet, csArr, typeArr, keyArr) {
if (!sheet || !csArr || !typeArr || !keyArr) {
return null;
}
let clientObj = {};
let serverObj = {};
// 如果指定了特殊key则取指定列否则默认取第1列
let langIndex = 1;
if (this.options && this.options.length > 0) {
let data = sheet.data[2];
for (let line = 0; line < 10; line++) { // 只遍历前10列
if (data[line] && data[line] == this.options) {
langIndex = line;
break;
}
}
}
for (let i = 4, len = sheet.data.length; i < len; i++) {
let data = sheet.data[i];
if (data.length <= 0 || !data[0]) {
break; // 碰到空行则直接跳出
}
let keyName = data[0];
let value = this.formatValueByType(data[langIndex], typeArr[1]);
let target = this.getFileTarget(csArr[1]);
if (FileTarget.Client === target) {
clientObj[keyName] = value;
} else if (FileTarget.Server === target) {
serverObj[keyName] = value;
} else {
clientObj[keyName] = value;
serverObj[keyName] = value;
}
}
let clientStr = '';
let serverStr = '';
let keys = Object.keys(clientObj);
if (keys.length > 0) {
clientStr = JSON.stringify(clientObj, null, '\t'); // 格式化Json输出
}
keys = Object.keys(serverObj);
if (keys.length > 0) {
serverStr = JSON.stringify(serverObj, null, '\t'); // 格式化Json输出
}
return { clientStr, serverStr };
},
// 返回i18n标准的js代码字符串
i18nJsFromSheet(sheet, csArr, typeArr, keyArr) {
let outData = this.jsonObjectFromSheet(sheet, csArr, typeArr, keyArr);
let str = '';
if (outData && outData.clientStr && outData.clientStr.length > 0) {
let outStr = outData.clientStr.replace(/\\\\n/g, "\\n");
str = `if (!window.i18n) {window.i18n = {};}
if (!window.i18n.languages) {window.i18n.languages = {};}
window.i18n.languages['zh'] = ` + outStr + ';';
}
return str;
},
writeFile(fullPath, content) {
if (!fullPath || !content) {
console.error('writeFile: invalid params, path=' + fullPath);
return;
}
fs.writeFile(fullPath, content, (err) => {
if (err) {
console.error('writeFile error: ' + err);
return;
}
// console.log(content);
//console.log('Success to write file: ' + fullPath);
});
},
// 删除目录下所有文件(不包含子目录)
delAllFiles(dir) {
if (dir && fs.existsSync(dir)) {
let files = fs.readdirSync(dir);
files.forEach((file, index) => {
let curPath = path.join(dir, file);
if (!fs.statSync(curPath).isDirectory()) {
fs.unlinkSync(curPath);
}
});
}
},
// 获取有效数组并转换为小写
lowerArray(data) {
let arr = [];
if (!data || data.length <= 0) {
return arr;
}
for (let i = 0, len = data.length; i < len; i++) {
let str = data[i];
if (!str || str.length <= 0) {
break; // 碰到空字符串代表之后的列都不需要,所以直接返回
}
arr.push(str.toLowerCase());
}
return arr;
},
getFileTarget(cs) {
let ret = FileTarget.General;
if (cs === 'c') {
ret = FileTarget.Client;
} else if (cs === 's') {
ret = FileTarget.Server;
}
return ret;
},
checkColumnIsValid(cs, type, key) {
if (!cs || cs.length <= 0) {
return false;
}
if (cs !== 'cs' && cs !== 'c' && cs !== 's') {
return false;
}
if (!type || type.length <= 0) {
return false;
}
if (key == undefined || key.length <= 0) {
return false;
}
return true;
},
getSheetName(sheet) {
let name = '';
if (!sheet.name || sheet.name.length <= 0 || !sheet.data || sheet.data.length <= 0) {
return name;
}
let arr = sheet.name.split('|');
if (!arr || arr.length !== 2) {
return name;
}
name = 'Conf' + arr[1] + '.json';
return name;
},
formatValueByType(value, type) {
let ret = null;
switch (type) {
case 'string':
case 'int[]':
ret = (value != undefined) ? String(value) : '';
break;
case 'int':
ret = (value != undefined) ? parseInt(value) : 0;
break;
case 'float':
ret = (value != undefined) ? parseFloat(value) : 0;
ret = this.keepFloat8(ret);
break;
default:
ret = null;
break;
}
return ret;
},
// 保留小数点后8位输入参数为浮点数
keepFloat8(num) {
let ret = Math.round(num * 100000000) / 100000000;
return ret;
},
};
module.exports = MainRun;
MainRun.start();