index.js 26 KB

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