mixin.js 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598
  1. import XEUtils from 'xe-utils';
  2. import { VxeUI } from '../../../ui';
  3. import { isColumnInfo, getCellValue, createHandleGetRowId } from '../../src/util';
  4. import { parseFile, formatText, eqEmptyValue } from '../../../ui/src/utils';
  5. import { hasClass } from '../../../ui/src/dom';
  6. import { createHtmlPage, getExportBlobByContent } from './util';
  7. import { warnLog, errLog } from '../../../ui/src/log';
  8. const { getI18n, renderer } = VxeUI;
  9. let htmlCellElem;
  10. const csvBOM = '\ufeff';
  11. const enterSymbol = '\r\n';
  12. function hasTreeChildren($xeTable, row) {
  13. const treeOpts = $xeTable.computeTreeOpts;
  14. const childrenField = treeOpts.children || treeOpts.childrenField;
  15. return row[childrenField] && row[childrenField].length;
  16. }
  17. function getSeq($xeTable, cellValue, row, $rowIndex, column, $columnIndex) {
  18. const seqOpts = $xeTable.computeSeqOpts;
  19. const seqMethod = seqOpts.seqMethod || column.seqMethod;
  20. if (seqMethod) {
  21. return seqMethod({
  22. $table: $xeTable,
  23. row,
  24. rowIndex: $xeTable.getRowIndex(row),
  25. $rowIndex,
  26. column,
  27. columnIndex: $xeTable.getColumnIndex(column),
  28. $columnIndex
  29. });
  30. }
  31. return cellValue;
  32. }
  33. function defaultFilterExportColumn(column) {
  34. return !!column.field || ['seq', 'checkbox', 'radio'].indexOf(column.type || '') === -1;
  35. }
  36. function toTableBorder(border) {
  37. if (border === true) {
  38. return 'full';
  39. }
  40. if (border) {
  41. return border;
  42. }
  43. return 'default';
  44. }
  45. function toBooleanValue(cellValue) {
  46. return XEUtils.isBoolean(cellValue) ? (cellValue ? 'TRUE' : 'FALSE') : cellValue;
  47. }
  48. const toStringValue = (cellValue) => {
  49. return eqEmptyValue(cellValue) ? '' : `${cellValue}`;
  50. };
  51. function getBodyLabelData($xeTable, opts, columns, datas) {
  52. const props = $xeTable;
  53. const { isAllExpand, mode } = opts;
  54. const { treeConfig } = props;
  55. const radioOpts = $xeTable.computeRadioOpts;
  56. const checkboxOpts = $xeTable.computeCheckboxOpts;
  57. const treeOpts = $xeTable.computeTreeOpts;
  58. const columnOpts = $xeTable.computeColumnOpts;
  59. if (!htmlCellElem) {
  60. htmlCellElem = document.createElement('div');
  61. }
  62. if (treeConfig) {
  63. const childrenField = treeOpts.children || treeOpts.childrenField;
  64. // 如果是树表格只允许导出数据源
  65. const rest = [];
  66. const expandMaps = {};
  67. const useMaps = {};
  68. const { handleGetRowId } = createHandleGetRowId($xeTable);
  69. XEUtils.eachTree(datas, (item, $rowIndex, items, path, parent, nodes) => {
  70. const row = item._row || item;
  71. const rowid = handleGetRowId(row);
  72. if (useMaps[rowid]) {
  73. return;
  74. }
  75. const parentRow = parent && parent._row ? parent._row : parent;
  76. const pRowid = parentRow ? handleGetRowId(parentRow) : '';
  77. if ((isAllExpand || !parentRow || (expandMaps[pRowid] && $xeTable.isTreeExpandByRow(parentRow)))) {
  78. const hasRowChild = hasTreeChildren($xeTable, row);
  79. const item = {
  80. _row: row,
  81. _level: nodes.length - 1,
  82. _hasChild: hasRowChild,
  83. _expand: hasRowChild && $xeTable.isTreeExpandByRow(row)
  84. };
  85. columns.forEach((column, $columnIndex) => {
  86. let cellValue = '';
  87. const renderOpts = column.editRender || column.cellRender;
  88. let bodyExportMethod = column.exportMethod || columnOpts.exportMethod;
  89. if (!bodyExportMethod && renderOpts && renderOpts.name) {
  90. const compConf = renderer.get(renderOpts.name);
  91. if (compConf) {
  92. bodyExportMethod = compConf.tableExportMethod || compConf.exportMethod;
  93. }
  94. }
  95. if (!bodyExportMethod) {
  96. bodyExportMethod = columnOpts.exportMethod;
  97. }
  98. if (bodyExportMethod) {
  99. cellValue = bodyExportMethod({ $table: $xeTable, row, column, options: opts });
  100. }
  101. else {
  102. switch (column.type) {
  103. case 'seq': {
  104. const seqVal = path.map((num, i) => i % 2 === 0 ? (Number(num) + 1) : '.').join('');
  105. cellValue = mode === 'all' ? seqVal : getSeq($xeTable, seqVal, row, $rowIndex, column, $columnIndex);
  106. break;
  107. }
  108. case 'checkbox':
  109. cellValue = toBooleanValue($xeTable.isCheckedByCheckboxRow(row));
  110. item._checkboxLabel = checkboxOpts.labelField ? XEUtils.get(row, checkboxOpts.labelField) : '';
  111. item._checkboxDisabled = checkboxOpts.checkMethod && !checkboxOpts.checkMethod({ $table: $xeTable, row });
  112. break;
  113. case 'radio':
  114. cellValue = toBooleanValue($xeTable.isCheckedByRadioRow(row));
  115. item._radioLabel = radioOpts.labelField ? XEUtils.get(row, radioOpts.labelField) : '';
  116. item._radioDisabled = radioOpts.checkMethod && !radioOpts.checkMethod({ $table: $xeTable, row });
  117. break;
  118. default:
  119. if (opts.original) {
  120. cellValue = getCellValue(row, column);
  121. }
  122. else {
  123. cellValue = $xeTable.getCellLabel(row, column);
  124. if (column.type === 'html') {
  125. htmlCellElem.innerHTML = cellValue;
  126. cellValue = htmlCellElem.innerText.trim();
  127. }
  128. else {
  129. const cell = $xeTable.getCellElement(row, column);
  130. if (cell && !hasClass(cell, 'is--progress')) {
  131. cellValue = cell.innerText.trim();
  132. }
  133. }
  134. }
  135. }
  136. }
  137. item[column.id] = toStringValue(cellValue);
  138. });
  139. useMaps[rowid] = true;
  140. if (pRowid) {
  141. expandMaps[pRowid] = true;
  142. }
  143. rest.push(Object.assign(item, row));
  144. }
  145. }, { children: childrenField });
  146. return rest;
  147. }
  148. return datas.map((row, $rowIndex) => {
  149. const item = {
  150. _row: row
  151. };
  152. columns.forEach((column, $columnIndex) => {
  153. let cellValue = '';
  154. const renderOpts = column.editRender || column.cellRender;
  155. let bodyExportMethod = column.exportMethod || columnOpts.exportMethod;
  156. if (!bodyExportMethod && renderOpts && renderOpts.name) {
  157. const compConf = renderer.get(renderOpts.name);
  158. if (compConf) {
  159. bodyExportMethod = compConf.tableExportMethod || compConf.exportMethod;
  160. }
  161. }
  162. if (bodyExportMethod) {
  163. cellValue = bodyExportMethod({ $table: $xeTable, row, column, options: opts });
  164. }
  165. else {
  166. switch (column.type) {
  167. case 'seq': {
  168. const seqValue = $rowIndex + 1;
  169. cellValue = mode === 'all' ? seqValue : getSeq($xeTable, seqValue, row, $rowIndex, column, $columnIndex);
  170. break;
  171. }
  172. case 'checkbox':
  173. cellValue = toBooleanValue($xeTable.isCheckedByCheckboxRow(row));
  174. item._checkboxLabel = checkboxOpts.labelField ? XEUtils.get(row, checkboxOpts.labelField) : '';
  175. item._checkboxDisabled = checkboxOpts.checkMethod && !checkboxOpts.checkMethod({ $table: $xeTable, row });
  176. break;
  177. case 'radio':
  178. cellValue = toBooleanValue($xeTable.isCheckedByRadioRow(row));
  179. item._radioLabel = radioOpts.labelField ? XEUtils.get(row, radioOpts.labelField) : '';
  180. item._radioDisabled = radioOpts.checkMethod && !radioOpts.checkMethod({ $table: $xeTable, row });
  181. break;
  182. default:
  183. if (opts.original) {
  184. cellValue = getCellValue(row, column);
  185. }
  186. else {
  187. cellValue = $xeTable.getCellLabel(row, column);
  188. if (column.type === 'html') {
  189. htmlCellElem.innerHTML = cellValue;
  190. cellValue = htmlCellElem.innerText.trim();
  191. }
  192. else {
  193. const cell = $xeTable.getCellElement(row, column);
  194. if (cell && !hasClass(cell, 'is--progress')) {
  195. cellValue = cell.innerText.trim();
  196. }
  197. }
  198. }
  199. }
  200. }
  201. item[column.id] = toStringValue(cellValue);
  202. });
  203. return item;
  204. });
  205. }
  206. function getExportData($xeTable, opts) {
  207. const $xeGrid = $xeTable.$xeGrid;
  208. const $xeGantt = $xeTable.$xeGantt;
  209. const { columns, dataFilterMethod } = opts;
  210. let datas = opts.data;
  211. if (dataFilterMethod) {
  212. datas = datas.filter((row, index) => dataFilterMethod({ $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, row, $rowIndex: index }));
  213. }
  214. return getBodyLabelData($xeTable, opts, columns, datas);
  215. }
  216. function getBooleanValue(cellValue) {
  217. return cellValue === 'TRUE' || cellValue === 'true' || cellValue === true;
  218. }
  219. function getHeaderTitle($xeTable, opts, column) {
  220. const columnOpts = $xeTable.computeColumnOpts;
  221. const headExportMethod = column.headerExportMethod || columnOpts.headerExportMethod;
  222. return headExportMethod ? headExportMethod({ column, options: opts, $table: $xeTable }) : ((opts.isTitle ? column.getTitle() : column.field) || '');
  223. }
  224. function getFooterCellValue($xeTable, opts, row, column) {
  225. const columnOpts = $xeTable.computeColumnOpts;
  226. const renderOpts = column.editRender || column.cellRender;
  227. let footLabelMethod = column.footerExportMethod;
  228. if (!footLabelMethod && renderOpts && renderOpts.name) {
  229. const compConf = renderer.get(renderOpts.name);
  230. if (compConf) {
  231. footLabelMethod = compConf.tableFooterExportMethod || compConf.footerExportMethod || compConf.footerCellExportMethod;
  232. }
  233. }
  234. if (!footLabelMethod) {
  235. footLabelMethod = columnOpts.footerExportMethod;
  236. }
  237. const _columnIndex = $xeTable.getVTColumnIndex(column);
  238. if (footLabelMethod) {
  239. return footLabelMethod({ $table: $xeTable, items: row, itemIndex: _columnIndex, row, _columnIndex, column, options: opts });
  240. }
  241. // 兼容老模式
  242. if (XEUtils.isArray(row)) {
  243. return XEUtils.toValueString(row[_columnIndex]);
  244. }
  245. return XEUtils.get(row, column.field);
  246. }
  247. function getFooterData($xeTable, opts, footerTableData) {
  248. const $xeGrid = $xeTable.$xeGrid;
  249. const $xeGantt = $xeTable.$xeGantt;
  250. const { footerFilterMethod } = opts;
  251. return footerFilterMethod ? footerTableData.filter((items, index) => footerFilterMethod({ $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, items, $rowIndex: index })) : footerTableData;
  252. }
  253. function getCsvCellTypeLabel(column, cellValue) {
  254. if (cellValue) {
  255. if (column.type === 'seq') {
  256. return `\t${cellValue}`;
  257. }
  258. switch (column.cellType) {
  259. case 'string':
  260. if (!isNaN(cellValue)) {
  261. return `\t${cellValue}`;
  262. }
  263. break;
  264. case 'number':
  265. break;
  266. default:
  267. if (cellValue.length >= 12 && !isNaN(cellValue)) {
  268. return `\t${cellValue}`;
  269. }
  270. break;
  271. }
  272. }
  273. return cellValue;
  274. }
  275. function toTxtCellLabel(val) {
  276. if (/[",\s\n]/.test(val)) {
  277. return `"${val.replace(/"/g, '""')}"`;
  278. }
  279. return val;
  280. }
  281. function toCsv($xeTable, opts, columns, datas) {
  282. const reactData = $xeTable;
  283. let content = csvBOM;
  284. if (opts.isHeader) {
  285. content += columns.map(column => toTxtCellLabel(getHeaderTitle($xeTable, opts, column))).join(',') + enterSymbol;
  286. }
  287. datas.forEach(row => {
  288. content += columns.map(column => toTxtCellLabel(getCsvCellTypeLabel(column, row[column.id]))).join(',') + enterSymbol;
  289. });
  290. if (opts.isFooter) {
  291. const { footerTableData } = reactData;
  292. const footers = getFooterData($xeTable, opts, footerTableData);
  293. footers.forEach((row) => {
  294. content += columns.map(column => toTxtCellLabel(getFooterCellValue($xeTable, opts, row, column))).join(',') + enterSymbol;
  295. });
  296. }
  297. return content;
  298. }
  299. function toTxt($xeTable, opts, columns, datas) {
  300. const reactData = $xeTable;
  301. let content = '';
  302. if (opts.isHeader) {
  303. content += columns.map(column => toTxtCellLabel(getHeaderTitle($xeTable, opts, column))).join('\t') + enterSymbol;
  304. }
  305. datas.forEach(row => {
  306. content += columns.map(column => toTxtCellLabel(row[column.id])).join('\t') + enterSymbol;
  307. });
  308. if (opts.isFooter) {
  309. const { footerTableData } = reactData;
  310. const footers = getFooterData($xeTable, opts, footerTableData);
  311. footers.forEach((row) => {
  312. content += columns.map(column => toTxtCellLabel(getFooterCellValue($xeTable, opts, row, column))).join('\t') + enterSymbol;
  313. });
  314. }
  315. return content;
  316. }
  317. function hasEllipsis($xeTable, column, property, allColumnOverflow) {
  318. const reactData = $xeTable;
  319. const columnOverflow = column[property];
  320. const headOverflow = XEUtils.isUndefined(columnOverflow) || XEUtils.isNull(columnOverflow) ? allColumnOverflow : columnOverflow;
  321. const showEllipsis = headOverflow === 'ellipsis';
  322. const showTitle = headOverflow === 'title';
  323. const showTooltip = headOverflow === true || headOverflow === 'tooltip';
  324. let isEllipsis = showTitle || showTooltip || showEllipsis;
  325. // 虚拟滚动不支持动态高度
  326. const { scrollXLoad, scrollYLoad } = reactData;
  327. if ((scrollXLoad || scrollYLoad) && !isEllipsis) {
  328. isEllipsis = true;
  329. }
  330. return isEllipsis;
  331. }
  332. function toHtml($xeTable, opts, columns, datas) {
  333. const props = $xeTable;
  334. const reactData = $xeTable;
  335. const internalData = $xeTable;
  336. const { id, border, treeConfig, headerAlign: allHeaderAlign, align: allAlign, footerAlign: allFooterAlign, showOverflow: allColumnOverflow, showHeaderOverflow: allColumnHeaderOverflow } = props;
  337. const { isAllSelected, isIndeterminate } = reactData;
  338. const { mergeBodyCellMaps } = internalData;
  339. const treeOpts = $xeTable.computeTreeOpts;
  340. const { print: isPrint, isHeader, isFooter, isColgroup, isMerge, colgroups, original } = opts;
  341. const allCls = 'check-all';
  342. const clss = [
  343. 'vxe-table',
  344. `border--${toTableBorder(border)}`,
  345. isPrint ? 'is--print' : '',
  346. isHeader ? 'is--header' : ''
  347. ].filter(cls => cls);
  348. const tables = [
  349. `<table class="${clss.join(' ')}" border="0" cellspacing="0" cellpadding="0">`,
  350. `<colgroup>${columns.map(column => `<col style="width:${column.renderWidth}px">`).join('')}</colgroup>`
  351. ];
  352. if (isHeader) {
  353. tables.push('<thead>');
  354. if (isColgroup && !original) {
  355. colgroups.forEach((cols) => {
  356. tables.push(`<tr>${cols.map(column => {
  357. const headAlign = column.headerAlign || column.align || allHeaderAlign || allAlign;
  358. const classNames = hasEllipsis($xeTable, column, 'showHeaderOverflow', allColumnHeaderOverflow) ? ['col--ellipsis'] : [];
  359. const cellTitle = getHeaderTitle($xeTable, opts, column);
  360. let childWidth = 0;
  361. let countChild = 0;
  362. XEUtils.eachTree([column], item => {
  363. if (!item.childNodes || !column.childNodes.length) {
  364. countChild++;
  365. }
  366. childWidth += item.renderWidth;
  367. }, { children: 'childNodes' });
  368. const cellWidth = childWidth - countChild;
  369. if (headAlign) {
  370. classNames.push(`col--${headAlign}`);
  371. }
  372. if (column.type === 'checkbox') {
  373. return `<th class="${classNames.join(' ')}" colspan="${column._colSpan}" rowspan="${column._rowSpan}"><div ${isPrint ? '' : `style="width: ${cellWidth}px"`}><input type="checkbox" class="${allCls}" ${isAllSelected ? 'checked' : ''}><span>${cellTitle}</span></div></th>`;
  374. }
  375. return `<th class="${classNames.join(' ')}" colspan="${column._colSpan}" rowspan="${column._rowSpan}" title="${cellTitle}"><div ${isPrint ? '' : `style="width: ${cellWidth}px"`}><span>${formatText(cellTitle, true)}</span></div></th>`;
  376. }).join('')}</tr>`);
  377. });
  378. }
  379. else {
  380. tables.push(`<tr>${columns.map(column => {
  381. const headAlign = column.headerAlign || column.align || allHeaderAlign || allAlign;
  382. const classNames = hasEllipsis($xeTable, column, 'showHeaderOverflow', allColumnHeaderOverflow) ? ['col--ellipsis'] : [];
  383. const cellTitle = getHeaderTitle($xeTable, opts, column);
  384. if (headAlign) {
  385. classNames.push(`col--${headAlign}`);
  386. }
  387. if (column.type === 'checkbox') {
  388. return `<th class="${classNames.join(' ')}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><input type="checkbox" class="${allCls}" ${isAllSelected ? 'checked' : ''}><span>${cellTitle}</span></div></th>`;
  389. }
  390. return `<th class="${classNames.join(' ')}" title="${cellTitle}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><span>${formatText(cellTitle, true)}</span></div></th>`;
  391. }).join('')}</tr>`);
  392. }
  393. tables.push('</thead>');
  394. }
  395. if (datas.length) {
  396. tables.push('<tbody>');
  397. if (treeConfig) {
  398. datas.forEach(item => {
  399. tables.push('<tr>' + columns.map(column => {
  400. const cellAlign = column.align || allAlign;
  401. const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [];
  402. const cellValue = item[column.id];
  403. if (cellAlign) {
  404. classNames.push(`col--${cellAlign}`);
  405. }
  406. if (column.treeNode) {
  407. let treeIcon = '';
  408. if (item._hasChild) {
  409. treeIcon = `<i class="${item._expand ? 'vxe-table--tree-fold-icon' : 'vxe-table--tree-unfold-icon'}"></i>`;
  410. }
  411. classNames.push('vxe-table--tree-node');
  412. if (column.type === 'radio') {
  413. return `<td class="${classNames.join(' ')}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><div class="vxe-table--tree-node-wrapper" style="padding-left: ${item._level * treeOpts.indent}px"><div class="vxe-table--tree-icon-wrapper">${treeIcon}</div><div class="vxe-table--tree-cell"><input type="radio" name="radio_${id}" ${item._radioDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._radioLabel}</span></div></div></div></td>`;
  414. }
  415. else if (column.type === 'checkbox') {
  416. return `<td class="${classNames.join(' ')}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><div class="vxe-table--tree-node-wrapper" style="padding-left: ${item._level * treeOpts.indent}px"><div class="vxe-table--tree-icon-wrapper">${treeIcon}</div><div class="vxe-table--tree-cell"><input type="checkbox" ${item._checkboxDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._checkboxLabel}</span></div></div></div></td>`;
  417. }
  418. return `<td class="${classNames.join(' ')}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><div class="vxe-table--tree-node-wrapper" style="padding-left: ${item._level * treeOpts.indent}px"><div class="vxe-table--tree-icon-wrapper">${treeIcon}</div><div class="vxe-table--tree-cell">${cellValue}</div></div></div></td>`;
  419. }
  420. if (column.type === 'radio') {
  421. return `<td class="${classNames.join(' ')}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><input type="radio" name="radio_${id}" ${item._radioDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._radioLabel}</span></div></td>`;
  422. }
  423. else if (column.type === 'checkbox') {
  424. return `<td class="${classNames.join(' ')}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><input type="checkbox" ${item._checkboxDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._checkboxLabel}</span></div></td>`;
  425. }
  426. return `<td class="${classNames.join(' ')}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}>${formatText(cellValue, true)}</div></td>`;
  427. }).join('') + '</tr>');
  428. });
  429. }
  430. else {
  431. datas.forEach(item => {
  432. tables.push('<tr>' + columns.map(column => {
  433. const colid = column.id;
  434. const cellAlign = column.align || allAlign;
  435. const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [];
  436. const cellValue = item[colid];
  437. let rowSpan = 1;
  438. let colSpan = 1;
  439. if (isMerge) {
  440. const _rowIndex = $xeTable.getVTRowIndex(item._row);
  441. const _columnIndex = $xeTable.getVTColumnIndex(column);
  442. const spanRest = mergeBodyCellMaps[`${_rowIndex}:${_columnIndex}`];
  443. if (spanRest) {
  444. const { rowspan, colspan } = spanRest;
  445. if (!rowspan || !colspan) {
  446. return '';
  447. }
  448. if (rowspan > 1) {
  449. rowSpan = rowspan;
  450. }
  451. if (colspan > 1) {
  452. colSpan = colspan;
  453. }
  454. }
  455. }
  456. if (cellAlign) {
  457. classNames.push(`col--${cellAlign}`);
  458. }
  459. if (column.type === 'radio') {
  460. return `<td class="${classNames.join(' ')}" rowspan="${rowSpan}" colspan="${colSpan}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><input type="radio" name="radio_${id}" ${item._radioDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._radioLabel}</span></div></td>`;
  461. }
  462. else if (column.type === 'checkbox') {
  463. return `<td class="${classNames.join(' ')}" rowspan="${rowSpan}" colspan="${colSpan}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}><input type="checkbox" ${item._checkboxDisabled ? 'disabled ' : ''}${getBooleanValue(cellValue) ? 'checked' : ''}><span>${item._checkboxLabel}</span></div></td>`;
  464. }
  465. return `<td class="${classNames.join(' ')}" rowspan="${rowSpan}" colspan="${colSpan}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}>${formatText(cellValue, true)}</div></td>`;
  466. }).join('') + '</tr>');
  467. });
  468. }
  469. tables.push('</tbody>');
  470. }
  471. if (isFooter) {
  472. const { footerTableData } = reactData;
  473. const footers = getFooterData($xeTable, opts, footerTableData);
  474. if (footers.length) {
  475. tables.push('<tfoot>');
  476. footers.forEach((row) => {
  477. tables.push(`<tr>${columns.map(column => {
  478. const footAlign = column.footerAlign || column.align || allFooterAlign || allAlign;
  479. const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [];
  480. const cellValue = getFooterCellValue($xeTable, opts, row, column);
  481. if (footAlign) {
  482. classNames.push(`col--${footAlign}`);
  483. }
  484. return `<td class="${classNames.join(' ')}" title="${cellValue}"><div ${isPrint ? '' : `style="width: ${column.renderWidth}px"`}>${formatText(cellValue, true)}</div></td>`;
  485. }).join('')}</tr>`);
  486. });
  487. tables.push('</tfoot>');
  488. }
  489. }
  490. // 是否半选状态
  491. const script = !isAllSelected && isIndeterminate ? `<script>(function(){var a=document.querySelector(".${allCls}");if(a){a.indeterminate=true}})()</script>` : '';
  492. tables.push('</table>', script);
  493. return isPrint ? tables.join('') : createHtmlPage(opts, tables.join(''));
  494. }
  495. function toXML($xeTable, opts, columns, datas) {
  496. const reactData = $xeTable;
  497. let xml = [
  498. '<?xml version="1.0"?>',
  499. '<?mso-application progid="Excel.Sheet"?>',
  500. '<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">',
  501. '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">',
  502. '<Version>16.00</Version>',
  503. '</DocumentProperties>',
  504. '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">',
  505. '<WindowHeight>7920</WindowHeight>',
  506. '<WindowWidth>21570</WindowWidth>',
  507. '<WindowTopX>32767</WindowTopX>',
  508. '<WindowTopY>32767</WindowTopY>',
  509. '<ProtectStructure>False</ProtectStructure>',
  510. '<ProtectWindows>False</ProtectWindows>',
  511. '</ExcelWorkbook>',
  512. `<Worksheet ss:Name="${opts.sheetName}">`,
  513. '<Table>',
  514. columns.map(column => `<Column ss:Width="${column.renderWidth}"/>`).join('')
  515. ].join('');
  516. if (opts.isHeader) {
  517. xml += `<Row>${columns.map(column => `<Cell><Data ss:Type="String">${getHeaderTitle($xeTable, opts, column)}</Data></Cell>`).join('')}</Row>`;
  518. }
  519. datas.forEach(row => {
  520. xml += '<Row>' + columns.map(column => `<Cell><Data ss:Type="String">${row[column.id]}</Data></Cell>`).join('') + '</Row>';
  521. });
  522. if (opts.isFooter) {
  523. const { footerTableData } = reactData;
  524. const footers = getFooterData($xeTable, opts, footerTableData);
  525. footers.forEach((row) => {
  526. xml += `<Row>${columns.map(column => `<Cell><Data ss:Type="String">${getFooterCellValue($xeTable, opts, row, column)}</Data></Cell>`).join('')}</Row>`;
  527. });
  528. }
  529. return `${xml}</Table></Worksheet></Workbook>`;
  530. }
  531. function getContent($xeTable, opts, columns, datas) {
  532. if (columns.length) {
  533. switch (opts.type) {
  534. case 'csv':
  535. return toCsv($xeTable, opts, columns, datas);
  536. case 'txt':
  537. return toTxt($xeTable, opts, columns, datas);
  538. case 'html':
  539. return toHtml($xeTable, opts, columns, datas);
  540. case 'xml':
  541. return toXML($xeTable, opts, columns, datas);
  542. }
  543. }
  544. return '';
  545. }
  546. function downloadFile($xeTable, opts, content) {
  547. const { filename, type, download } = opts;
  548. if (!download) {
  549. const blob = getExportBlobByContent(content, opts);
  550. return Promise.resolve({ type, content, blob });
  551. }
  552. if (VxeUI.saveFile) {
  553. VxeUI.saveFile({ filename, type, content }).then(() => {
  554. if (opts.message !== false) {
  555. if (VxeUI.modal) {
  556. VxeUI.modal.message({ content: getI18n('vxe.table.expSuccess'), status: 'success' });
  557. }
  558. }
  559. });
  560. }
  561. }
  562. function clearColumnConvert(columns) {
  563. XEUtils.eachTree(columns, column => {
  564. delete column._level;
  565. delete column._colSpan;
  566. delete column._rowSpan;
  567. delete column._children;
  568. delete column.childNodes;
  569. }, { children: 'children' });
  570. }
  571. function handleExport($xeTable, opts) {
  572. const $xeGrid = $xeTable.$xeGrid;
  573. const $xeGantt = $xeTable.$xeGantt;
  574. const { remote, columns, colgroups, exportMethod, afterExportMethod } = opts;
  575. return new Promise(resolve => {
  576. if (remote) {
  577. const params = { options: opts, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt };
  578. resolve(exportMethod ? exportMethod(params) : params);
  579. }
  580. else {
  581. const datas = getExportData($xeTable, opts);
  582. resolve($xeTable.preventEvent(null, 'event.export', { options: opts, columns, colgroups, datas }, () => {
  583. return downloadFile($xeTable, opts, getContent($xeTable, opts, columns, datas));
  584. }));
  585. }
  586. }).then((params) => {
  587. clearColumnConvert(columns);
  588. if (!opts.print) {
  589. if (afterExportMethod) {
  590. afterExportMethod({ status: true, options: opts, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt });
  591. }
  592. }
  593. return Object.assign({ status: true }, params);
  594. }).catch(() => {
  595. clearColumnConvert(columns);
  596. if (!opts.print) {
  597. if (afterExportMethod) {
  598. afterExportMethod({ status: false, options: opts, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt });
  599. }
  600. }
  601. const params = { status: false };
  602. return Promise.reject(params);
  603. });
  604. }
  605. function getElementsByTagName(elem, qualifiedName) {
  606. return elem.getElementsByTagName(qualifiedName);
  607. }
  608. function getTxtCellKey(now) {
  609. return `#${now}@${XEUtils.uniqueId()}`;
  610. }
  611. function replaceTxtCell(cell, vMaps) {
  612. return cell.replace(/#\d+@\d+/g, (key) => XEUtils.hasOwnProp(vMaps, key) ? vMaps[key] : key);
  613. }
  614. function getTxtCellValue(val, vMaps) {
  615. const rest = replaceTxtCell(val, vMaps);
  616. return rest.replace(/^"+$/g, (qVal) => '"'.repeat(Math.ceil(qVal.length / 2)));
  617. }
  618. function toExportField(tableConf, field) {
  619. const { fieldMaps, titleMaps } = tableConf;
  620. // title 转 field
  621. if (!fieldMaps[field]) {
  622. const teCol = titleMaps[field];
  623. if (teCol && teCol.field) {
  624. field = teCol.field;
  625. }
  626. }
  627. return field;
  628. }
  629. function parseCsvAndTxt(tableConf, content, cellSeparator) {
  630. const list = content.split(enterSymbol);
  631. const rows = [];
  632. let fields = [];
  633. if (list.length) {
  634. const vMaps = {};
  635. const now = Date.now();
  636. list.forEach((rVal) => {
  637. if (rVal) {
  638. const item = {};
  639. rVal = rVal.replace(/("")|(\n)/g, (text, dVal) => {
  640. const key = getTxtCellKey(now);
  641. vMaps[key] = dVal ? '"' : '\n';
  642. return key;
  643. }).replace(/"(.*?)"/g, (text, cVal) => {
  644. const key = getTxtCellKey(now);
  645. vMaps[key] = replaceTxtCell(cVal, vMaps);
  646. return key;
  647. });
  648. const cells = rVal.split(cellSeparator);
  649. if (!fields.length) {
  650. fields = cells.map((val) => toExportField(tableConf, getTxtCellValue(val.trim(), vMaps)));
  651. }
  652. else {
  653. cells.forEach((val, colIndex) => {
  654. if (colIndex < fields.length) {
  655. item[fields[colIndex]] = getTxtCellValue(val.trim(), vMaps);
  656. }
  657. });
  658. rows.push(item);
  659. }
  660. }
  661. });
  662. }
  663. return { fields, rows };
  664. }
  665. function parseCsv(tableConf, content) {
  666. return parseCsvAndTxt(tableConf, content, ',');
  667. }
  668. function parseTxt(tableConf, content) {
  669. return parseCsvAndTxt(tableConf, content, '\t');
  670. }
  671. function parseHTML(tableConf, content) {
  672. const domParser = new DOMParser();
  673. const xmlDoc = domParser.parseFromString(content, 'text/html');
  674. const bodyNodes = getElementsByTagName(xmlDoc, 'body');
  675. const rows = [];
  676. const fields = [];
  677. if (bodyNodes.length) {
  678. const tableNodes = getElementsByTagName(bodyNodes[0], 'table');
  679. if (tableNodes.length) {
  680. const theadNodes = getElementsByTagName(tableNodes[0], 'thead');
  681. if (theadNodes.length) {
  682. XEUtils.arrayEach(getElementsByTagName(theadNodes[0], 'tr'), rowNode => {
  683. XEUtils.arrayEach(getElementsByTagName(rowNode, 'th'), cellNode => {
  684. fields.push(toExportField(tableConf, cellNode.textContent || ''));
  685. });
  686. });
  687. const tbodyNodes = getElementsByTagName(tableNodes[0], 'tbody');
  688. if (tbodyNodes.length) {
  689. XEUtils.arrayEach(getElementsByTagName(tbodyNodes[0], 'tr'), rowNode => {
  690. const item = {};
  691. XEUtils.arrayEach(getElementsByTagName(rowNode, 'td'), (cellNode, colIndex) => {
  692. if (fields[colIndex]) {
  693. item[fields[colIndex]] = cellNode.textContent || '';
  694. }
  695. });
  696. rows.push(item);
  697. });
  698. }
  699. }
  700. }
  701. }
  702. return { fields, rows };
  703. }
  704. function parseXML(tableConf, content) {
  705. const domParser = new DOMParser();
  706. const xmlDoc = domParser.parseFromString(content, 'application/xml');
  707. const sheetNodes = getElementsByTagName(xmlDoc, 'Worksheet');
  708. const rows = [];
  709. const fields = [];
  710. if (sheetNodes.length) {
  711. const tableNodes = getElementsByTagName(sheetNodes[0], 'Table');
  712. if (tableNodes.length) {
  713. const rowNodes = getElementsByTagName(tableNodes[0], 'Row');
  714. if (rowNodes.length) {
  715. XEUtils.arrayEach(getElementsByTagName(rowNodes[0], 'Cell'), cellNode => {
  716. fields.push(toExportField(tableConf, cellNode.textContent || ''));
  717. });
  718. XEUtils.arrayEach(rowNodes, (rowNode, index) => {
  719. if (index) {
  720. const item = {};
  721. const cellNodes = getElementsByTagName(rowNode, 'Cell');
  722. XEUtils.arrayEach(cellNodes, (cellNode, colIndex) => {
  723. if (fields[colIndex]) {
  724. item[fields[colIndex]] = cellNode.textContent;
  725. }
  726. });
  727. rows.push(item);
  728. }
  729. });
  730. }
  731. }
  732. }
  733. return { fields, rows };
  734. }
  735. function handleImport($xeTable, content, opts) {
  736. const internalData = $xeTable;
  737. const { tableFullColumn, _importResolve, _importReject } = internalData;
  738. let rest = { fields: [], rows: [] };
  739. const tableFieldMaps = {};
  740. const tableTitleMaps = {};
  741. tableFullColumn.forEach((column) => {
  742. const field = column.field;
  743. const title = column.getTitle();
  744. if (field) {
  745. tableFieldMaps[field] = column;
  746. }
  747. if (title) {
  748. tableTitleMaps[column.getTitle()] = column;
  749. }
  750. });
  751. const tableConf = {
  752. fieldMaps: tableFieldMaps,
  753. titleMaps: tableTitleMaps
  754. };
  755. switch (opts.type) {
  756. case 'csv':
  757. rest = parseCsv(tableConf, content);
  758. break;
  759. case 'txt':
  760. rest = parseTxt(tableConf, content);
  761. break;
  762. case 'html':
  763. rest = parseHTML(tableConf, content);
  764. break;
  765. case 'xml':
  766. rest = parseXML(tableConf, content);
  767. break;
  768. }
  769. const { fields, rows } = rest;
  770. const status = fields.some(field => tableFieldMaps[field] || tableTitleMaps[field]);
  771. if (status) {
  772. $xeTable.createData(rows)
  773. .then((data) => {
  774. let loadRest;
  775. if (opts.mode === 'insert' || opts.mode === 'insertBottom') {
  776. loadRest = $xeTable.insertAt(data, -1);
  777. }
  778. if (opts.mode === 'insertTop') {
  779. loadRest = $xeTable.insert(data);
  780. }
  781. else {
  782. loadRest = $xeTable.reloadData(data);
  783. }
  784. if (opts.message !== false) {
  785. // 检测弹窗模块
  786. if (!VxeUI.modal) {
  787. errLog('vxe.error.reqModule', ['Modal']);
  788. }
  789. VxeUI.modal.message({ content: getI18n('vxe.table.impSuccess', [rows.length]), status: 'success' });
  790. }
  791. return loadRest.then(() => {
  792. if (_importResolve) {
  793. _importResolve({ status: true });
  794. }
  795. });
  796. });
  797. }
  798. else if (opts.message !== false) {
  799. // 检测弹窗模块
  800. if (!VxeUI.modal) {
  801. errLog('vxe.error.reqModule', ['Modal']);
  802. }
  803. VxeUI.modal.message({ content: getI18n('vxe.error.impFields'), status: 'error' });
  804. if (_importReject) {
  805. _importReject({ status: false });
  806. }
  807. }
  808. }
  809. function handleFileImport($xeTable, file, opts) {
  810. const internalData = $xeTable;
  811. const importOpts = $xeTable.computeImportOpts;
  812. const { importMethod, afterImportMethod } = opts;
  813. const { type, filename } = parseFile(file);
  814. // 检查类型,如果为自定义导出,则不需要校验类型
  815. if (!importMethod && !XEUtils.includes(XEUtils.keys(importOpts._typeMaps), type)) {
  816. if (opts.message !== false) {
  817. // 检测弹窗模块
  818. if (!VxeUI.modal) {
  819. errLog('vxe.error.reqModule', ['Modal']);
  820. }
  821. VxeUI.modal.message({ content: getI18n('vxe.error.notType', [type]), status: 'error' });
  822. }
  823. const params = { status: false };
  824. return Promise.reject(params);
  825. }
  826. const rest = new Promise((resolve, reject) => {
  827. const _importResolve = (params) => {
  828. resolve(params);
  829. $xeTable._importResolve = null;
  830. $xeTable._importReject = null;
  831. };
  832. const _importReject = (params) => {
  833. reject(params);
  834. $xeTable._importResolve = null;
  835. $xeTable._importReject = null;
  836. };
  837. $xeTable._importResolve = _importResolve;
  838. $xeTable._importReject = _importReject;
  839. if (window.FileReader) {
  840. const options = Object.assign({ mode: 'insertTop' }, opts, { type, filename });
  841. if (options.remote) {
  842. if (importMethod) {
  843. Promise.resolve(importMethod({ file, options, $table: $xeTable })).then(() => {
  844. _importResolve({ status: true });
  845. }).catch(() => {
  846. _importResolve({ status: true });
  847. });
  848. }
  849. else {
  850. _importResolve({ status: true });
  851. }
  852. }
  853. else {
  854. const { tableFullColumn } = internalData;
  855. $xeTable.preventEvent(null, 'event.import', { file, options, columns: tableFullColumn }, () => {
  856. const reader = new FileReader();
  857. reader.onerror = () => {
  858. errLog('vxe.error.notType', [type]);
  859. _importReject({ status: false });
  860. };
  861. reader.onload = (e) => {
  862. handleImport($xeTable, e.target.result, options);
  863. };
  864. reader.readAsText(file, options.encoding || 'UTF-8');
  865. });
  866. }
  867. }
  868. else {
  869. // 不支持的浏览器
  870. errLog('vxe.error.notExp');
  871. _importResolve({ status: true });
  872. }
  873. });
  874. return rest.then(() => {
  875. if (afterImportMethod) {
  876. afterImportMethod({ status: true, options: opts, $table: $xeTable });
  877. }
  878. }).catch((e) => {
  879. if (afterImportMethod) {
  880. afterImportMethod({ status: false, options: opts, $table: $xeTable });
  881. }
  882. return Promise.reject(e);
  883. });
  884. }
  885. function handleCloseExport() {
  886. if (VxeUI.modal) {
  887. return VxeUI.modal.close('VXE_EXPORT_MODAL');
  888. }
  889. return Promise.resolve();
  890. }
  891. function handleFilterColumns(exportOpts, column, columns) {
  892. return columns.some((item) => {
  893. if (isColumnInfo(item)) {
  894. return column.id === item.id;
  895. }
  896. else if (XEUtils.isString(item)) {
  897. return column.field === item;
  898. }
  899. else {
  900. const colid = item.id || item.colId;
  901. const type = item.type;
  902. const field = item.field;
  903. if (colid) {
  904. return column.id === colid;
  905. }
  906. else if (field && type) {
  907. return column.field === field && column.type === type;
  908. }
  909. else if (field) {
  910. return column.field === field;
  911. }
  912. else if (type) {
  913. return column.type === type;
  914. }
  915. }
  916. return false;
  917. });
  918. }
  919. function handleFilterFields(exportOpts, column, includeFields, excludeFields) {
  920. if (excludeFields) {
  921. if (XEUtils.includes(excludeFields, column.field)) {
  922. return false;
  923. }
  924. }
  925. if (includeFields) {
  926. if (XEUtils.includes(includeFields, column.field)) {
  927. return true;
  928. }
  929. return false;
  930. }
  931. return exportOpts.original ? !!column.field : defaultFilterExportColumn(column);
  932. }
  933. function handleExportAndPrint($xeTable, options, isPrint) {
  934. const props = $xeTable;
  935. const reactData = $xeTable;
  936. const internalData = $xeTable;
  937. const $xeGrid = $xeTable.$xeGrid;
  938. const $xeGantt = $xeTable.$xeGantt;
  939. const $xeGGWrapper = $xeGrid || $xeGantt;
  940. const { treeConfig, showHeader, showFooter } = props;
  941. const { initStore, isGroup, footerTableData, exportStore, exportParams } = reactData;
  942. const { collectColumn, mergeBodyList, mergeFooterList } = internalData;
  943. const exportOpts = $xeTable.computeExportOpts;
  944. const hasTree = treeConfig;
  945. const customOpts = $xeTable.computeCustomOpts;
  946. const selectRecords = $xeTable.getCheckboxRecords();
  947. const proxyOpts = $xeGGWrapper ? $xeGGWrapper.computeProxyOpts : {};
  948. const hasFooter = !!footerTableData.length;
  949. const hasMerge = !!(mergeBodyList.length || mergeFooterList.length);
  950. const defOpts = Object.assign({
  951. message: true,
  952. isHeader: showHeader,
  953. isTitle: showHeader,
  954. isFooter: showFooter,
  955. isColgroup: isGroup,
  956. isMerge: hasMerge,
  957. useStyle: true,
  958. current: 'current',
  959. modes: (proxyOpts.ajax && proxyOpts.ajax.queryAll ? ['all'] : []).concat(['current', 'selected', 'empty'])
  960. }, options);
  961. const types = defOpts.types || XEUtils.keys(exportOpts._typeMaps);
  962. const modes = defOpts.modes || [];
  963. const checkMethod = customOpts.checkMethod;
  964. const exportColumns = collectColumn.slice(0);
  965. const { columns, excludeFields, includeFields } = defOpts;
  966. // 处理类型
  967. const typeList = types.map((value) => {
  968. return {
  969. value,
  970. label: getI18n(`vxe.export.types.${value}`)
  971. };
  972. });
  973. const modeList = modes.map((item) => {
  974. if (item && item.value) {
  975. return {
  976. value: item.value,
  977. label: item.label || item.value
  978. };
  979. }
  980. return {
  981. value: item,
  982. label: getI18n(`vxe.export.modes.${item}`)
  983. };
  984. });
  985. // 默认选中
  986. XEUtils.eachTree(exportColumns, (column, index, items, path, parent) => {
  987. const isColGroup = column.children && column.children.length > 0;
  988. let isChecked = false;
  989. if (columns && columns.length) {
  990. isChecked = handleFilterColumns(defOpts, column, columns);
  991. }
  992. else if (excludeFields || includeFields) {
  993. isChecked = handleFilterFields(defOpts, column, includeFields, excludeFields);
  994. }
  995. else {
  996. isChecked = column.visible && (isColGroup || defaultFilterExportColumn(column));
  997. }
  998. column.checked = isChecked;
  999. column.halfChecked = false;
  1000. column.disabled = (parent && parent.disabled) || (checkMethod ? !checkMethod({ $table: $xeTable, column }) : false);
  1001. });
  1002. // 更新条件
  1003. Object.assign(exportStore, {
  1004. columns: exportColumns,
  1005. typeList,
  1006. modeList,
  1007. hasFooter,
  1008. hasMerge,
  1009. hasTree,
  1010. isPrint,
  1011. hasColgroup: isGroup,
  1012. visible: true
  1013. });
  1014. // 默认参数
  1015. Object.assign(exportParams, {
  1016. mode: selectRecords.length ? 'selected' : 'current'
  1017. }, defOpts);
  1018. const { filename, sheetName, mode, type } = exportParams;
  1019. if (filename) {
  1020. if (XEUtils.isFunction(filename)) {
  1021. exportParams.filename = filename({
  1022. options: defOpts,
  1023. $table: $xeTable,
  1024. $grid: $xeGrid,
  1025. $gantt: $xeGantt
  1026. });
  1027. }
  1028. else {
  1029. exportParams.filename = `${filename}`;
  1030. }
  1031. }
  1032. if (sheetName) {
  1033. if (XEUtils.isFunction(sheetName)) {
  1034. exportParams.sheetName = sheetName({
  1035. options: defOpts,
  1036. $table: $xeTable,
  1037. $grid: $xeGrid,
  1038. $gantt: $xeGantt
  1039. });
  1040. }
  1041. else {
  1042. exportParams.sheetName = `${sheetName}`;
  1043. }
  1044. }
  1045. if (!modeList.some(item => item.value === mode)) {
  1046. exportParams.mode = modeList[0].value;
  1047. }
  1048. if (!typeList.some(item => item.value === type)) {
  1049. exportParams.type = typeList[0].value;
  1050. }
  1051. initStore.export = true;
  1052. return $xeTable.$nextTick();
  1053. }
  1054. const getConvertColumns = (columns) => {
  1055. const result = [];
  1056. columns.forEach((column) => {
  1057. if (column.childNodes && column.childNodes.length) {
  1058. result.push(column);
  1059. result.push(...getConvertColumns(column.childNodes));
  1060. }
  1061. else {
  1062. result.push(column);
  1063. }
  1064. });
  1065. return result;
  1066. };
  1067. const convertToRows = (originColumns) => {
  1068. let maxLevel = 1;
  1069. const traverse = (column, parent) => {
  1070. if (parent) {
  1071. column._level = parent._level + 1;
  1072. if (maxLevel < column._level) {
  1073. maxLevel = column._level;
  1074. }
  1075. }
  1076. if (column.childNodes && column.childNodes.length) {
  1077. let colSpan = 0;
  1078. column.childNodes.forEach((subColumn) => {
  1079. traverse(subColumn, column);
  1080. colSpan += subColumn._colSpan;
  1081. });
  1082. column._colSpan = colSpan;
  1083. }
  1084. else {
  1085. column._colSpan = 1;
  1086. }
  1087. };
  1088. originColumns.forEach((column) => {
  1089. column._level = 1;
  1090. traverse(column);
  1091. });
  1092. const rows = [];
  1093. for (let i = 0; i < maxLevel; i++) {
  1094. rows.push([]);
  1095. }
  1096. const allColumns = getConvertColumns(originColumns);
  1097. allColumns.forEach((column) => {
  1098. if (column.childNodes && column.childNodes.length) {
  1099. column._rowSpan = 1;
  1100. }
  1101. else {
  1102. column._rowSpan = maxLevel - column._level + 1;
  1103. }
  1104. rows[column._level - 1].push(column);
  1105. });
  1106. return rows;
  1107. };
  1108. export default {
  1109. methods: {
  1110. /**
  1111. * 导出文件,支持 csv/html/xml/txt
  1112. * 如果是树表格,则默认是导出所有节点
  1113. * 如果是启用了虚拟滚动,则只能导出数据源,可以配合 dataFilterMethod 函数转换数据
  1114. * @param {Object} options 参数
  1115. */
  1116. _exportData(options) {
  1117. const $xeTable = this;
  1118. const props = $xeTable;
  1119. const reactData = $xeTable;
  1120. const internalData = $xeTable;
  1121. const $xeGrid = $xeTable.$xeGrid;
  1122. const $xeGantt = $xeTable.$xeGantt;
  1123. const $xeGGWrapper = $xeGrid || $xeGantt;
  1124. const { treeConfig, showHeader, showFooter } = props;
  1125. const { isGroup } = reactData;
  1126. const { tableFullColumn, afterFullData, afterTreeFullData, collectColumn, mergeBodyList, mergeFooterList } = internalData;
  1127. const exportOpts = $xeTable.computeExportOpts;
  1128. const treeOpts = $xeTable.computeTreeOpts;
  1129. const proxyOpts = $xeGGWrapper ? $xeGGWrapper.computeProxyOpts : {};
  1130. const hasMerge = !!(mergeBodyList.length || mergeFooterList.length);
  1131. const opts = Object.assign({
  1132. message: true,
  1133. isHeader: showHeader,
  1134. isTitle: showHeader,
  1135. isFooter: showFooter,
  1136. isColgroup: isGroup,
  1137. isMerge: hasMerge,
  1138. useStyle: true,
  1139. current: 'current',
  1140. modes: (proxyOpts.ajax && proxyOpts.ajax.queryAll ? ['all'] : []).concat(['current', 'selected', 'empty']),
  1141. download: true,
  1142. type: 'csv'
  1143. // filename: '',
  1144. // sheetName: '',
  1145. // original: false,
  1146. // isAllExpand: false,
  1147. // data: null,
  1148. // remote: false,
  1149. // dataFilterMethod: null,
  1150. // footerFilterMethod: null,
  1151. // exportMethod: null,
  1152. // columnFilterMethod: null,
  1153. // beforeExportMethod: null,
  1154. // afterExportMethod: null
  1155. }, exportOpts, options);
  1156. let { filename, sheetName, type, mode, columns, original, columnFilterMethod, beforeExportMethod, includeFields, excludeFields } = opts;
  1157. let groups = [];
  1158. const selectRecords = $xeTable.getCheckboxRecords();
  1159. if (!mode) {
  1160. mode = selectRecords.length ? 'selected' : 'current';
  1161. }
  1162. let isCustomCol = false;
  1163. let customCols = [];
  1164. if (columns && columns.length) {
  1165. isCustomCol = true;
  1166. customCols = columns;
  1167. }
  1168. else {
  1169. customCols = XEUtils.searchTree(collectColumn, column => {
  1170. const isColGroup = column.children && column.children.length > 0;
  1171. let isChecked = false;
  1172. if (columns && columns.length) {
  1173. isChecked = handleFilterColumns(opts, column, columns);
  1174. }
  1175. else if (excludeFields || includeFields) {
  1176. isChecked = handleFilterFields(opts, column, includeFields, excludeFields);
  1177. }
  1178. else {
  1179. isChecked = column.visible && (isColGroup || defaultFilterExportColumn(column));
  1180. }
  1181. return isChecked;
  1182. }, { children: 'children', mapChildren: 'childNodes', original: true });
  1183. }
  1184. const handleOptions = Object.assign({}, opts, { filename: '', sheetName: '' });
  1185. // 如果设置源数据,则默认导出设置了字段的列
  1186. if (!isCustomCol && !columnFilterMethod) {
  1187. columnFilterMethod = ({ column }) => {
  1188. if (excludeFields) {
  1189. if (XEUtils.includes(excludeFields, column.field)) {
  1190. return false;
  1191. }
  1192. }
  1193. if (includeFields) {
  1194. if (XEUtils.includes(includeFields, column.field)) {
  1195. return true;
  1196. }
  1197. return false;
  1198. }
  1199. return original ? !!column.field : defaultFilterExportColumn(column);
  1200. };
  1201. handleOptions.columnFilterMethod = columnFilterMethod;
  1202. }
  1203. if (customCols) {
  1204. handleOptions._isCustomColumn = true;
  1205. groups = XEUtils.searchTree(XEUtils.mapTree(customCols, (item) => {
  1206. let targetColumn;
  1207. if (item) {
  1208. if (isColumnInfo(item)) {
  1209. targetColumn = item;
  1210. }
  1211. else if (XEUtils.isString(item)) {
  1212. targetColumn = $xeTable.getColumnByField(item);
  1213. }
  1214. else {
  1215. const colid = item.id || item.colId;
  1216. const type = item.type;
  1217. const field = item.field;
  1218. if (colid) {
  1219. targetColumn = $xeTable.getColumnById(colid);
  1220. }
  1221. else if (field && type) {
  1222. targetColumn = tableFullColumn.find((column) => column.field === field && column.type === type);
  1223. }
  1224. else if (field) {
  1225. targetColumn = $xeTable.getColumnByField(field);
  1226. }
  1227. else if (type) {
  1228. targetColumn = tableFullColumn.find((column) => column.type === type);
  1229. }
  1230. }
  1231. return targetColumn || {};
  1232. }
  1233. }, {
  1234. children: 'childNodes',
  1235. mapChildren: '_children'
  1236. }), (column, index) => isColumnInfo(column) && (!columnFilterMethod || columnFilterMethod({ $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, column: column, $columnIndex: index })), {
  1237. children: '_children',
  1238. mapChildren: 'childNodes',
  1239. original: true
  1240. });
  1241. }
  1242. else {
  1243. groups = XEUtils.searchTree(isGroup ? collectColumn : tableFullColumn, (column, index) => column.visible && (!columnFilterMethod || columnFilterMethod({ $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, column, $columnIndex: index })), { children: 'children', mapChildren: 'childNodes', original: true });
  1244. }
  1245. // 获取所有列
  1246. const cols = [];
  1247. XEUtils.eachTree(groups, column => {
  1248. const isColGroup = column.children && column.children.length;
  1249. if (!isColGroup) {
  1250. cols.push(column);
  1251. }
  1252. }, { children: 'childNodes' });
  1253. // 构建分组层级
  1254. handleOptions.columns = cols;
  1255. handleOptions.colgroups = convertToRows(groups);
  1256. if (filename) {
  1257. if (XEUtils.isFunction(filename)) {
  1258. handleOptions.filename = filename({
  1259. options: opts,
  1260. $table: $xeTable,
  1261. $grid: $xeGrid,
  1262. $gantt: $xeGantt
  1263. });
  1264. }
  1265. else {
  1266. handleOptions.filename = `${filename}`;
  1267. }
  1268. }
  1269. if (!handleOptions.filename) {
  1270. handleOptions.filename = getI18n(handleOptions.original ? 'vxe.table.expOriginFilename' : 'vxe.table.expFilename', [XEUtils.toDateString(Date.now(), 'yyyyMMddHHmmss')]);
  1271. }
  1272. if (sheetName) {
  1273. if (XEUtils.isFunction(sheetName)) {
  1274. handleOptions.sheetName = sheetName({
  1275. options: opts,
  1276. $table: $xeTable,
  1277. $grid: $xeGrid,
  1278. $gantt: $xeGantt
  1279. });
  1280. }
  1281. else {
  1282. handleOptions.sheetName = `${sheetName}`;
  1283. }
  1284. }
  1285. if (!handleOptions.sheetName) {
  1286. handleOptions.sheetName = document.title || '';
  1287. }
  1288. // 检查类型,如果为自定义导出,则不需要校验类型
  1289. if (!handleOptions.exportMethod && !XEUtils.includes(XEUtils.keys(exportOpts._typeMaps), type)) {
  1290. errLog('vxe.error.notType', [type]);
  1291. if (['xlsx', 'pdf'].includes(type)) {
  1292. warnLog('vxe.error.reqPlugin', [4, 'plugin-export-xlsx']);
  1293. }
  1294. const params = { status: false };
  1295. return Promise.reject(params);
  1296. }
  1297. if (!handleOptions.print) {
  1298. if (beforeExportMethod) {
  1299. beforeExportMethod({ options: handleOptions, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt });
  1300. }
  1301. }
  1302. if (!handleOptions.data) {
  1303. handleOptions.data = [];
  1304. if (mode === 'selected') {
  1305. if (['html', 'pdf'].indexOf(type) > -1 && treeConfig) {
  1306. handleOptions.data = XEUtils.searchTree($xeTable.getTableData().fullData, item => $xeTable.findRowIndexOf(selectRecords, item) > -1, Object.assign({}, treeOpts, { data: '_row' }));
  1307. }
  1308. else {
  1309. handleOptions.data = selectRecords;
  1310. }
  1311. }
  1312. else if (mode === 'all') {
  1313. if (!$xeGGWrapper) {
  1314. errLog('vxe.error.errProp', ['all', 'mode=current,selected']);
  1315. }
  1316. if ($xeGGWrapper && !handleOptions.remote) {
  1317. const gridReactData = $xeGGWrapper;
  1318. const proxyOpts = $xeGGWrapper.computeProxyOpts;
  1319. const { sortData } = gridReactData;
  1320. const { beforeQueryAll, afterQueryAll, ajax = {} } = proxyOpts;
  1321. const resConfigs = proxyOpts.response || proxyOpts.props || {};
  1322. const ajaxMethods = ajax.queryAll;
  1323. const queryAllSuccessMethods = ajax.queryAllSuccess;
  1324. const queryAllErrorMethods = ajax.queryAllError;
  1325. if (!ajaxMethods) {
  1326. errLog('vxe.error.notFunc', ['proxy-config.ajax.queryAll']);
  1327. }
  1328. if (ajaxMethods) {
  1329. const params = {
  1330. $table: $xeTable,
  1331. $grid: $xeGrid,
  1332. $gantt: $xeGantt,
  1333. sort: sortData.length ? sortData[0] : {},
  1334. sorts: sortData,
  1335. filters: gridReactData.filterData,
  1336. form: gridReactData.formData,
  1337. options: handleOptions
  1338. };
  1339. return Promise.resolve((beforeQueryAll || ajaxMethods)(params))
  1340. .then(rest => {
  1341. const listProp = resConfigs.list;
  1342. handleOptions.data = (listProp ? (XEUtils.isFunction(listProp) ? listProp({ data: rest, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }) : XEUtils.get(rest, listProp)) : rest) || [];
  1343. if (afterQueryAll) {
  1344. afterQueryAll(params);
  1345. }
  1346. if (queryAllSuccessMethods) {
  1347. queryAllSuccessMethods(Object.assign(Object.assign({}, params), { response: rest }));
  1348. }
  1349. return handleExport($xeTable, handleOptions);
  1350. })
  1351. .catch((rest) => {
  1352. if (queryAllErrorMethods) {
  1353. queryAllErrorMethods(Object.assign(Object.assign({}, params), { response: rest }));
  1354. }
  1355. });
  1356. }
  1357. }
  1358. }
  1359. else if (mode === 'current') {
  1360. handleOptions.data = treeConfig ? afterTreeFullData : afterFullData;
  1361. }
  1362. }
  1363. else {
  1364. handleOptions._isCustomData = true;
  1365. }
  1366. return handleExport($xeTable, handleOptions);
  1367. },
  1368. _importByFile(file, options) {
  1369. const $xeTable = this;
  1370. const opts = Object.assign({}, options);
  1371. const { beforeImportMethod } = opts;
  1372. if (beforeImportMethod) {
  1373. beforeImportMethod({ options: opts, $table: $xeTable });
  1374. }
  1375. return handleFileImport($xeTable, file, opts);
  1376. },
  1377. _importData(options) {
  1378. const $xeTable = this;
  1379. const { importOpts } = this;
  1380. const opts = Object.assign({
  1381. types: XEUtils.keys(importOpts._typeMaps)
  1382. // beforeImportMethod: null,
  1383. // afterImportMethod: null
  1384. }, importOpts, options);
  1385. const { beforeImportMethod, afterImportMethod } = opts;
  1386. if (beforeImportMethod) {
  1387. beforeImportMethod({ options: opts, $table: this });
  1388. }
  1389. return VxeUI.readFile(opts).catch(e => {
  1390. if (afterImportMethod) {
  1391. afterImportMethod({ status: false, options: opts, $table: $xeTable });
  1392. }
  1393. return Promise.reject(e);
  1394. }).then((params) => {
  1395. const { file } = params;
  1396. return handleFileImport($xeTable, file, opts);
  1397. });
  1398. },
  1399. _saveFile(options) {
  1400. return VxeUI.saveFile(options);
  1401. },
  1402. _readFile(options) {
  1403. return VxeUI.readFile(options);
  1404. },
  1405. _print(options) {
  1406. const $xeTable = this;
  1407. const $xeGrid = $xeTable.$xeGrid;
  1408. const $xeGantt = $xeTable.$xeGantt;
  1409. const printOpts = $xeTable.computePrintOpts;
  1410. const opts = Object.assign({
  1411. original: false
  1412. // beforePrintMethod
  1413. }, printOpts, options, {
  1414. type: 'html',
  1415. download: false,
  1416. remote: false,
  1417. print: true
  1418. });
  1419. const { sheetName } = opts;
  1420. let printTitle = '';
  1421. if (sheetName) {
  1422. if (XEUtils.isFunction(sheetName)) {
  1423. printTitle = sheetName({
  1424. options: opts,
  1425. $table: $xeTable,
  1426. $grid: $xeGrid,
  1427. $gantt: $xeGantt
  1428. });
  1429. }
  1430. else {
  1431. printTitle = `${sheetName}`;
  1432. }
  1433. }
  1434. if (!printTitle) {
  1435. printTitle = document.title || '';
  1436. }
  1437. const beforePrintMethod = opts.beforePrintMethod;
  1438. const tableHtml = opts.html || opts.content;
  1439. return new Promise((resolve, reject) => {
  1440. if (VxeUI.print) {
  1441. if (tableHtml) {
  1442. resolve(VxeUI.print({
  1443. title: printTitle,
  1444. html: tableHtml,
  1445. customStyle: opts.style,
  1446. beforeMethod: beforePrintMethod
  1447. ? ({ html }) => {
  1448. return beforePrintMethod({
  1449. html,
  1450. content: html,
  1451. options: opts,
  1452. $table: $xeTable,
  1453. $grid: $xeGrid,
  1454. $gantt: $xeGantt
  1455. });
  1456. }
  1457. : undefined
  1458. }));
  1459. }
  1460. else {
  1461. resolve($xeTable.exportData(opts).then(({ content }) => {
  1462. return VxeUI.print({
  1463. title: printTitle,
  1464. html: content,
  1465. customStyle: opts.style,
  1466. beforeMethod: beforePrintMethod
  1467. ? ({ html }) => {
  1468. return beforePrintMethod({
  1469. html,
  1470. content: html,
  1471. options: opts,
  1472. $table: $xeTable,
  1473. $grid: $xeGrid,
  1474. $gantt: $xeGantt
  1475. });
  1476. }
  1477. : undefined
  1478. });
  1479. }));
  1480. }
  1481. }
  1482. else {
  1483. const e = { status: false };
  1484. reject(e);
  1485. }
  1486. });
  1487. },
  1488. _getPrintHtml(options) {
  1489. const $xeTable = this;
  1490. const printOpts = $xeTable.computePrintOpts;
  1491. const opts = Object.assign({
  1492. original: false
  1493. // beforePrintMethod
  1494. }, printOpts, options, {
  1495. type: 'html',
  1496. download: false,
  1497. remote: false,
  1498. print: true
  1499. });
  1500. return $xeTable.exportData(opts).then(({ content }) => {
  1501. return {
  1502. html: content
  1503. };
  1504. });
  1505. },
  1506. _closeImport() {
  1507. if (VxeUI.modal) {
  1508. return VxeUI.modal.close('VXE_IMPORT_MODAL');
  1509. }
  1510. return Promise.resolve();
  1511. },
  1512. _openImport(options) {
  1513. const $xeTable = this;
  1514. const props = $xeTable;
  1515. const reactData = $xeTable;
  1516. const { treeConfig, importConfig } = props;
  1517. const { initStore, importStore, importParams } = reactData;
  1518. const importOpts = $xeTable.computeImportOpts;
  1519. const defOpts = Object.assign({
  1520. mode: 'insertTop',
  1521. message: true,
  1522. types: XEUtils.keys(importOpts._typeMaps),
  1523. modes: ['insertTop', 'covering']
  1524. }, importOpts, options);
  1525. const types = defOpts.types || [];
  1526. const modes = defOpts.modes || [];
  1527. const isTree = !!treeConfig;
  1528. if (isTree) {
  1529. if (defOpts.message) {
  1530. VxeUI.modal.message({ content: getI18n('vxe.error.treeNotImp'), status: 'error' });
  1531. }
  1532. return;
  1533. }
  1534. if (!importConfig) {
  1535. errLog('vxe.error.reqProp', ['import-config']);
  1536. }
  1537. // 处理类型
  1538. const typeList = types.map((value) => {
  1539. return {
  1540. value,
  1541. label: getI18n(`vxe.export.types.${value}`)
  1542. };
  1543. });
  1544. const modeList = modes.map((item) => {
  1545. if (item && item.value) {
  1546. return {
  1547. value: item.value,
  1548. label: item.label || item.value
  1549. };
  1550. }
  1551. return {
  1552. value: item,
  1553. label: getI18n(`vxe.import.modes.${item}`)
  1554. };
  1555. });
  1556. Object.assign(importStore, {
  1557. file: null,
  1558. type: '',
  1559. filename: '',
  1560. modeList,
  1561. typeList,
  1562. visible: true
  1563. });
  1564. Object.assign(importParams, defOpts);
  1565. if (!modeList.some(item => item.value === importParams.mode)) {
  1566. importParams.mode = modeList[0].value;
  1567. }
  1568. initStore.import = true;
  1569. },
  1570. _closeExport: handleCloseExport,
  1571. _openExport(options) {
  1572. const $xeTable = this;
  1573. const props = $xeTable;
  1574. const exportOpts = $xeTable.computeExportOpts;
  1575. const defOpts = Object.assign({
  1576. message: true,
  1577. types: XEUtils.keys(exportOpts._typeMaps)
  1578. }, exportOpts, options);
  1579. if (!props.exportConfig) {
  1580. errLog('vxe.error.reqProp', ['export-config']);
  1581. }
  1582. return handleExportAndPrint($xeTable, defOpts);
  1583. },
  1584. _closePrint: handleCloseExport,
  1585. _openPrint(options) {
  1586. const $xeTable = this;
  1587. const props = $xeTable;
  1588. const printOpts = $xeTable.computePrintOpts;
  1589. const defOpts = Object.assign({
  1590. message: true
  1591. }, printOpts, options);
  1592. if (!props.printConfig) {
  1593. errLog('vxe.error.reqProp', ['print-config']);
  1594. }
  1595. return handleExportAndPrint($xeTable, defOpts, true);
  1596. }
  1597. }
  1598. };