index.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. let packageName = "excel-killer";
  2. let fs = require('fire-fs');
  3. let ffs = require('fs');
  4. let path = require('fire-path');
  5. let CfgUtil = Editor.require('packages://' + packageName + '/core/CfgUtil.js');
  6. let excelItem = Editor.require('packages://' + packageName + '/panel/item/excelItem.js');
  7. let nodeXlsx = Editor.require('packages://' + packageName + '/node_modules/node-xlsx');
  8. let Electron = require('electron');
  9. let uglifyJs = Editor.require('packages://' + packageName + '/node_modules/uglify-js');
  10. let fsExtra = Editor.require('packages://' + packageName + '/node_modules/fs-extra');
  11. let jsonBeautifully = Editor.require('packages://' + packageName + '/node_modules/json-beautifully');
  12. var chokidar = Editor.require('packages://' + packageName + '/node_modules/chokidar');
  13. Editor.Panel.extend({
  14. style: fs.readFileSync(Editor.url('packages://' + packageName + '/panel/index.css', 'utf8')) + "",
  15. template: fs.readFileSync(Editor.url('packages://' + packageName + '/panel/index.html', 'utf8')) + "",
  16. $: {
  17. logTextArea: '#logTextArea',
  18. },
  19. ready() {
  20. let logCtrl = this.$logTextArea;
  21. let logListScrollToBottom = function () {
  22. setTimeout(function () {
  23. logCtrl.scrollTop = logCtrl.scrollHeight;
  24. }, 10);
  25. };
  26. excelItem.init();
  27. window.plugin = new window.Vue({
  28. el: this.shadowRoot,
  29. created() {
  30. this._initPluginCfg();
  31. },
  32. init() {
  33. },
  34. data: {
  35. logView: "",
  36. excelRootPath: null,
  37. isMergeJson: false,
  38. isFormatJson: false,// 是否格式化Json
  39. jsonSavePath: null,//json文件存放目录
  40. isJsonAllCfgFileExist: false,// 是否单一配置文件存在
  41. jsonAllCfgFileName: null,// json配置文件名
  42. jsSavePath: null,// 插件资源目录
  43. jsFileName: null,//js配置文件名
  44. isJsFileExist: false,
  45. isFormatJsCode: false,
  46. excelArray: [],
  47. excelFileArr: [],
  48. },
  49. methods: {
  50. _addLog(str) {
  51. let time = new Date();
  52. // this.logView = "[" + time.toLocaleString() + "]: " + str + "\n" + this.logView;
  53. this.logView += "[" + time.toLocaleString() + "]: " + str + "\n";
  54. logListScrollToBottom();
  55. },
  56. onBtnClickTellMe() {
  57. // let data = nodeXlsx.parse(path.join(this.excelRootPath, 'test2.xlsx'));
  58. // console.log(data);
  59. // return;
  60. let url = "http://wpa.qq.com/msgrd?v=3&uin=774177933&site=qq&menu=yes";
  61. Electron.shell.openExternal(url);
  62. },
  63. _saveConfig() {
  64. let data = {
  65. excelRootPath: this.excelRootPath,
  66. jsFileName: this.jsFileName,
  67. jsonAllFileName: this.jsonAllCfgFileName,
  68. isMergeJson: this.isMergeJson,
  69. isFormatJsCode: this.isFormatJsCode,
  70. isFormatJson: this.isFormatJson,
  71. };
  72. CfgUtil.saveCfgByData(data);
  73. },
  74. _watchDir(event, filePath) {
  75. console.log(event, filePath);
  76. let ext = path.extname(filePath);
  77. if (ext === ".xlsx" || ext === ".xls") {
  78. this._onAnalyzeExcelDirPath(this.excelRootPath);
  79. }
  80. },
  81. onBtnClickHelpDoc(){
  82. let url = "https://github.com/tidys/CocosCreatorPlugins/tree/master/packages/excel-killer/README.md";
  83. Electron.shell.openExternal(url);
  84. },
  85. _initPluginCfg() {
  86. console.log("initCfg");
  87. CfgUtil.initCfg(function (data) {
  88. if (data) {
  89. this.excelRootPath = data.excelRootPath || "";
  90. if (fs.existsSync(this.excelRootPath)) {
  91. // this._onAnalyzeExcelDirPath(this.excelRootPath);
  92. chokidar.watch(this.excelRootPath).on('all', this._watchDir.bind(this));
  93. } else {
  94. }
  95. this.jsFileName = data.jsFileName || "GameJsCfg";
  96. this.jsonAllCfgFileName = data.jsonAllFileName || "GameJsonCfg";
  97. this.isMergeJson = data.isMergeJson;
  98. this.isFormatJsCode = data.isFormatJsCode;
  99. this.isFormatJson = data.isFormatJson;
  100. this.checkJsFileExist();
  101. this.checkJsonAllCfgFileExist();
  102. }
  103. }.bind(this));
  104. this._initCfgSavePath();// 默认json路径
  105. },
  106. _initCfgSavePath() {
  107. let projectPath = Editor.Project.path;
  108. let pluginResPath = path.join(projectPath, "plugin-resource");
  109. if (!fs.existsSync(pluginResPath)) {
  110. fs.mkdirSync(pluginResPath);
  111. }
  112. let jsonSavePath = path.join(pluginResPath, "json");
  113. if (!fs.existsSync(jsonSavePath)) {
  114. fs.mkdirSync(jsonSavePath);
  115. }
  116. this.jsonSavePath = jsonSavePath;
  117. let jsSavePath = path.join(pluginResPath, "js");
  118. if (!fs.existsSync(jsSavePath)) {
  119. fs.mkdirSync(jsSavePath);
  120. }
  121. this.jsSavePath = jsSavePath;
  122. let excelSavePath = path.join(pluginResPath, "excel");
  123. if (!fs.existsSync(excelSavePath)) {
  124. fs.mkdirSync(excelSavePath);
  125. }
  126. this.excelRootPath = excelSavePath;
  127. },
  128. onBtnClickFormatJson() {
  129. this.isFormatJson = !this.isFormatJson;
  130. this._saveConfig();
  131. },
  132. // 是否合并json
  133. onBtnClickMergeJson() {
  134. this.isMergeJson = !this.isMergeJson;
  135. this._saveConfig();
  136. },
  137. // 打开合并的json
  138. onBtnClickJsonAllCfgFile() {
  139. let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
  140. if (fs.existsSync(saveFileFullPath)) {
  141. Electron.shell.openItem(saveFileFullPath);
  142. Electron.shell.beep();
  143. } else {
  144. // this._addLog("目录不存在:" + this.resourceRootDir);
  145. this._addLog("目录不存在:" + saveFileFullPath);
  146. return;
  147. }
  148. },
  149. checkJsonAllCfgFileExist() {
  150. let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
  151. if (fs.existsSync(saveFileFullPath)) {
  152. this.isJsonAllCfgFileExist = true;
  153. } else {
  154. this.isJsonAllCfgFileExist = false;
  155. }
  156. },
  157. onBtnClickFormatJsCode() {
  158. this.isFormatJsCode = !this.isFormatJsCode;
  159. this._saveConfig();
  160. },
  161. onBtnClickOpenExcelRootPath() {
  162. if (fs.existsSync(this.excelRootPath)) {
  163. Electron.shell.showItemInFolder(this.excelRootPath);
  164. Electron.shell.beep();
  165. } else {
  166. this._addLog("目录不存在:" + this.excelRootPath);
  167. }
  168. },
  169. onBtnClickSelectExcelRootPath() {
  170. let res = Editor.Dialog.openFile({
  171. title: "选择Excel的根目录",
  172. defaultPath: Editor.Project.path,
  173. properties: ['openDirectory'],
  174. });
  175. if (res !== -1) {
  176. let dir = res[0];
  177. if (dir !== this.excelRootPath) {
  178. this.excelRootPath = dir;
  179. chokidar.watch(this.excelRootPath).on('all', this._watchDir.bind(this));
  180. // this._onAnalyzeExcelDirPath(dir);
  181. this._saveConfig();
  182. }
  183. }
  184. },
  185. // 修改js配置文件
  186. onJsFileNameChanged() {
  187. this._saveConfig();
  188. },
  189. // 修改json配置文件
  190. onJsonAllCfgFileChanged() {
  191. this._saveConfig();
  192. },
  193. // 查找出目录下的所有excel文件
  194. _onAnalyzeExcelDirPath(dir) {
  195. // let dir = path.normalize("D:\\proj\\CocosCreatorPlugins\\doc\\excel-killer");
  196. if (dir) {
  197. // 查找json文件
  198. let allFileArr = [];
  199. let excelFileArr = [];
  200. // 获取目录下所有的文件
  201. readDirSync(dir);
  202. // 过滤出来.xlsx的文件
  203. for (let k in allFileArr) {
  204. let file = allFileArr[k];
  205. let extName = path.extname(file);
  206. if (extName === ".xlsx" || extName === ".xls") {
  207. excelFileArr.push(file);
  208. } else {
  209. this._addLog("不支持的文件类型: " + file);
  210. }
  211. }
  212. this.excelFileArr = excelFileArr;
  213. // 组装显示的数据
  214. let excelSheetArray = [];
  215. let sheetDuplicationChecker = {};//表单重名检测
  216. for (let k in excelFileArr) {
  217. let itemFullPath = excelFileArr[k];
  218. // this._addLog("excel : " + itemFullPath);
  219. let excelData = nodeXlsx.parse(itemFullPath);
  220. //todo 检测重名的sheet
  221. for (let j in excelData) {
  222. let itemData = {
  223. isUse: true,
  224. fullPath: itemFullPath,
  225. name: "name",
  226. sheet: excelData[j].name
  227. };
  228. itemData.name = itemFullPath.substr(dir.length + 1, itemFullPath.length - dir.length);
  229. if (excelData[j].data.length === 0) {
  230. this._addLog("[Error] 空Sheet: " + itemData.name + " - " + itemData.sheet);
  231. continue;
  232. }
  233. if (sheetDuplicationChecker[itemData.sheet]) {
  234. // 重名sheet问题
  235. this._addLog("[Error ] 出现了重名sheet: " + itemData.sheet);
  236. this._addLog("[Sheet1] " + sheetDuplicationChecker[itemData.sheet].fullPath);
  237. this._addLog("[Sheet2] " + itemFullPath);
  238. this._addLog("请仔细检查Excel-Sheet!");
  239. } else {
  240. sheetDuplicationChecker[itemData.sheet] = itemData;
  241. excelSheetArray.push(itemData);
  242. }
  243. }
  244. }
  245. this.excelArray = excelSheetArray;
  246. function readDirSync(dirPath) {
  247. let dirInfo = fs.readdirSync(dirPath);
  248. for (let i = 0; i < dirInfo.length; i++) {
  249. let item = dirInfo[i];
  250. let itemFullPath = path.join(dirPath, item);
  251. let info = fs.statSync(itemFullPath);
  252. if (info.isDirectory()) {
  253. // this._addLog('dir: ' + itemFullPath);
  254. readDirSync(itemFullPath);
  255. } else if (info.isFile()) {
  256. let headStr = item.substr(0, 2);
  257. if (headStr === "~$") {
  258. window.plugin._addLog("检索到excel产生的临时文件:" + itemFullPath);
  259. } else {
  260. allFileArr.push(itemFullPath);
  261. }
  262. // this._addLog('file: ' + itemFullPath);
  263. }
  264. }
  265. }
  266. }
  267. },
  268. onBtnClickSelectSheet(event) {
  269. let b = event.currentTarget.value;
  270. for (let k in this.excelArray) {
  271. this.excelArray[k].isUse = b;
  272. }
  273. },
  274. onBtnClickOpenJsonSavePath() {
  275. if (fs.existsSync(this.jsonSavePath)) {
  276. Electron.shell.showItemInFolder(this.jsonSavePath);
  277. Electron.shell.beep();
  278. } else {
  279. this._addLog("目录不存在:" + this.jsonSavePath);
  280. }
  281. },
  282. onBtnClickOpenJsSavePath() {
  283. if (fs.existsSync(this.jsSavePath)) {
  284. Electron.shell.showItemInFolder(this.jsSavePath);
  285. Electron.shell.beep();
  286. } else {
  287. this._addLog("目录不存在:" + this.jsSavePath);
  288. }
  289. },
  290. _getJavaScriptSaveData(excelData, itemSheet) {
  291. let title = excelData[0];
  292. let desc = excelData[1];
  293. let sheetFormatData = {};
  294. for (let i = 2; i < excelData.length; i++) {
  295. let lineData = excelData[i];
  296. if (lineData.length === 0) {
  297. // 空行直接跳过
  298. continue;
  299. } else {
  300. // if (lineData.length < title.length) {
  301. // this._addLog("[Error] 发现第" + i + "行缺少字段,跳过改行数据配置.");
  302. // continue;
  303. // } else if (lineData.length > title.length) {
  304. // this._addLog("[Error] 发现第" + i + "行多余字段,跳过改行数据配置.");
  305. // continue;
  306. // }
  307. }
  308. let saveLineData = {};
  309. for (let j = 1; j < title.length; j++) {
  310. let key = title[j];
  311. let value = lineData[j];
  312. if (value === undefined) {
  313. value = "";
  314. // this._addLog("[Error] 发现空单元格:" + itemSheet.name + "*" + itemSheet.sheet + " => (" + key + "," + (i + 1) + ")");
  315. }
  316. saveLineData[key] = value;
  317. }
  318. sheetFormatData[lineData[0].toString()] = saveLineData;
  319. }
  320. return sheetFormatData;
  321. },
  322. _getJsonSaveData(excelData, itemSheet) {
  323. let title = excelData[0];
  324. let desc = excelData[1];
  325. let ret = null;
  326. let useFormat1 = false;
  327. if (useFormat1) {
  328. let saveData1 = [];// 格式1:对应的为数组
  329. for (let i = 2; i < excelData.length; i++) {
  330. let lineData = excelData[i];
  331. // if (lineData.length < title.length) {
  332. // continue;
  333. // } else if (lineData.length > title.length) {
  334. // continue;
  335. // }
  336. if(!lineData) continue;
  337. let saveLineData = {};
  338. for (let j = 0; j < title.length; j++) {
  339. let key = title[j];
  340. let value = lineData[j];
  341. if (value === undefined) {
  342. value = "";
  343. }
  344. // this._addLog("" + value);
  345. saveLineData[key] = value;
  346. }
  347. saveData1.push(saveLineData);
  348. }
  349. ret = saveData1;
  350. } else {
  351. let saveData2 = {};// 格式2:id作为索引
  352. for (let i = 2; i < excelData.length; i++) {
  353. let lineData = excelData[i];
  354. // if (lineData.length < title.length) {
  355. // continue;
  356. // } else if (lineData.length > title.length) {
  357. // continue;
  358. // }
  359. if(!lineData) continue;
  360. let saveLineData = {};
  361. for (let j = 1; j < title.length; j++) {
  362. let key = title[j];
  363. let value = lineData[j];
  364. if (value === undefined) {
  365. value = "";
  366. }
  367. // this._addLog("" + value);
  368. saveLineData[key] = value;
  369. }
  370. saveData2[lineData[0].toString()] = saveLineData;
  371. }
  372. ret = saveData2;
  373. }
  374. return ret;
  375. },
  376. // 打开生成的js配置文件
  377. onBtnClickOpenJsFile() {
  378. let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
  379. if (fs.existsSync(saveFileFullPath)) {
  380. Electron.shell.openItem(saveFileFullPath);
  381. Electron.shell.beep();
  382. } else {
  383. this._addLog("目录不存在:" + this.saveFileFullPath);
  384. }
  385. },
  386. // 检测js配置文件是否存在
  387. checkJsFileExist() {
  388. let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
  389. if (fs.existsSync(saveFileFullPath)) {
  390. this.isJsFileExist = true;
  391. } else {
  392. this.isJsFileExist = false;
  393. }
  394. },
  395. // 生成配置
  396. onBtnClickGen() {
  397. console.log("onBtnClickGen");
  398. // 参数校验
  399. if (this.excelArray.length <= 0) {
  400. this._addLog("未发现要生成的配置!");
  401. return;
  402. }
  403. if (this.isMergeJson) {
  404. if (this.jsonAllCfgFileName.length <= 0) {
  405. this._addLog("请输入要保存的json文件名!");
  406. return;
  407. }
  408. }
  409. if (this.jsFileName.length <= 0) {
  410. this._addLog("请输入要保存的js文件名!");
  411. return;
  412. }
  413. this.logView = "";
  414. // 删除老的配置
  415. fsExtra.emptyDirSync(this.jsonSavePath);
  416. fsExtra.emptyDirSync(this.jsSavePath);
  417. let jsSaveData = {};// 保存的js数据
  418. let jsonAllSaveData = {};// 保存的json数据
  419. for (let k in this.excelArray) {
  420. let itemSheet = this.excelArray[k];
  421. if (itemSheet.isUse) {
  422. let excelData = nodeXlsx.parse(itemSheet.fullPath);
  423. let sheetData = null;
  424. for (let j in excelData) {
  425. if (excelData[j].name === itemSheet.sheet) {
  426. sheetData = excelData[j].data;
  427. }
  428. }
  429. if (sheetData) {
  430. if (sheetData.length > 2) {
  431. // 保存为json
  432. let jsonSaveData = this._getJsonSaveData(sheetData, itemSheet);
  433. if (this.isMergeJson) {
  434. jsonAllSaveData[itemSheet.sheet] = jsonSaveData;
  435. } else {
  436. let saveStr = JSON.stringify(jsonSaveData);
  437. if (this.isFormatJson) {// 格式化json
  438. saveStr = jsonBeautifully(saveStr);
  439. }
  440. let saveFileFullPath = path.join(this.jsonSavePath, itemSheet.sheet + ".json");
  441. fs.writeFileSync(saveFileFullPath, saveStr);
  442. this._addLog("[Json]:" + saveFileFullPath);
  443. let projectJson = path.join(Editor.Project.path, 'assets/resources/json');
  444. projectJson = path.join(projectJson, itemSheet.sheet + ".json");
  445. if(ffs.existsSync(projectJson)) {
  446. ffs.rmSync(projectJson);
  447. }
  448. fs.writeFileSync(projectJson, saveStr);
  449. this._addLog("[Json]:" + projectJson);
  450. }
  451. // 保存为js
  452. let sheetJsData = this._getJavaScriptSaveData(sheetData, itemSheet);
  453. // 检测重复问题
  454. if (jsSaveData[itemSheet.sheet] === undefined) {
  455. jsSaveData[itemSheet.sheet] = sheetJsData;
  456. } else {
  457. this._addLog("发现重名sheet:" + itemSheet.name + "(" + itemSheet.sheet + ")");
  458. }
  459. } else {
  460. this._addLog("行数低于2行,无效sheet:" + itemSheet.sheet);
  461. }
  462. } else {
  463. this._addLog("未发现数据");
  464. }
  465. } else {
  466. console.log("忽略配置: " + itemSheet.fullPath + ' - ' + itemSheet.sheet);
  467. }
  468. }
  469. // =====================>>>> 保存json文件 <<<=================================
  470. if (this.isMergeJson) {
  471. let saveFileFullPath = path.join(this.jsonSavePath, this.jsonAllCfgFileName + ".json");
  472. let str = JSON.stringify(jsonAllSaveData);
  473. if (this.isFormatJson) {
  474. str = jsonBeautifully(str);
  475. }
  476. fs.writeFileSync(saveFileFullPath, str);
  477. this._addLog("[Json]:" + saveFileFullPath);
  478. }
  479. // =====================>>>> 保存js文件 <<<=================================
  480. // TODO 保证key的顺序一致性
  481. let saveFileFullPath = path.join(this.jsSavePath, this.jsFileName + ".js");
  482. let saveStr = "module.exports = " + JSON.stringify(jsSaveData) + ";";
  483. if (this.isFormatJsCode) {// 保存为格式化代码
  484. let ast = uglifyJs.parse(saveStr);
  485. let ret = uglifyJs.minify(ast, {
  486. output: {
  487. beautify: true,//如果希望得到格式化的输出,传入true
  488. indent_start: 0,//(仅当beautify为true时有效) - 初始缩进空格
  489. indent_level: 4,//(仅当beautify为true时有效) - 缩进级别,空格数量
  490. }
  491. });
  492. if (ret.error) {
  493. this._addLog('error: ' + ret.error.message);
  494. } else if (ret.code) {
  495. fs.writeFileSync(saveFileFullPath, ret.code);
  496. this._addLog("[JavaScript]" + saveFileFullPath);
  497. } else {
  498. this._addLog(JSON.stringify(ret));
  499. }
  500. } else {// 保存为单行代码
  501. fs.writeFileSync(saveFileFullPath, saveStr);
  502. this._addLog("[JavaScript]" + saveFileFullPath);
  503. }
  504. this._addLog("全部转换完成!");
  505. this.checkJsFileExist();
  506. this.checkJsonAllCfgFileExist();
  507. },
  508. },
  509. });
  510. },
  511. messages: {
  512. 'excel-killer:hello'(event) {
  513. }
  514. }
  515. });