Authored by 陈峰

Merge branch 'hotfix/excel-fix' into 'release/6.7.9'

Hotfix/excel fix



See merge request !65
@@ -5,8 +5,8 @@ @@ -5,8 +5,8 @@
5 */ 5 */
6 const _ = require('lodash'); 6 const _ = require('lodash');
7 const moment = require('moment'); 7 const moment = require('moment');
8 -const excelExport = require('excel-export');  
9 const ActivityModel = require('../models/activity'); 8 const ActivityModel = require('../models/activity');
  9 +const {excel_export} = require('../../../utils/excel');
10 10
11 const DO_SUCCESS = '操作成功'; 11 const DO_SUCCESS = '操作成功';
12 const DO_FAILED = '操作失败!'; 12 const DO_FAILED = '操作失败!';
@@ -80,10 +80,14 @@ const zeroBuy = { @@ -80,10 +80,14 @@ const zeroBuy = {
80 } 80 }
81 81
82 req.ctx(ActivityModel).getZerobuyExportList(req.query.id) 82 req.ctx(ActivityModel).getZerobuyExportList(req.query.id)
83 - .then(result => {  
84 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
85 - res.setHeader('Content-Disposition', `attachment; filename=zerobuy_${id}_${new Date().getTime()}.xlsx`);  
86 - res.end(excelExport.execute(result), 'binary'); 83 + .then(conf => {
  84 + return excel_export({
  85 + title: conf.cols,
  86 + data: conf.rows,
  87 + fileName: `zerobuy_${id}_${new Date().getTime()}`,
  88 + sheetName: conf.name,
  89 + res
  90 + });
87 }).catch(next); 91 }).catch(next);
88 }, 92 },
89 zeroBuyEdit(req, res, next) { 93 zeroBuyEdit(req, res, next) {
@@ -581,32 +585,7 @@ const activity = { @@ -581,32 +585,7 @@ const activity = {
581 585
582 let conf = { 586 let conf = {
583 name: 'mysheet', 587 name: 'mysheet',
584 - cols: [  
585 - {  
586 - caption: '文章ID',  
587 - type: 'number'  
588 - },  
589 - {  
590 - caption: '内容',  
591 - type: 'string'  
592 - },  
593 - {  
594 - caption: '赞数',  
595 - type: 'number'  
596 - },  
597 - {  
598 - caption: '发布者',  
599 - type: 'string'  
600 - },  
601 - {  
602 - caption: '手机号',  
603 - type: 'string'  
604 - },  
605 - {  
606 - caption: '创建时间',  
607 - type: 'string'  
608 - }  
609 - ], 588 + cols: ['文章ID', '内容', '赞数', '发布者', '手机号', '创建时间'],
610 rows: [] 589 rows: []
611 }; 590 };
612 591
@@ -625,11 +604,13 @@ const activity = { @@ -625,11 +604,13 @@ const activity = {
625 conf.rows.push(temp); 604 conf.rows.push(temp);
626 }); 605 });
627 606
628 - let exportFile = excelExport.execute(conf);  
629 -  
630 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
631 - res.setHeader('Content-Disposition', 'attachment; filename=articleList.xlsx');  
632 - res.end(exportFile, 'binary'); 607 + return excel_export({
  608 + title: conf.cols,
  609 + data: conf.rows,
  610 + fileName: 'articleList',
  611 + sheetName: conf.name,
  612 + res
  613 + });
633 }) 614 })
634 .catch(next); 615 .catch(next);
635 616
@@ -7,13 +7,11 @@ @@ -7,13 +7,11 @@
7 const _ = require('lodash'); 7 const _ = require('lodash');
8 const CouponModel = require('../models/coupon'); 8 const CouponModel = require('../models/coupon');
9 const moment = require('moment'); 9 const moment = require('moment');
10 -const excelExport = require('excel-export');  
11 const INVALID_PARAMS = '参数错误'; 10 const INVALID_PARAMS = '参数错误';
12 -  
13 -  
14 const xlsx = require('xlsx'); 11 const xlsx = require('xlsx');
15 const DO_SUCCESS = '操作成功'; 12 const DO_SUCCESS = '操作成功';
16 const GET_SUCCESS = '获取成功'; 13 const GET_SUCCESS = '获取成功';
  14 +const {excel_export} = require('../../../utils/excel');
17 15
18 let loadExcel = function(path) { 16 let loadExcel = function(path) {
19 let workbook = xlsx.readFile(path); 17 let workbook = xlsx.readFile(path);
@@ -147,22 +145,13 @@ const couponController = { @@ -147,22 +145,13 @@ const couponController = {
147 } 145 }
148 }, 146 },
149 downloadTpl(req, res) { 147 downloadTpl(req, res) {
150 - let conf = {  
151 - name: 'mysheet',  
152 - cols: [  
153 - {  
154 - caption: 'couponNo',  
155 - type: 'String'  
156 - }  
157 - ],  
158 - rows: [['c3236233105'], ['c3236233105']]  
159 - };  
160 - let exportFile = excelExport.execute(conf);  
161 -  
162 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
163 - res.setHeader('Content-Disposition', 'attachment; filename=couponNoTpl.xlsx');  
164 - res.end(exportFile, 'binary');  
165 - 148 + return excel_export({
  149 + title: ['couponNo'],
  150 + data: [['c3236233105'], ['c3236233105']],
  151 + fileName: 'couponNoTpl',
  152 + sheetName: '优惠券',
  153 + res
  154 + });
166 }, 155 },
167 downloadNoList(req, res, next) { 156 downloadNoList(req, res, next) {
168 const couponId = req.query.id; 157 const couponId = req.query.id;
@@ -178,24 +167,7 @@ const couponController = { @@ -178,24 +167,7 @@ const couponController = {
178 167
179 let conf = { 168 let conf = {
180 name: 'mysheet', 169 name: 'mysheet',
181 - cols: [  
182 - {  
183 - caption: '券ID',  
184 - type: 'number'  
185 - },  
186 - {  
187 - caption: '券码',  
188 - type: 'string'  
189 - },  
190 - {  
191 - caption: '用户id',  
192 - type: 'number'  
193 - },  
194 - {  
195 - caption: '领取时间',  
196 - type: 'string'  
197 - }  
198 - ], 170 + cols: ['券ID', '券码', '用户id', '领取时间'],
199 rows: [] 171 rows: []
200 }; 172 };
201 173
@@ -215,12 +187,13 @@ const couponController = { @@ -215,12 +187,13 @@ const couponController = {
215 } 187 }
216 conf.rows.push(temp); 188 conf.rows.push(temp);
217 }); 189 });
218 -  
219 - let exportFile = excelExport.execute(conf);  
220 -  
221 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
222 - res.setHeader('Content-Disposition', 'attachment; filename=couponList.xlsx');  
223 - res.end(exportFile, 'binary'); 190 + return excel_export({
  191 + title: conf.cols,
  192 + data: conf.rows,
  193 + fileName: 'couponList',
  194 + sheetName: conf.name,
  195 + res
  196 + });
224 }) 197 })
225 .catch(next); 198 .catch(next);
226 199
@@ -5,9 +5,9 @@ @@ -5,9 +5,9 @@
5 */ 5 */
6 const _ = require('lodash'); 6 const _ = require('lodash');
7 const moment = require('moment'); 7 const moment = require('moment');
8 -const excelExport = require('excel-export');  
9 const UserModel = require('../models/user'); 8 const UserModel = require('../models/user');
10 const camelcase = require('camelcase'); 9 const camelcase = require('camelcase');
  10 +const {excel_export} = require('../../../utils/excel');
11 11
12 const DO_SUCCESS = '操作成功'; 12 const DO_SUCCESS = '操作成功';
13 const GET_SUCCESS = '获取成功'; 13 const GET_SUCCESS = '获取成功';
@@ -235,24 +235,7 @@ const userController = { @@ -235,24 +235,7 @@ const userController = {
235 exportUserList(req, res, next) { 235 exportUserList(req, res, next) {
236 let conf = { 236 let conf = {
237 name: 'mysheet', 237 name: 'mysheet',
238 - cols: [  
239 - {  
240 - caption: '用户ID',  
241 - type: 'number'  
242 - },  
243 - {  
244 - caption: '昵称',  
245 - type: 'string'  
246 - },  
247 - {  
248 - caption: '手机号',  
249 - type: 'string'  
250 - },  
251 - {  
252 - caption: '创建时间',  
253 - type: 'string'  
254 - }  
255 - ], 238 + cols: ['用户ID', '昵称', '手机号', '创建时间'],
256 rows: [] 239 rows: []
257 }; 240 };
258 241
@@ -269,11 +252,13 @@ const userController = { @@ -269,11 +252,13 @@ const userController = {
269 conf.rows.push(temp); 252 conf.rows.push(temp);
270 }); 253 });
271 254
272 - let exportFile = excelExport.execute(conf);  
273 -  
274 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
275 - res.setHeader('Content-Disposition', 'attachment; filename=userList.xlsx');  
276 - res.end(exportFile, 'binary'); 255 + return excel_export({
  256 + title: conf.cols,
  257 + data: conf.rows,
  258 + fileName: 'userList',
  259 + sheetName: conf.name,
  260 + res
  261 + });
277 }) 262 })
278 .catch(next); 263 .catch(next);
279 }, 264 },
@@ -281,39 +266,14 @@ const userController = { @@ -281,39 +266,14 @@ const userController = {
281 let conf = { 266 let conf = {
282 name: 'myInfoSheet', 267 name: 'myInfoSheet',
283 cols: [ 268 cols: [
284 - {  
285 - caption: '用户ID',  
286 - type: 'number'  
287 - },  
288 - {  
289 - caption: '昵称',  
290 - type: 'string'  
291 - },  
292 - {  
293 - caption: '生日',  
294 - type: 'string'  
295 - },  
296 - {  
297 - caption: '性别',  
298 - type: 'string'  
299 - },  
300 - {  
301 - caption: '手机号',  
302 - type: 'string'  
303 - },  
304 - {  
305 - caption: 'email',  
306 - type: 'string'  
307 - },  
308 - {  
309 - caption: '省份',  
310 - type: 'string'  
311 - },  
312 - {  
313 - caption: '城市',  
314 - type: 'string'  
315 - },  
316 - ], 269 + '用户ID',
  270 + '昵称',
  271 + '生日',
  272 + '性别',
  273 + '手机号',
  274 + 'email',
  275 + '省份',
  276 + '城市'],
317 rows: [] 277 rows: []
318 }; 278 };
319 279
@@ -334,38 +294,24 @@ const userController = { @@ -334,38 +294,24 @@ const userController = {
334 conf.rows.push(temp); 294 conf.rows.push(temp);
335 }); 295 });
336 296
337 - let exportFile = excelExport.execute(conf);  
338 -  
339 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
340 - res.setHeader('Content-Disposition', 'attachment; filename=userInfoList.xlsx');  
341 - res.end(exportFile, 'binary'); 297 + return excel_export({
  298 + title: conf.cols,
  299 + data: conf.rows,
  300 + fileName: 'userInfoList',
  301 + sheetName: conf.name,
  302 + res
  303 + });
342 }).catch(next); 304 }).catch(next);
343 }, 305 },
344 exportPrizeUserList(req, res, next) { 306 exportPrizeUserList(req, res, next) {
345 let conf = { 307 let conf = {
346 name: 'myInfoSheet', 308 name: 'myInfoSheet',
347 cols: [ 309 cols: [
348 - {  
349 - caption: '用户ID',  
350 - type: 'number'  
351 - },  
352 - {  
353 - caption: '昵称',  
354 - type: 'string'  
355 - },  
356 - {  
357 - caption: '手机号',  
358 - type: 'string'  
359 - },  
360 - {  
361 - caption: '地址',  
362 - type: 'string'  
363 - },  
364 - {  
365 - caption: '鞋码',  
366 - type: 'number'  
367 - }  
368 - ], 310 + '用户ID',
  311 + '昵称',
  312 + '手机号',
  313 + '地址',
  314 + '鞋码'],
369 rows: [] 315 rows: []
370 }; 316 };
371 317
@@ -383,11 +329,13 @@ const userController = { @@ -383,11 +329,13 @@ const userController = {
383 conf.rows.push(temp); 329 conf.rows.push(temp);
384 }); 330 });
385 331
386 - let exportFile = excelExport.execute(conf);  
387 -  
388 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
389 - res.setHeader('Content-Disposition', 'attachment; filename=userInfoList.xlsx');  
390 - res.end(exportFile, 'binary'); 332 + return excel_export({
  333 + title: conf.cols,
  334 + data: conf.rows,
  335 + fileName: 'prizeUserList',
  336 + sheetName: conf.name,
  337 + res
  338 + });
391 }).catch(next); 339 }).catch(next);
392 }, 340 },
393 userLoginLog(req, res) { 341 userLoginLog(req, res) {
@@ -404,28 +352,7 @@ const userController = { @@ -404,28 +352,7 @@ const userController = {
404 352
405 let conf = { 353 let conf = {
406 name: 'mysheet', 354 name: 'mysheet',
407 - cols: [  
408 - {  
409 - caption: '用户id',  
410 - type: 'string'  
411 - },  
412 - {  
413 - caption: '登录时间',  
414 - type: 'string'  
415 - },  
416 - {  
417 - caption: '平台',  
418 - type: 'string'  
419 - },  
420 - {  
421 - caption: '地址',  
422 - type: 'string'  
423 - },  
424 - {  
425 - caption: 'ip',  
426 - type: 'string'  
427 - }  
428 - ], 355 + cols: ['用户id', '登录时间', '平台', '地址', 'ip'],
429 rows: [] 356 rows: []
430 }; 357 };
431 358
@@ -443,11 +370,13 @@ const userController = { @@ -443,11 +370,13 @@ const userController = {
443 conf.rows.push(temp); 370 conf.rows.push(temp);
444 }); 371 });
445 372
446 - let exportFile = excelExport.execute(conf);  
447 -  
448 - res.setHeader('Content-Type', 'application/vnd.openxmlformats');  
449 - res.setHeader('Content-Disposition', 'attachment; filename=user-login-log.xlsx');  
450 - res.end(exportFile, 'binary'); 373 + return excel_export({
  374 + title: conf.cols,
  375 + data: conf.rows,
  376 + fileName: 'user-login-log',
  377 + sheetName: conf.name,
  378 + res
  379 + });
451 }) 380 })
452 .catch(next); 381 .catch(next);
453 } 382 }
@@ -558,36 +558,7 @@ class AdminModel extends global.yoho.BaseModel { @@ -558,36 +558,7 @@ class AdminModel extends global.yoho.BaseModel {
558 558
559 return { 559 return {
560 name: 'sheet', 560 name: 'sheet',
561 - cols: [  
562 - {  
563 - caption: '编号',  
564 - type: 'number'  
565 - },  
566 - {  
567 - caption: 'UID',  
568 - type: 'number'  
569 - },  
570 - {  
571 - caption: '昵称',  
572 - type: 'string'  
573 - },  
574 - {  
575 - caption: '抽奖码',  
576 - type: 'string'  
577 - },  
578 - {  
579 - caption: '是否分享获得',  
580 - type: 'string'  
581 - },  
582 - {  
583 - caption: '邀请者',  
584 - type: 'string'  
585 - },  
586 - {  
587 - caption: '获得时间',  
588 - type: 'string'  
589 - }  
590 - ], 561 + cols: ['编号', 'UID', '昵称', '抽奖码', '是否分享获得', '邀请者', '获得时间'],
591 rows 562 rows
592 }; 563 };
593 }); 564 });
@@ -45,7 +45,6 @@ @@ -45,7 +45,6 @@
45 "cookie-session": "^2.0.0-beta.2", 45 "cookie-session": "^2.0.0-beta.2",
46 "cors": "^2.8.3", 46 "cors": "^2.8.3",
47 "cssnano": "^3.10.0", 47 "cssnano": "^3.10.0",
48 - "excel-export": "^0.5.1",  
49 "express": "^4.15.3", 48 "express": "^4.15.3",
50 "fast-safe-stringify": "^1.2.0", 49 "fast-safe-stringify": "^1.2.0",
51 "feed": "^1.1.0", 50 "feed": "^1.1.0",
@@ -80,6 +79,7 @@ @@ -80,6 +79,7 @@
80 "uuid": "^3.1.0", 79 "uuid": "^3.1.0",
81 "whatwg-fetch": "^2.0.3", 80 "whatwg-fetch": "^2.0.3",
82 "xlsx": "^0.11.16", 81 "xlsx": "^0.11.16",
  82 + "xlsx-style":"^0.8.13",
83 "yoho-md5": "^2.0.0", 83 "yoho-md5": "^2.0.0",
84 "yoho-node-lib": "^0.6.33", 84 "yoho-node-lib": "^0.6.33",
85 "yoho-zookeeper": "^1.0.8" 85 "yoho-zookeeper": "^1.0.8"
  1 +const XLSX = require('xlsx-style');
  2 +const fs = require('fs');
  3 +const os = require('os');
  4 +var exec = require('child_process').exec;
  5 +
  6 +function datenum(v, date1904) {
  7 + if (date1904) {
  8 + v += 1462;
  9 + }
  10 + let epoch = Date.parse(v);
  11 +
  12 + return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
  13 +}
  14 +let Workbook = function() {
  15 + this.SheetNames = [];
  16 + this.Sheets = {};
  17 +};
  18 +
  19 +
  20 +const sheet_arr = (data) => {
  21 + let ws = {};
  22 + let wscols = [];
  23 + let range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
  24 +
  25 + for (let R = 0; R !== data.length; ++R) {
  26 +
  27 + for (let C = 0; C !== data[R].length; ++C) {
  28 + wscols.push({wch: 15});// 单元格宽度
  29 + if (range.s.r > R) {
  30 + range.s.r = R;
  31 + }
  32 + if (range.s.c > C) {
  33 + range.s.c = C;
  34 + }
  35 + if (range.e.r < R) {
  36 + range.e.r = R;
  37 + }
  38 + if (range.e.c < C) {
  39 + range.e.c = C;
  40 + }
  41 + let cell = {v: data[R][C]};
  42 +
  43 + if (cell.v === null) {
  44 + continue;
  45 + }
  46 + let cell_ref = XLSX.utils.encode_cell({c: C, r: R});
  47 +
  48 + /* TEST: proper cell types and value handling */
  49 + if (typeof cell.v === 'number') {
  50 + cell.t = 'n';
  51 + } else if (typeof cell.v === 'boolean') {
  52 + cell.t = 'b';
  53 + } else if (cell.v instanceof Date) {
  54 + cell.t = 'n';
  55 + cell.z = XLSX.SSF._table[14];
  56 + cell.v = datenum(cell.v);
  57 + } else {
  58 + cell.t = 's';
  59 + }
  60 + cell.s = {
  61 + border: {
  62 + left: {
  63 + style: 'thin',
  64 + color: {
  65 + auto: 1
  66 + }
  67 + },
  68 + right: {
  69 + style: 'thin',
  70 + color: {
  71 + auto: 1
  72 + }
  73 + },
  74 + top: {
  75 + style: 'thin',
  76 + color: {
  77 + auto: 1
  78 + }
  79 + },
  80 + bottom: {
  81 + style: 'thin',
  82 + color: {
  83 + auto: 1
  84 + }
  85 + }
  86 + }
  87 + };
  88 + ws[cell_ref] = cell;
  89 + }
  90 + }
  91 +
  92 + if (range.s.c < 10000000) {
  93 + ws['!ref'] = XLSX.utils.encode_range(range);
  94 + }
  95 + ws['!cols'] = wscols;
  96 + return ws;
  97 +};
  98 +
  99 +const excel_export = (obj) =>{
  100 + let arr_name = obj.title;
  101 + let arr = obj.data;
  102 + let ws_name = obj.sheetName;
  103 + let file = os.tmpdir() + '/' + obj.fileName + '.xlsx';
  104 + let wb = new Workbook();
  105 +
  106 + arr.unshift(arr_name);
  107 + wb.SheetNames.push(ws_name);
  108 + wb.Sheets[ws_name] = sheet_arr(arr);
  109 +
  110 + XLSX.writeFile(wb, file);
  111 + let filestream = fs.createReadStream(file);
  112 +
  113 + obj.res.setHeader('Content-Type', 'application/octet-stream');
  114 + obj.res.setHeader('Content-Disposition', 'attachment; filename=' + obj.fileName + '.xlsx');
  115 + filestream.pipe(obj.res);
  116 + exec(['rm', '-rf', file].join(' '));
  117 +};
  118 +
  119 +module.exports = {
  120 + excel_export
  121 +};