2
0

ExporterExcel.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. import { ReactiveCache } from '/imports/reactiveCache';
  2. import { TAPi18n } from '/imports/i18n';
  3. import { createWorkbook } from './createWorkbook';
  4. import {
  5. formatDateTime,
  6. formatDate,
  7. formatTime,
  8. getISOWeek,
  9. isValidDate,
  10. isBefore,
  11. isAfter,
  12. isSame,
  13. add,
  14. subtract,
  15. startOf,
  16. endOf,
  17. format,
  18. parseDate,
  19. now,
  20. createDate,
  21. fromNow,
  22. calendar
  23. } from '/imports/lib/dateUtils';
  24. // exporter maybe is broken since Gridfs introduced, add fs and path
  25. class ExporterExcel {
  26. constructor(boardId, userLanguage) {
  27. this._boardId = boardId;
  28. this.userLanguage = userLanguage;
  29. }
  30. build(res) {
  31. const fs = Npm.require('fs');
  32. const os = Npm.require('os');
  33. const path = Npm.require('path');
  34. const byBoard = {
  35. boardId: this._boardId,
  36. };
  37. const byBoardNoLinked = {
  38. boardId: this._boardId,
  39. linkedId: {
  40. $in: ['', null],
  41. },
  42. };
  43. // we do not want to retrieve boardId in related elements
  44. const noBoardId = {
  45. fields: {
  46. boardId: 0,
  47. },
  48. };
  49. const result = {
  50. _format: 'wekan-board-1.0.0',
  51. };
  52. _.extend(
  53. result,
  54. ReactiveCache.getBoard(this._boardId, {
  55. fields: {
  56. stars: 0,
  57. },
  58. }),
  59. );
  60. result.lists = ReactiveCache.getLists(byBoard, noBoardId);
  61. result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);
  62. result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);
  63. result.customFields = ReactiveCache.getCustomFields(
  64. {
  65. boardIds: {
  66. $in: [this.boardId],
  67. },
  68. },
  69. {
  70. fields: {
  71. boardId: 0,
  72. },
  73. },
  74. );
  75. result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);
  76. result.activities = ReactiveCache.getActivities(byBoard, noBoardId);
  77. result.rules = ReactiveCache.getRules(byBoard, noBoardId);
  78. result.checklists = [];
  79. result.checklistItems = [];
  80. result.subtaskItems = [];
  81. result.triggers = [];
  82. result.actions = [];
  83. result.cards.forEach((card) => {
  84. result.checklists.push(
  85. ...ReactiveCache.getChecklists({
  86. cardId: card._id,
  87. }),
  88. );
  89. result.checklistItems.push(
  90. ...ReactiveCache.getChecklistItems({
  91. cardId: card._id,
  92. }),
  93. );
  94. result.subtaskItems.push(
  95. ...ReactiveCache.getCards({
  96. parentId: card._id,
  97. }),
  98. );
  99. });
  100. result.rules.forEach((rule) => {
  101. result.triggers.push(
  102. ...ReactiveCache.getTriggers(
  103. {
  104. _id: rule.triggerId,
  105. },
  106. noBoardId,
  107. ),
  108. );
  109. result.actions.push(
  110. ...ReactiveCache.getActions(
  111. {
  112. _id: rule.actionId,
  113. },
  114. noBoardId,
  115. ),
  116. );
  117. });
  118. // we also have to export some user data - as the other elements only
  119. // include id but we have to be careful:
  120. // 1- only exports users that are linked somehow to that board
  121. // 2- do not export any sensitive information
  122. const users = {};
  123. result.members.forEach((member) => {
  124. users[member.userId] = true;
  125. });
  126. result.lists.forEach((list) => {
  127. users[list.userId] = true;
  128. });
  129. result.cards.forEach((card) => {
  130. users[card.userId] = true;
  131. if (card.members) {
  132. card.members.forEach((memberId) => {
  133. users[memberId] = true;
  134. });
  135. }
  136. if (card.assignees) {
  137. card.assignees.forEach((memberId) => {
  138. users[memberId] = true;
  139. });
  140. }
  141. });
  142. result.comments.forEach((comment) => {
  143. users[comment.userId] = true;
  144. });
  145. result.activities.forEach((activity) => {
  146. users[activity.userId] = true;
  147. });
  148. result.checklists.forEach((checklist) => {
  149. users[checklist.userId] = true;
  150. });
  151. const byUserIds = {
  152. _id: {
  153. $in: Object.getOwnPropertyNames(users),
  154. },
  155. };
  156. // we use whitelist to be sure we do not expose inadvertently
  157. // some secret fields that gets added to User later.
  158. const userFields = {
  159. fields: {
  160. _id: 1,
  161. username: 1,
  162. 'profile.initials': 1,
  163. 'profile.avatarUrl': 1,
  164. },
  165. };
  166. result.users = ReactiveCache.getUsers(byUserIds, userFields)
  167. .map((user) => {
  168. // user avatar is stored as a relative url, we export absolute
  169. if ((user.profile || {}).avatarUrl) {
  170. user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
  171. }
  172. return user;
  173. });
  174. //init exceljs workbook
  175. const workbook = createWorkbook();
  176. workbook.creator = TAPi18n.__('export-board','',this.userLanguage);
  177. workbook.lastModifiedBy = TAPi18n.__('export-board','',this.userLanguage);
  178. workbook.created = new Date();
  179. workbook.modified = new Date();
  180. workbook.lastPrinted = new Date();
  181. const filename = `${result.title}.xlsx`;
  182. //init worksheet
  183. let worksheetTitle = result.title;
  184. if (worksheetTitle.length > 31) {
  185. // MS Excel doesn't allow worksheet name longer than 31 chars
  186. // Exceljs truncate names to 31 chars
  187. let words = worksheetTitle.split(' ');
  188. let tmpTitle = '';
  189. for (let i=0;i<words.length; i++) {
  190. if (words[0].length > 27) {
  191. // title has no spaces
  192. tmpTitle = words[0].substr(0,27) + ' ';
  193. break;
  194. }
  195. if(tmpTitle.length + words[i].length < 27) {
  196. tmpTitle += words[i] + ' ';
  197. }
  198. else {
  199. break;
  200. }
  201. }
  202. worksheetTitle = tmpTitle + '...';
  203. }
  204. const worksheet = workbook.addWorksheet(worksheetTitle, {
  205. properties: {
  206. tabColor: {
  207. argb: 'FFC0000',
  208. },
  209. },
  210. pageSetup: {
  211. paperSize: 9,
  212. orientation: 'landscape',
  213. },
  214. });
  215. //get worksheet
  216. const ws = workbook.getWorksheet(worksheetTitle);
  217. ws.properties.defaultRowHeight = 20;
  218. //init columns
  219. //Excel font. Western: Arial. zh-CN: 宋体
  220. ws.columns = [
  221. {
  222. key: 'a',
  223. width: 14,
  224. },
  225. {
  226. key: 'b',
  227. width: 40,
  228. },
  229. {
  230. key: 'c',
  231. width: 60,
  232. },
  233. {
  234. key: 'd',
  235. width: 40,
  236. },
  237. {
  238. key: 'e',
  239. width: 20,
  240. },
  241. {
  242. key: 'f',
  243. width: 20,
  244. style: {
  245. font: {
  246. name: TAPi18n.__('excel-font'),
  247. size: '10',
  248. },
  249. numFmt: 'yyyy/mm/dd hh:mm:ss',
  250. },
  251. },
  252. {
  253. key: 'g',
  254. width: 20,
  255. style: {
  256. font: {
  257. name: TAPi18n.__('excel-font'),
  258. size: '10',
  259. },
  260. numFmt: 'yyyy/mm/dd hh:mm:ss',
  261. },
  262. },
  263. {
  264. key: 'h',
  265. width: 20,
  266. style: {
  267. font: {
  268. name: TAPi18n.__('excel-font'),
  269. size: '10',
  270. },
  271. numFmt: 'yyyy/mm/dd hh:mm:ss',
  272. },
  273. },
  274. {
  275. key: 'i',
  276. width: 20,
  277. style: {
  278. font: {
  279. name: TAPi18n.__('excel-font'),
  280. size: '10',
  281. },
  282. numFmt: 'yyyy/mm/dd hh:mm:ss',
  283. },
  284. },
  285. {
  286. key: 'j',
  287. width: 20,
  288. style: {
  289. font: {
  290. name: TAPi18n.__('excel-font'),
  291. size: '10',
  292. },
  293. numFmt: 'yyyy/mm/dd hh:mm:ss',
  294. },
  295. },
  296. {
  297. key: 'k',
  298. width: 20,
  299. style: {
  300. font: {
  301. name: TAPi18n.__('excel-font'),
  302. size: '10',
  303. },
  304. numFmt: 'yyyy/mm/dd hh:mm:ss',
  305. },
  306. },
  307. {
  308. key: 'l',
  309. width: 20,
  310. },
  311. {
  312. key: 'm',
  313. width: 20,
  314. },
  315. {
  316. key: 'n',
  317. width: 20,
  318. },
  319. {
  320. key: 'o',
  321. width: 20,
  322. },
  323. {
  324. key: 'p',
  325. width: 20,
  326. },
  327. {
  328. key: 'q',
  329. width: 20,
  330. },
  331. {
  332. key: 'r',
  333. width: 20,
  334. },
  335. ];
  336. //add title line
  337. ws.mergeCells('A1:H1');
  338. ws.getCell('A1').value = result.title;
  339. ws.getCell('A1').style = {
  340. font: {
  341. name: TAPi18n.__('excel-font'),
  342. size: '20',
  343. },
  344. };
  345. ws.getCell('A1').alignment = {
  346. vertical: 'middle',
  347. horizontal: 'left',
  348. };
  349. ws.getRow(1).height = 40;
  350. //get member and assignee info
  351. let jmem = '';
  352. let jassig = '';
  353. const jmeml = {};
  354. const jassigl = {};
  355. for (const i in result.users) {
  356. jmem = `${jmem + result.users[i].username},`;
  357. jmeml[result.users[i]._id] = result.users[i].username;
  358. }
  359. jmem = jmem.substr(0, jmem.length - 1);
  360. for (const ia in result.users) {
  361. jassig = `${jassig + result.users[ia].username},`;
  362. jassigl[result.users[ia]._id] = result.users[ia].username;
  363. }
  364. jassig = jassig.substr(0, jassig.length - 1);
  365. //get kanban list info
  366. const jlist = {};
  367. for (const klist in result.lists) {
  368. jlist[result.lists[klist]._id] = result.lists[klist].title;
  369. }
  370. //get kanban swimlanes info
  371. const jswimlane = {};
  372. for (const kswimlane in result.swimlanes) {
  373. jswimlane[result.swimlanes[kswimlane]._id] =
  374. result.swimlanes[kswimlane].title;
  375. }
  376. //get kanban label info
  377. const jlabel = {};
  378. var isFirst = 1;
  379. for (const klabel in result.labels) {
  380. // console.log(klabel);
  381. if (isFirst == 0) {
  382. jlabel[result.labels[klabel]._id] = `,${result.labels[klabel].name}`;
  383. } else {
  384. isFirst = 0;
  385. jlabel[result.labels[klabel]._id] = result.labels[klabel].name;
  386. }
  387. }
  388. //add data +8 hours
  389. function addTZhours(jdate) {
  390. if (!jdate) { return ' '; }
  391. const curdate = new Date(jdate);
  392. const checkCorrectDate = new Date(curdate);
  393. if (isValidDate(checkCorrectDate)) {
  394. return curdate;
  395. } else {
  396. return ' ';
  397. }
  398. ////Do not add 8 hours to GMT. Use GMT instead.
  399. ////Could not yet figure out how to get localtime.
  400. //return new Date(curdate.setHours(curdate.getHours() + 8));
  401. //return curdate;
  402. }
  403. //cell center
  404. function cellCenter(cellno) {
  405. ws.getCell(cellno).alignment = {
  406. vertical: 'middle',
  407. horizontal: 'center',
  408. wrapText: true,
  409. };
  410. }
  411. function cellLeft(cellno) {
  412. ws.getCell(cellno).alignment = {
  413. vertical: 'middle',
  414. horizontal: 'left',
  415. wrapText: true,
  416. };
  417. }
  418. // cell Card alignment
  419. function cellCardAlignment(cellno) {
  420. ws.getCell(cellno).alignment = {
  421. vertical: 'top',
  422. horizontal: 'left',
  423. wrapText: true,
  424. };
  425. }
  426. //all border
  427. function allBorder(cellno) {
  428. ws.getCell(cellno).border = {
  429. top: {
  430. style: 'thin',
  431. },
  432. left: {
  433. style: 'thin',
  434. },
  435. bottom: {
  436. style: 'thin',
  437. },
  438. right: {
  439. style: 'thin',
  440. },
  441. };
  442. }
  443. //add blank row
  444. ws.addRow().values = ['', '', '', '', '', ''];
  445. //add board description
  446. ws.addRow().values = [
  447. TAPi18n.__('description','',this.userLanguage),
  448. result.description,
  449. ];
  450. ws.mergeCells('B3:H3');
  451. ws.getRow(3).height = 40;
  452. // In MS Excel, we can't use the AutoFit feature on a column that contains a cell merged with cells in other columns.
  453. // Likewise, we can't use AutoFit on a row that contains a cell merged with cells in other rows.
  454. ws.getRow(3).font = {
  455. name: TAPi18n.__('excel-font'),
  456. size: 10,
  457. };
  458. ws.getCell('A3').style = {
  459. font: {
  460. name: TAPi18n.__('excel-font'),
  461. size: '10',
  462. bold: true,
  463. },
  464. };
  465. ws.getCell(`B3`).alignment = {
  466. wrapText: true,
  467. vertical: 'middle',
  468. };
  469. cellCenter('A3');
  470. //add blank row
  471. ws.addRow().values = ['', '', '', '', '', ''];
  472. //add kanban info
  473. ws.addRow().values = [
  474. TAPi18n.__('createdAt','',this.userLanguage),
  475. addTZhours(result.createdAt),
  476. TAPi18n.__('modifiedAt','',this.userLanguage),
  477. addTZhours(result.modifiedAt),
  478. TAPi18n.__('members','',this.userLanguage),
  479. jmem,
  480. ];
  481. ws.getRow(5).font = {
  482. name: TAPi18n.__('excel-font'),
  483. size: 10,
  484. bold: true,
  485. };
  486. ws.mergeCells('F5:R5');
  487. ws.getCell('B5').style = {
  488. font: {
  489. name: TAPi18n.__('excel-font'),
  490. size: '10',
  491. bold: true,
  492. },
  493. numFmt: 'yyyy/mm/dd hh:mm:ss',
  494. };
  495. ws.getCell('D5').style = {
  496. font: {
  497. name: TAPi18n.__('excel-font'),
  498. size: '10',
  499. bold: true,
  500. },
  501. numFmt: 'yyyy/mm/dd hh:mm:ss',
  502. };
  503. cellCenter('A5');
  504. cellCenter('B5');
  505. cellCenter('C5');
  506. cellCenter('D5');
  507. cellCenter('E5');
  508. cellLeft('F5');
  509. ws.getRow(5).height = 20;
  510. allBorder('A5');
  511. allBorder('B5');
  512. allBorder('C5');
  513. allBorder('D5');
  514. allBorder('E5');
  515. allBorder('F5');
  516. //add blank row
  517. ws.addRow().values = [
  518. '',
  519. '',
  520. '',
  521. '',
  522. '',
  523. '',
  524. '',
  525. '',
  526. '',
  527. '',
  528. '',
  529. '',
  530. '',
  531. '',
  532. '',
  533. ];
  534. //add card title
  535. //ws.addRow().values = ['编号', '标题', '创建人', '创建时间', '更新时间', '列表', '成员', '描述', '标签'];
  536. //this is where order in which the excel file generates
  537. ws.addRow().values = [
  538. TAPi18n.__('number','',this.userLanguage),
  539. TAPi18n.__('title','',this.userLanguage),
  540. TAPi18n.__('description','',this.userLanguage),
  541. TAPi18n.__('parent-card','',this.userLanguage),
  542. TAPi18n.__('owner','',this.userLanguage),
  543. TAPi18n.__('createdAt','',this.userLanguage),
  544. TAPi18n.__('last-modified-at','',this.userLanguage),
  545. TAPi18n.__('card-received','',this.userLanguage),
  546. TAPi18n.__('card-start','',this.userLanguage),
  547. TAPi18n.__('card-due','',this.userLanguage),
  548. TAPi18n.__('card-end','',this.userLanguage),
  549. TAPi18n.__('list','',this.userLanguage),
  550. TAPi18n.__('swimlane','',this.userLanguage),
  551. TAPi18n.__('assignee','',this.userLanguage),
  552. TAPi18n.__('members','',this.userLanguage),
  553. TAPi18n.__('labels','',this.userLanguage),
  554. TAPi18n.__('overtime-hours','',this.userLanguage),
  555. TAPi18n.__('spent-time-hours','',this.userLanguage),
  556. ];
  557. ws.getRow(7).height = 20;
  558. allBorder('A7');
  559. allBorder('B7');
  560. allBorder('C7');
  561. allBorder('D7');
  562. allBorder('E7');
  563. allBorder('F7');
  564. allBorder('G7');
  565. allBorder('H7');
  566. allBorder('I7');
  567. allBorder('J7');
  568. allBorder('K7');
  569. allBorder('L7');
  570. allBorder('M7');
  571. allBorder('N7');
  572. allBorder('O7');
  573. allBorder('P7');
  574. allBorder('Q7');
  575. allBorder('R7');
  576. cellCenter('A7');
  577. cellCenter('B7');
  578. cellCenter('C7');
  579. cellCenter('D7');
  580. cellCenter('E7');
  581. cellCenter('F7');
  582. cellCenter('G7');
  583. cellCenter('H7');
  584. cellCenter('I7');
  585. cellCenter('J7');
  586. cellCenter('K7');
  587. cellCenter('L7');
  588. cellCenter('M7');
  589. cellCenter('N7');
  590. cellCenter('O7');
  591. cellCenter('P7');
  592. cellCenter('Q7');
  593. cellCenter('R7');
  594. ws.getRow(7).font = {
  595. name: TAPi18n.__('excel-font'),
  596. size: 10,
  597. bold: true,
  598. };
  599. //add blank row
  600. //add card info
  601. for (const i in result.cards) {
  602. const jcard = result.cards[i];
  603. //get member info
  604. let jcmem = '';
  605. for (const j in jcard.members) {
  606. jcmem += jmeml[jcard.members[j]];
  607. jcmem += ' ';
  608. }
  609. //get assignee info
  610. let jcassig = '';
  611. for (const ja in jcard.assignees) {
  612. jcassig += jassigl[jcard.assignees[ja]];
  613. jcassig += ' ';
  614. }
  615. //get card label info
  616. let jclabel = '';
  617. for (const jl in jcard.labelIds) {
  618. jclabel += jlabel[jcard.labelIds[jl]];
  619. jclabel += ' ';
  620. }
  621. //get parent name
  622. if (jcard.parentId) {
  623. const parentCard = result.cards.find(
  624. (card) => card._id === jcard.parentId,
  625. );
  626. jcard.parentCardTitle = parentCard ? parentCard.title : '';
  627. }
  628. //add card detail
  629. const t = Number(i) + 1;
  630. ws.addRow().values = [
  631. t.toString(),
  632. jcard.title,
  633. jcard.description,
  634. jcard.parentCardTitle,
  635. jmeml[jcard.userId],
  636. addTZhours(jcard.createdAt),
  637. addTZhours(jcard.dateLastActivity),
  638. addTZhours(jcard.receivedAt),
  639. addTZhours(jcard.startAt),
  640. addTZhours(jcard.dueAt),
  641. addTZhours(jcard.endAt),
  642. jlist[jcard.listId],
  643. jswimlane[jcard.swimlaneId],
  644. jcassig,
  645. jcmem,
  646. jclabel,
  647. jcard.isOvertime ? 'true' : 'false',
  648. jcard.spentTime,
  649. ];
  650. const y = Number(i) + 8;
  651. //ws.getRow(y).height = 25;
  652. ws.getRow(y).font = {
  653. name: TAPi18n.__('excel-font'),
  654. size: 10,
  655. };
  656. // Border
  657. allBorder(`A${y}`);
  658. allBorder(`B${y}`);
  659. allBorder(`C${y}`);
  660. allBorder(`D${y}`);
  661. allBorder(`E${y}`);
  662. allBorder(`F${y}`);
  663. allBorder(`G${y}`);
  664. allBorder(`H${y}`);
  665. allBorder(`I${y}`);
  666. allBorder(`J${y}`);
  667. allBorder(`K${y}`);
  668. allBorder(`L${y}`);
  669. allBorder(`M${y}`);
  670. allBorder(`N${y}`);
  671. allBorder(`O${y}`);
  672. allBorder(`P${y}`);
  673. allBorder(`Q${y}`);
  674. allBorder(`R${y}`);
  675. // Alignment
  676. ws.getCell(`A${y}`).alignment = {
  677. vertical: 'top',
  678. horizontal: 'right',
  679. wrapText: true,
  680. };
  681. cellCardAlignment(`B${y}`);
  682. cellCardAlignment(`C${y}`);
  683. cellCardAlignment(`D${y}`);
  684. cellCardAlignment(`E${y}`);
  685. cellCardAlignment(`F${y}`);
  686. cellCardAlignment(`G${y}`);
  687. cellCardAlignment(`H${y}`);
  688. cellCardAlignment(`I${y}`);
  689. cellCardAlignment(`J${y}`);
  690. cellCardAlignment(`K${y}`);
  691. cellCardAlignment(`L${y}`);
  692. cellCardAlignment(`M${y}`);
  693. cellCardAlignment(`N${y}`);
  694. cellCardAlignment(`O${y}`);
  695. cellCardAlignment(`P${y}`);
  696. ws.getCell(`Q${y}`).alignment = {
  697. vertical: 'top',
  698. horizontal: 'center',
  699. wrapText: true,
  700. };
  701. ws.getCell(`R${y}`).alignment = {
  702. vertical: 'top',
  703. horizontal: 'center',
  704. wrapText: true,
  705. };
  706. }
  707. //Activities worksheet
  708. //init worksheet
  709. const worksheet2 = workbook.addWorksheet(TAPi18n.__('activity','',this.userLanguage), {
  710. properties: {
  711. tabColor: {
  712. argb: 'FFC0000',
  713. },
  714. },
  715. pageSetup: {
  716. paperSize: 9,
  717. orientation: 'landscape',
  718. },
  719. });
  720. //get worksheet
  721. const ws2 = workbook.getWorksheet(TAPi18n.__('activity','',this.userLanguage));
  722. ws2.properties.defaultRowHeight = 20;
  723. //init columns
  724. ws2.columns = [
  725. {
  726. key: 'a',
  727. width: 14,
  728. },
  729. {
  730. key: 'b',
  731. width: 60,
  732. },
  733. {
  734. key: 'c',
  735. width: 40,
  736. },
  737. {
  738. key: 'd',
  739. width: 40,
  740. },
  741. {
  742. key: 'e',
  743. width: 30,
  744. style: {
  745. font: {
  746. name: TAPi18n.__('excel-font'),
  747. size: '10',
  748. },
  749. numFmt: 'yyyy/mm/dd hh:mm:ss',
  750. },
  751. },
  752. {
  753. key: 'f',
  754. width: 30,
  755. style: {
  756. font: {
  757. name: TAPi18n.__('excel-font'),
  758. size: '10',
  759. },
  760. numFmt: 'yyyy/mm/dd hh:mm:ss',
  761. },
  762. },
  763. ];
  764. // cell Card alignment
  765. function cellCardAlignmentWs2(cellno) {
  766. ws2.getCell(cellno).alignment = {
  767. vertical: 'top',
  768. horizontal: 'left',
  769. wrapText: true,
  770. };
  771. }
  772. //all border
  773. function allBorderWs2(cellno) {
  774. ws2.getCell(cellno).border = {
  775. top: {
  776. style: 'thin',
  777. },
  778. left: {
  779. style: 'thin',
  780. },
  781. bottom: {
  782. style: 'thin',
  783. },
  784. right: {
  785. style: 'thin',
  786. },
  787. };
  788. }
  789. //add title line
  790. ws2.mergeCells('A1:F1');
  791. ws2.getCell('A1').value = result.title;
  792. ws2.getCell('A1').style = {
  793. font: {
  794. name: TAPi18n.__('excel-font'),
  795. size: '20',
  796. },
  797. };
  798. ws2.getCell('A1').alignment = {
  799. vertical: 'middle',
  800. horizontal: 'center',
  801. wrapText: true,
  802. };
  803. ws2.getRow(1).height = 40;
  804. //add blank row
  805. ws2.addRow().values = ['', '', '', '', '', ''];
  806. //add comment title
  807. ws2.addRow().values = [
  808. TAPi18n.__('number','',this.userLanguage),
  809. TAPi18n.__('activity','',this.userLanguage),
  810. TAPi18n.__('card','',this.userLanguage),
  811. TAPi18n.__('owner','',this.userLanguage),
  812. TAPi18n.__('createdAt','',this.userLanguage),
  813. TAPi18n.__('last-modified-at','',this.userLanguage),
  814. ];
  815. ws2.getRow(3).height = 20;
  816. ws2.getRow(3).font = {
  817. name: TAPi18n.__('excel-font'),
  818. size: 10,
  819. bold: true,
  820. };
  821. ws2.getRow(3).alignment = {
  822. vertical: 'middle',
  823. horizontal: 'center',
  824. wrapText: true,
  825. };
  826. allBorderWs2('A3');
  827. allBorderWs2('B3');
  828. allBorderWs2('C3');
  829. allBorderWs2('D3');
  830. allBorderWs2('E3');
  831. allBorderWs2('F3');
  832. //add comment info
  833. let commentcnt = 0;
  834. for (const i in result.comments) {
  835. const jcomment = result.comments[i];
  836. //card title
  837. const parentCard = result.cards.find(
  838. (card) => card._id === jcomment.cardId,
  839. );
  840. jcomment.cardTitle = parentCard ? parentCard.title : '';
  841. if (jcomment.cardTitle == '') {
  842. continue;
  843. }
  844. //add comment detail
  845. commentcnt++;
  846. ws2.addRow().values = [
  847. commentcnt.toString(),
  848. jcomment.text,
  849. jcomment.cardTitle,
  850. jmeml[jcomment.userId],
  851. addTZhours(jcomment.createdAt),
  852. addTZhours(jcomment.modifiedAt),
  853. ];
  854. const y = commentcnt + 3;
  855. ws2.getRow(y).font = {
  856. name: TAPi18n.__('excel-font'),
  857. size: 10,
  858. };
  859. // Border
  860. allBorderWs2(`A${y}`);
  861. allBorderWs2(`B${y}`);
  862. allBorderWs2(`C${y}`);
  863. allBorderWs2(`D${y}`);
  864. allBorderWs2(`E${y}`);
  865. allBorderWs2(`F${y}`);
  866. // Alignment
  867. ws2.getCell(`A${y}`).alignment = {
  868. vertical: 'top',
  869. horizontal: 'right',
  870. wrapText: true,
  871. };
  872. cellCardAlignmentWs2(`B${y}`);
  873. cellCardAlignmentWs2(`C${y}`);
  874. cellCardAlignmentWs2(`D${y}`);
  875. cellCardAlignmentWs2(`E${y}`);
  876. cellCardAlignmentWs2(`F${y}`);
  877. }
  878. workbook.xlsx.write(res).then(function () {});
  879. }
  880. canExport(user) {
  881. const board = ReactiveCache.getBoard(this._boardId);
  882. return board && board.isVisibleBy(user);
  883. }
  884. }
  885. export { ExporterExcel };