import XEUtils from 'xe-utils' import { VxeUI } from '../../../ui' import { isColumnInfo, getCellValue, createHandleGetRowId } from '../../src/util' import { parseFile, formatText, eqEmptyValue } from '../../../ui/src/utils' import { hasClass } from '../../../ui/src/dom' import { createHtmlPage, getExportBlobByContent } from './util' import { warnLog, errLog } from '../../../ui/src/log' import type { VxeTablePropTypes, VxeTableDefines, TableReactData, VxeTablePrivateMethods, TableInternalData, GridReactData, VxeColumnPropTypes, VxeTableConstructor } from '../../../../types' const { getI18n, renderer } = VxeUI let htmlCellElem: any const csvBOM = '\ufeff' const enterSymbol = '\r\n' function hasTreeChildren ($xeTable: VxeTableConstructor, row: any) { const treeOpts = $xeTable.computeTreeOpts const childrenField = treeOpts.children || treeOpts.childrenField return row[childrenField] && row[childrenField].length } function getSeq ($xeTable: VxeTableConstructor, cellValue: any, row: any, $rowIndex: number, column: VxeTableDefines.ColumnInfo, $columnIndex: number) { const seqOpts = $xeTable.computeSeqOpts const seqMethod = seqOpts.seqMethod || (column as any).seqMethod if (seqMethod) { return seqMethod({ $table: $xeTable, row, rowIndex: $xeTable.getRowIndex(row), $rowIndex, column, columnIndex: $xeTable.getColumnIndex(column), $columnIndex }) } return cellValue } function defaultFilterExportColumn (column: VxeTableDefines.ColumnInfo) { return !!column.field || ['seq', 'checkbox', 'radio'].indexOf(column.type || '') === -1 } function toTableBorder (border: VxeTablePropTypes.Border | undefined) { if (border === true) { return 'full' } if (border) { return border } return 'default' } function toBooleanValue (cellValue: any) { return XEUtils.isBoolean(cellValue) ? (cellValue ? 'TRUE' : 'FALSE') : cellValue } const toStringValue = (cellValue: any) => { return eqEmptyValue(cellValue) ? '' : `${cellValue}` } function getBodyLabelData ($xeTable: VxeTableConstructor, opts: VxeTablePropTypes.ExportHandleOptions, columns: VxeTableDefines.ColumnInfo[], datas: any[]) { const props = $xeTable const { isAllExpand, mode } = opts const { treeConfig } = props const radioOpts = $xeTable.computeRadioOpts const checkboxOpts = $xeTable.computeCheckboxOpts const treeOpts = $xeTable.computeTreeOpts const columnOpts = $xeTable.computeColumnOpts if (!htmlCellElem) { htmlCellElem = document.createElement('div') } if (treeConfig) { const childrenField = treeOpts.children || treeOpts.childrenField // 如果是树表格只允许导出数据源 const rest: any[] = [] const expandMaps: Record = {} const useMaps: Record = {} const { handleGetRowId } = createHandleGetRowId($xeTable) XEUtils.eachTree(datas, (item, $rowIndex, items, path, parent, nodes) => { const row = item._row || item const rowid = handleGetRowId(row) if (useMaps[rowid]) { return } const parentRow = parent && parent._row ? parent._row : parent const pRowid = parentRow ? handleGetRowId(parentRow) : '' if ((isAllExpand || !parentRow || (expandMaps[pRowid] && $xeTable.isTreeExpandByRow(parentRow)))) { const hasRowChild = hasTreeChildren($xeTable, row) const item: any = { _row: row, _level: nodes.length - 1, _hasChild: hasRowChild, _expand: hasRowChild && $xeTable.isTreeExpandByRow(row) } columns.forEach((column, $columnIndex) => { let cellValue: string | number | boolean | null = '' const renderOpts = column.editRender || column.cellRender let bodyExportMethod: VxeColumnPropTypes.ExportMethod | undefined = column.exportMethod || columnOpts.exportMethod if (!bodyExportMethod && renderOpts && renderOpts.name) { const compConf = renderer.get(renderOpts.name) if (compConf) { bodyExportMethod = compConf.tableExportMethod || compConf.exportMethod } } if (!bodyExportMethod) { bodyExportMethod = columnOpts.exportMethod } if (bodyExportMethod) { cellValue = bodyExportMethod({ $table: $xeTable, row, column, options: opts }) } else { switch (column.type) { case 'seq': { const seqVal = path.map((num, i) => i % 2 === 0 ? (Number(num) + 1) : '.').join('') cellValue = mode === 'all' ? seqVal : getSeq($xeTable, seqVal, row, $rowIndex, column, $columnIndex) break } case 'checkbox': cellValue = toBooleanValue($xeTable.isCheckedByCheckboxRow(row)) item._checkboxLabel = checkboxOpts.labelField ? XEUtils.get(row, checkboxOpts.labelField) : '' item._checkboxDisabled = checkboxOpts.checkMethod && !checkboxOpts.checkMethod({ $table: $xeTable, row }) break case 'radio': cellValue = toBooleanValue($xeTable.isCheckedByRadioRow(row)) item._radioLabel = radioOpts.labelField ? XEUtils.get(row, radioOpts.labelField) : '' item._radioDisabled = radioOpts.checkMethod && !radioOpts.checkMethod({ $table: $xeTable, row }) break default: if (opts.original) { cellValue = getCellValue(row, column) } else { cellValue = $xeTable.getCellLabel(row, column) if (column.type === 'html') { htmlCellElem.innerHTML = cellValue cellValue = htmlCellElem.innerText.trim() } else { const cell = $xeTable.getCellElement(row, column) if (cell && !hasClass(cell, 'is--progress')) { cellValue = cell.innerText.trim() } } } } } item[column.id] = toStringValue(cellValue) }) useMaps[rowid] = true if (pRowid) { expandMaps[pRowid] = true } rest.push(Object.assign(item, row)) } }, { children: childrenField }) return rest } return datas.map((row, $rowIndex) => { const item: any = { _row: row } columns.forEach((column, $columnIndex) => { let cellValue: string | number | boolean | null = '' const renderOpts = column.editRender || column.cellRender let bodyExportMethod: VxeColumnPropTypes.ExportMethod | undefined = column.exportMethod || columnOpts.exportMethod if (!bodyExportMethod && renderOpts && renderOpts.name) { const compConf = renderer.get(renderOpts.name) if (compConf) { bodyExportMethod = compConf.tableExportMethod || compConf.exportMethod } } if (bodyExportMethod) { cellValue = bodyExportMethod({ $table: $xeTable, row, column, options: opts }) } else { switch (column.type) { case 'seq': { const seqValue = $rowIndex + 1 cellValue = mode === 'all' ? seqValue : getSeq($xeTable, seqValue, row, $rowIndex, column, $columnIndex) break } case 'checkbox': cellValue = toBooleanValue($xeTable.isCheckedByCheckboxRow(row)) item._checkboxLabel = checkboxOpts.labelField ? XEUtils.get(row, checkboxOpts.labelField) : '' item._checkboxDisabled = checkboxOpts.checkMethod && !checkboxOpts.checkMethod({ $table: $xeTable, row }) break case 'radio': cellValue = toBooleanValue($xeTable.isCheckedByRadioRow(row)) item._radioLabel = radioOpts.labelField ? XEUtils.get(row, radioOpts.labelField) : '' item._radioDisabled = radioOpts.checkMethod && !radioOpts.checkMethod({ $table: $xeTable, row }) break default: if (opts.original) { cellValue = getCellValue(row, column) } else { cellValue = $xeTable.getCellLabel(row, column) if (column.type === 'html') { htmlCellElem.innerHTML = cellValue cellValue = htmlCellElem.innerText.trim() } else { const cell = $xeTable.getCellElement(row, column) if (cell && !hasClass(cell, 'is--progress')) { cellValue = cell.innerText.trim() } } } } } item[column.id] = toStringValue(cellValue) }) return item }) } function getExportData ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions) { const { columns, dataFilterMethod } = opts let datas = opts.data if (dataFilterMethod) { datas = datas.filter((row, index) => dataFilterMethod({ $table: $xeTable, row, $rowIndex: index })) } return getBodyLabelData($xeTable, opts, columns, datas) } function getBooleanValue (cellValue: any) { return cellValue === 'TRUE' || cellValue === 'true' || cellValue === true } function getHeaderTitle ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, column: VxeTableDefines.ColumnInfo) { const columnOpts = $xeTable.computeColumnOpts const headExportMethod = column.headerExportMethod || columnOpts.headerExportMethod return headExportMethod ? headExportMethod({ column, options: opts, $table: $xeTable }) : ((opts.isTitle ? column.getTitle() : column.field) || '') } function getFooterCellValue ($xeTable: any, opts: VxeTablePropTypes.ExportHandleOptions, row: any, column: VxeTableDefines.ColumnInfo) { const columnOpts = $xeTable.computeColumnOpts const renderOpts = column.editRender || column.cellRender let footLabelMethod: VxeColumnPropTypes.FooterExportMethod | undefined = column.footerExportMethod if (!footLabelMethod && renderOpts && renderOpts.name) { const compConf = renderer.get(renderOpts.name) if (compConf) { footLabelMethod = compConf.tableFooterExportMethod || compConf.footerExportMethod || (compConf as any).footerCellExportMethod } } if (!footLabelMethod) { footLabelMethod = columnOpts.footerExportMethod } const _columnIndex = $xeTable.getVTColumnIndex(column) if (footLabelMethod) { return footLabelMethod({ $table: $xeTable, items: row, itemIndex: _columnIndex, row, _columnIndex, column, options: opts }) } // 兼容老模式 if (XEUtils.isArray(row)) { return XEUtils.toValueString(row[_columnIndex]) } return XEUtils.get(row, column.field) } function getFooterData ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, footerTableData: any[]) { const { footerFilterMethod } = opts return footerFilterMethod ? footerTableData.filter((items: any, index: any) => footerFilterMethod({ $table: $xeTable, items, $rowIndex: index })) : footerTableData } function getCsvCellTypeLabel (column: any, cellValue: any) { if (cellValue) { if (column.type === 'seq') { return `\t${cellValue}` } switch (column.cellType) { case 'string': if (!isNaN(cellValue)) { return `\t${cellValue}` } break case 'number': break default: if (cellValue.length >= 12 && !isNaN(cellValue)) { return `\t${cellValue}` } break } } return cellValue } function toTxtCellLabel (val: any) { if (/[",\s\n]/.test(val)) { return `"${val.replace(/"/g, '""')}"` } return val } function toCsv ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, columns: VxeTableDefines.ColumnInfo[], datas: any[]) { const reactData = $xeTable as unknown as TableReactData let content = csvBOM if (opts.isHeader) { content += columns.map(column => toTxtCellLabel(getHeaderTitle($xeTable, opts, column))).join(',') + enterSymbol } datas.forEach(row => { content += columns.map(column => toTxtCellLabel(getCsvCellTypeLabel(column, row[column.id]))).join(',') + enterSymbol }) if (opts.isFooter) { const { footerTableData } = reactData const footers = getFooterData($xeTable, opts, footerTableData) footers.forEach((row) => { content += columns.map(column => toTxtCellLabel(getFooterCellValue($xeTable, opts, row, column))).join(',') + enterSymbol }) } return content } function toTxt ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, columns: VxeTableDefines.ColumnInfo[], datas: any[]) { const reactData = $xeTable as unknown as TableReactData let content = '' if (opts.isHeader) { content += columns.map(column => toTxtCellLabel(getHeaderTitle($xeTable, opts, column))).join('\t') + enterSymbol } datas.forEach(row => { content += columns.map(column => toTxtCellLabel(row[column.id])).join('\t') + enterSymbol }) if (opts.isFooter) { const { footerTableData } = reactData const footers = getFooterData($xeTable, opts, footerTableData) footers.forEach((row) => { content += columns.map(column => toTxtCellLabel(getFooterCellValue($xeTable, opts, row, column))).join('\t') + enterSymbol }) } return content } function hasEllipsis ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, column: VxeTableDefines.ColumnInfo, property: 'showOverflow' | 'showHeaderOverflow', allColumnOverflow: VxeTablePropTypes.ShowOverflow | undefined) { const reactData = $xeTable as unknown as TableReactData const columnOverflow = column[property] const headOverflow = XEUtils.isUndefined(columnOverflow) || XEUtils.isNull(columnOverflow) ? allColumnOverflow : columnOverflow const showEllipsis = headOverflow === 'ellipsis' const showTitle = headOverflow === 'title' const showTooltip = headOverflow === true || headOverflow === 'tooltip' let isEllipsis = showTitle || showTooltip || showEllipsis // 虚拟滚动不支持动态高度 const { scrollXLoad, scrollYLoad } = reactData if ((scrollXLoad || scrollYLoad) && !isEllipsis) { isEllipsis = true } return isEllipsis } function toHtml ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, columns: VxeTableDefines.ColumnInfo[], datas: any[]) { const props = $xeTable const reactData = $xeTable as unknown as TableReactData const internalData = $xeTable as unknown as TableInternalData const { id, border, treeConfig, headerAlign: allHeaderAlign, align: allAlign, footerAlign: allFooterAlign, showOverflow: allColumnOverflow, showHeaderOverflow: allColumnHeaderOverflow } = props const { isAllSelected, isIndeterminate } = reactData const { mergeBodyCellMaps } = internalData const treeOpts = $xeTable.computeTreeOpts const { print: isPrint, isHeader, isFooter, isColgroup, isMerge, colgroups, original } = opts const allCls = 'check-all' const clss = [ 'vxe-table', `border--${toTableBorder(border)}`, isPrint ? 'is--print' : '', isHeader ? 'is--header' : '' ].filter(cls => cls) const tables = [ ``, `${columns.map(column => ``).join('')}` ] if (isHeader) { tables.push('') if (isColgroup && !original) { colgroups.forEach((cols: any[]) => { tables.push( `${cols.map(column => { const headAlign = column.headerAlign || column.align || allHeaderAlign || allAlign const classNames = hasEllipsis($xeTable, column, 'showHeaderOverflow', allColumnHeaderOverflow) ? ['col--ellipsis'] : [] const cellTitle = getHeaderTitle($xeTable, opts, column) let childWidth = 0 let countChild = 0 XEUtils.eachTree([column], item => { if (!item.childNodes || !column.childNodes.length) { countChild++ } childWidth += item.renderWidth }, { children: 'childNodes' }) const cellWidth = childWidth - countChild if (headAlign) { classNames.push(`col--${headAlign}`) } if (column.type === 'checkbox') { return `` } return `` }).join('')}` ) }) } else { tables.push( `${columns.map(column => { const headAlign = column.headerAlign || column.align || allHeaderAlign || allAlign const classNames = hasEllipsis($xeTable, column, 'showHeaderOverflow', allColumnHeaderOverflow) ? ['col--ellipsis'] : [] const cellTitle = getHeaderTitle($xeTable, opts, column) if (headAlign) { classNames.push(`col--${headAlign}`) } if (column.type === 'checkbox') { return `` } return `` }).join('')}` ) } tables.push('') } if (datas.length) { tables.push('') if (treeConfig) { datas.forEach(item => { tables.push( '' + columns.map(column => { const cellAlign = column.align || allAlign const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [] const cellValue = item[column.id] if (cellAlign) { classNames.push(`col--${cellAlign}`) } if (column.treeNode) { let treeIcon = '' if (item._hasChild) { treeIcon = `` } classNames.push('vxe-table--tree-node') if (column.type === 'radio') { return `` } else if (column.type === 'checkbox') { return `` } return `` } if (column.type === 'radio') { return `` } else if (column.type === 'checkbox') { return `` } return `` }).join('') + '' ) }) } else { datas.forEach(item => { tables.push( '' + columns.map(column => { const colid = column.id const cellAlign = column.align || allAlign const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [] const cellValue = item[colid] let rowSpan = 1 let colSpan = 1 if (isMerge) { const _rowIndex = $xeTable.getVTRowIndex(item._row) const _columnIndex = $xeTable.getVTColumnIndex(column) const spanRest = mergeBodyCellMaps[`${_rowIndex}:${_columnIndex}`] if (spanRest) { const { rowspan, colspan } = spanRest if (!rowspan || !colspan) { return '' } if (rowspan > 1) { rowSpan = rowspan } if (colspan > 1) { colSpan = colspan } } } if (cellAlign) { classNames.push(`col--${cellAlign}`) } if (column.type === 'radio') { return `` } else if (column.type === 'checkbox') { return `` } return `` }).join('') + '' ) }) } tables.push('') } if (isFooter) { const { footerTableData } = reactData const footers = getFooterData($xeTable, opts, footerTableData) if (footers.length) { tables.push('') footers.forEach((row: any) => { tables.push( `${columns.map(column => { const footAlign = column.footerAlign || column.align || allFooterAlign || allAlign const classNames = hasEllipsis($xeTable, column, 'showOverflow', allColumnOverflow) ? ['col--ellipsis'] : [] const cellValue = getFooterCellValue($xeTable, opts, row, column) if (footAlign) { classNames.push(`col--${footAlign}`) } return `` }).join('')}` ) }) tables.push('') } } // 是否半选状态 const script = !isAllSelected && isIndeterminate ? `` : '' tables.push('
${cellTitle}
${formatText(cellTitle, true)}
${cellTitle}
${formatText(cellTitle, true)}
${treeIcon}
${item._radioLabel}
${treeIcon}
${item._checkboxLabel}
${treeIcon}
${cellValue}
${item._radioLabel}
${item._checkboxLabel}
${formatText(cellValue, true)}
${item._radioLabel}
${item._checkboxLabel}
${formatText(cellValue, true)}
${formatText(cellValue, true)}
', script) return isPrint ? tables.join('') : createHtmlPage(opts, tables.join('')) } function toXML ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: any, columns: any[], datas: any[]) { const reactData = $xeTable as unknown as TableReactData let xml = [ '', '', '', '', '16.00', '', '', '7920', '21570', '32767', '32767', 'False', 'False', '', ``, '', columns.map(column => ``).join('') ].join('') if (opts.isHeader) { xml += `${columns.map(column => `${getHeaderTitle($xeTable, opts, column)}`).join('')}` } datas.forEach(row => { xml += '' + columns.map(column => `${row[column.id]}`).join('') + '' }) if (opts.isFooter) { const { footerTableData } = reactData const footers = getFooterData($xeTable, opts, footerTableData) footers.forEach((row: any) => { xml += `${columns.map(column => `${getFooterCellValue($xeTable, opts, row, column)}`).join('')}` }) } return `${xml}
` } function getContent ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions, columns: VxeTableDefines.ColumnInfo[], datas: any[]) { if (columns.length) { switch (opts.type) { case 'csv': return toCsv($xeTable, opts, columns, datas) case 'txt': return toTxt($xeTable, opts, columns, datas) case 'html': return toHtml($xeTable, opts, columns, datas) case 'xml': return toXML($xeTable, opts, columns, datas) } } return '' } function downloadFile ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: any, content: any) { const { filename, type, download } = opts if (!download) { const blob = getExportBlobByContent(content, opts) return Promise.resolve({ type, content, blob }) } if (VxeUI.saveFile) { VxeUI.saveFile({ filename, type, content }).then(() => { if (opts.message !== false) { if (VxeUI.modal) { VxeUI.modal.message({ content: getI18n('vxe.table.expSuccess'), status: 'success' }) } } }) } } function clearColumnConvert (columns: any) { XEUtils.eachTree(columns, column => { delete column._level delete column._colSpan delete column._rowSpan delete column._children delete column.childNodes }, { children: 'children' }) } function handleExport ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, opts: VxeTablePropTypes.ExportHandleOptions) { const $xeGrid = $xeTable.$xeGrid const { remote, columns, colgroups, exportMethod, afterExportMethod } = opts return new Promise(resolve => { if (remote) { const params = { options: opts, $table: $xeTable, $grid: $xeGrid } resolve(exportMethod ? exportMethod(params) : params) } else { const datas = getExportData($xeTable, opts) resolve( $xeTable.preventEvent(null, 'event.export', { options: opts, columns, colgroups, datas }, () => { return downloadFile($xeTable, opts, getContent($xeTable, opts, columns, datas)) }) ) } }).then((params) => { clearColumnConvert(columns) if (!opts.print) { if (afterExportMethod) { afterExportMethod({ status: true, options: opts, $table: $xeTable, $grid: $xeGrid }) } } return Object.assign({ status: true }, params) }).catch(() => { clearColumnConvert(columns) if (!opts.print) { if (afterExportMethod) { afterExportMethod({ status: false, options: opts, $table: $xeTable, $grid: $xeGrid }) } } const params = { status: false } return Promise.reject(params) }) } function getElementsByTagName (elem: any, qualifiedName: any) { return elem.getElementsByTagName(qualifiedName) as HTMLElement[] } function getTxtCellKey (now: any) { return `#${now}@${XEUtils.uniqueId()}` } function replaceTxtCell (cell: any, vMaps: any) { return cell.replace(/#\d+@\d+/g, (key: any) => XEUtils.hasOwnProp(vMaps, key) ? vMaps[key] : key) } function getTxtCellValue (val: any, vMaps: any) { const rest = replaceTxtCell(val, vMaps) return rest.replace(/^"+$/g, (qVal: any) => '"'.repeat(Math.ceil(qVal.length / 2))) } function toExportField (tableConf: { fieldMaps: Record titleMaps: Record }, field: string) { const { fieldMaps, titleMaps } = tableConf // title 转 field if (!fieldMaps[field]) { const teCol = titleMaps[field] if (teCol && teCol.field) { field = teCol.field } } return field } function parseCsvAndTxt (tableConf: { fieldMaps: Record titleMaps: Record }, content: string, cellSeparator: string) { const list = content.split(enterSymbol) const rows: any[] = [] let fields: string[] = [] if (list.length) { const vMaps: any = {} const now = Date.now() list.forEach((rVal) => { if (rVal) { const item: any = {} rVal = rVal.replace(/("")|(\n)/g, (text: string, dVal: string) => { const key = getTxtCellKey(now) vMaps[key] = dVal ? '"' : '\n' return key }).replace(/"(.*?)"/g, (text: string, cVal: string) => { const key = getTxtCellKey(now) vMaps[key] = replaceTxtCell(cVal, vMaps) return key }) const cells: string[] = rVal.split(cellSeparator) if (!fields.length) { fields = cells.map((val: string) => toExportField(tableConf, getTxtCellValue(val.trim(), vMaps))) } else { cells.forEach((val, colIndex) => { if (colIndex < fields.length) { item[fields[colIndex]] = getTxtCellValue(val.trim(), vMaps) } }) rows.push(item) } } }) } return { fields, rows } } function parseCsv (tableConf: { fieldMaps: Record titleMaps: Record }, content: string) { return parseCsvAndTxt(tableConf, content, ',') } function parseTxt (tableConf: { fieldMaps: Record titleMaps: Record }, content: string) { return parseCsvAndTxt(tableConf, content, '\t') } function parseHTML (tableConf: { fieldMaps: Record titleMaps: Record }, content: string) { const domParser = new DOMParser() const xmlDoc = domParser.parseFromString(content, 'text/html') const bodyNodes = getElementsByTagName(xmlDoc, 'body') const rows: any[] = [] const fields: string[] = [] if (bodyNodes.length) { const tableNodes = getElementsByTagName(bodyNodes[0], 'table') if (tableNodes.length) { const theadNodes = getElementsByTagName(tableNodes[0], 'thead') if (theadNodes.length) { XEUtils.arrayEach(getElementsByTagName(theadNodes[0], 'tr'), rowNode => { XEUtils.arrayEach(getElementsByTagName(rowNode, 'th'), cellNode => { fields.push(toExportField(tableConf, cellNode.textContent || '')) }) }) const tbodyNodes = getElementsByTagName(tableNodes[0], 'tbody') if (tbodyNodes.length) { XEUtils.arrayEach(getElementsByTagName(tbodyNodes[0], 'tr'), rowNode => { const item: any = {} XEUtils.arrayEach(getElementsByTagName(rowNode, 'td'), (cellNode, colIndex) => { if (fields[colIndex]) { item[fields[colIndex]] = cellNode.textContent || '' } }) rows.push(item) }) } } } } return { fields, rows } } function parseXML (tableConf: { fieldMaps: Record titleMaps: Record }, content: string) { const domParser = new DOMParser() const xmlDoc = domParser.parseFromString(content, 'application/xml') const sheetNodes = getElementsByTagName(xmlDoc, 'Worksheet') const rows: any[] = [] const fields: string[] = [] if (sheetNodes.length) { const tableNodes = getElementsByTagName(sheetNodes[0], 'Table') if (tableNodes.length) { const rowNodes = getElementsByTagName(tableNodes[0], 'Row') if (rowNodes.length) { XEUtils.arrayEach(getElementsByTagName(rowNodes[0], 'Cell'), cellNode => { fields.push(toExportField(tableConf, cellNode.textContent || '')) }) XEUtils.arrayEach(rowNodes, (rowNode, index) => { if (index) { const item: any = {} const cellNodes = getElementsByTagName(rowNode, 'Cell') XEUtils.arrayEach(cellNodes, (cellNode, colIndex) => { if (fields[colIndex]) { item[fields[colIndex]] = cellNode.textContent } }) rows.push(item) } }) } } } return { fields, rows } } function handleImport ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, content: any, opts: any) { const internalData = $xeTable as unknown as TableInternalData const { tableFullColumn, _importResolve, _importReject } = internalData let rest: { fields: string[]; rows: any[]; } = { fields: [], rows: [] } const tableFieldMaps: Record = {} const tableTitleMaps: Record = {} tableFullColumn.forEach((column) => { const field = column.field const title = column.getTitle() if (field) { tableFieldMaps[field] = column } if (title) { tableTitleMaps[column.getTitle()] = column } }) const tableConf = { fieldMaps: tableFieldMaps, titleMaps: tableTitleMaps } switch (opts.type) { case 'csv': rest = parseCsv(tableConf, content) break case 'txt': rest = parseTxt(tableConf, content) break case 'html': rest = parseHTML(tableConf, content) break case 'xml': rest = parseXML(tableConf, content) break } const { fields, rows } = rest const status = fields.some(field => tableFieldMaps[field] || tableTitleMaps[field]) if (status) { $xeTable.createData(rows) .then((data: any) => { let loadRest if (opts.mode === 'insert' || opts.mode === 'insertBottom') { loadRest = $xeTable.insertAt(data, -1) } if (opts.mode === 'insertTop') { loadRest = $xeTable.insert(data) } else { loadRest = $xeTable.reloadData(data) } if (opts.message !== false) { // 检测弹窗模块 if (!VxeUI.modal) { errLog('vxe.error.reqModule', ['Modal']) } VxeUI.modal.message({ content: getI18n('vxe.table.impSuccess', [rows.length]), status: 'success' }) } return loadRest.then(() => { if (_importResolve) { _importResolve({ status: true }) } }) }) } else if (opts.message !== false) { // 检测弹窗模块 if (!VxeUI.modal) { errLog('vxe.error.reqModule', ['Modal']) } VxeUI.modal.message({ content: getI18n('vxe.error.impFields'), status: 'error' }) if (_importReject) { _importReject({ status: false }) } } } function handleFileImport ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, file: any, opts: any) { const internalData = $xeTable as unknown as TableInternalData const importOpts = $xeTable.computeImportOpts const { importMethod, afterImportMethod } = opts const { type, filename } = parseFile(file) // 检查类型,如果为自定义导出,则不需要校验类型 if (!importMethod && !XEUtils.includes(XEUtils.keys(importOpts._typeMaps), type)) { if (opts.message !== false) { // 检测弹窗模块 if (!VxeUI.modal) { errLog('vxe.error.reqModule', ['Modal']) } VxeUI.modal.message({ content: getI18n('vxe.error.notType', [type]), status: 'error' }) } const params = { status: false } return Promise.reject(params) } const rest = new Promise((resolve, reject) => { const _importResolve = (params: any) => { resolve(params) ;($xeTable as any)._importResolve = null ;($xeTable as any)._importReject = null } const _importReject = (params: any) => { reject(params) ;($xeTable as any)._importResolve = null ;($xeTable as any)._importReject = null } ;($xeTable as any)._importResolve = _importResolve ;($xeTable as any)._importReject = _importReject if (window.FileReader) { const options = Object.assign({ mode: 'insertTop' }, opts, { type, filename }) if (options.remote) { if (importMethod) { Promise.resolve(importMethod({ file, options, $table: $xeTable })).then(() => { _importResolve({ status: true }) }).catch(() => { _importResolve({ status: true }) }) } else { _importResolve({ status: true }) } } else { const { tableFullColumn } = internalData $xeTable.preventEvent(null, 'event.import', { file, options, columns: tableFullColumn }, () => { const reader = new FileReader() reader.onerror = () => { errLog('vxe.error.notType', [type]) _importReject({ status: false }) } reader.onload = (e: any) => { handleImport($xeTable, e.target.result, options) } reader.readAsText(file, options.encoding || 'UTF-8') }) } } else { // 不支持的浏览器 errLog('vxe.error.notExp') _importResolve({ status: true }) } }) return rest.then(() => { if (afterImportMethod) { afterImportMethod({ status: true, options: opts, $table: $xeTable }) } }).catch((e) => { if (afterImportMethod) { afterImportMethod({ status: false, options: opts, $table: $xeTable }) } return Promise.reject(e) }) } function handleCloseExport () { if (VxeUI.modal) { return VxeUI.modal.close('VXE_EXPORT_MODAL') } return Promise.resolve() } function handleFilterColumns (exportOpts: VxeTablePropTypes.ExportConfig, column: VxeTableDefines.ColumnInfo, columns: VxeTableDefines.ColumnInfo[] | VxeTablePropTypes.ExportOrPrintColumnOption[]) { return columns.some((item: any) => { if (isColumnInfo(item)) { return column.id === (item as any).id } else if (XEUtils.isString(item)) { return column.field === item } else { const colid = item.id || item.colId const type = item.type const field = item.field if (colid) { return column.id === colid } else if (field && type) { return column.field === field && column.type === type } else if (field) { return column.field === field } else if (type) { return column.type === type } } return false }) } function handleFilterFields (exportOpts: VxeTablePropTypes.ExportConfig, column: VxeTableDefines.ColumnInfo, includeFields: string[] | undefined, excludeFields: string[] | undefined) { if (excludeFields) { if (XEUtils.includes(excludeFields, column.field)) { return false } } if (includeFields) { if (XEUtils.includes(includeFields, column.field)) { return true } return false } return exportOpts.original ? !!column.field : defaultFilterExportColumn(column) } function handleExportAndPrint ($xeTable: VxeTableConstructor, options: VxeTablePropTypes.ExportOpts | VxeTablePropTypes.ExportConfig, isPrint?: any) { const props = $xeTable const reactData = $xeTable as unknown as TableReactData const internalData = $xeTable as unknown as TableInternalData const $xeGrid = $xeTable.$xeGrid const { treeConfig, showHeader, showFooter } = props const { initStore, isGroup, footerTableData, exportStore, exportParams } = reactData const { collectColumn, mergeBodyList, mergeFooterList } = internalData const exportOpts = $xeTable.computeExportOpts const hasTree = treeConfig const customOpts = $xeTable.computeCustomOpts const selectRecords = $xeTable.getCheckboxRecords() const proxyOpts = $xeGrid ? $xeGrid.computeProxyOpts : {} const hasFooter = !!footerTableData.length const hasMerge = !!(mergeBodyList.length || mergeFooterList.length) const defOpts = Object.assign({ message: true, isHeader: showHeader, isTitle: showHeader, isFooter: showFooter, isColgroup: isGroup, isMerge: hasMerge, useStyle: true, current: 'current', modes: (proxyOpts.ajax && proxyOpts.ajax.queryAll ? ['all'] : []).concat(['current', 'selected', 'empty']) }, options) const types = defOpts.types || XEUtils.keys(exportOpts._typeMaps) const modes = defOpts.modes || [] const checkMethod = customOpts.checkMethod const exportColumns = collectColumn.slice(0) const { columns, excludeFields, includeFields } = defOpts // 处理类型 const typeList = types.map((value) => { return { value, label: getI18n(`vxe.export.types.${value}`) } }) const modeList = modes.map((item: any) => { if (item && item.value) { return { value: item.value, label: item.label || item.value } } return { value: item, label: getI18n(`vxe.export.modes.${item}`) } }) // 默认选中 XEUtils.eachTree(exportColumns, (column, index, items, path, parent) => { const isColGroup = column.children && column.children.length > 0 let isChecked = false if (columns && columns.length) { isChecked = handleFilterColumns(defOpts, column, columns) } else if (excludeFields || includeFields) { isChecked = handleFilterFields(defOpts, column, includeFields, excludeFields) } else { isChecked = column.visible && (isColGroup || defaultFilterExportColumn(column)) } column.checked = isChecked column.halfChecked = false column.disabled = (parent && parent.disabled) || (checkMethod ? !checkMethod({ $table: $xeTable, column }) : false) }) // 更新条件 Object.assign(exportStore, { columns: exportColumns, typeList, modeList, hasFooter, hasMerge, hasTree, isPrint, hasColgroup: isGroup, visible: true }) // 默认参数 Object.assign(exportParams, { mode: selectRecords.length ? 'selected' : 'current' }, defOpts) const { filename, sheetName, mode, type } = exportParams if (filename) { if (XEUtils.isFunction(filename)) { exportParams.filename = filename({ options: defOpts, $table: $xeTable, $grid: $xeGrid }) } else { exportParams.filename = `${filename}` } } if (sheetName) { if (XEUtils.isFunction(sheetName)) { exportParams.sheetName = sheetName({ options: defOpts, $table: $xeTable, $grid: $xeGrid }) } else { exportParams.sheetName = `${sheetName}` } } if (!modeList.some(item => item.value === mode)) { exportParams.mode = modeList[0].value } if (!typeList.some(item => item.value === type)) { exportParams.type = typeList[0].value } initStore.export = true return $xeTable.$nextTick() } const getConvertColumns = (columns: any[]) => { const result: any[] = [] columns.forEach((column) => { if (column.childNodes && column.childNodes.length) { result.push(column) result.push(...getConvertColumns(column.childNodes)) } else { result.push(column) } }) return result } const convertToRows = (originColumns: any[]) => { let maxLevel = 1 const traverse = (column: any, parent?: any) => { if (parent) { column._level = parent._level + 1 if (maxLevel < column._level) { maxLevel = column._level } } if (column.childNodes && column.childNodes.length) { let colSpan = 0 column.childNodes.forEach((subColumn: any) => { traverse(subColumn, column) colSpan += subColumn._colSpan }) column._colSpan = colSpan } else { column._colSpan = 1 } } originColumns.forEach((column) => { column._level = 1 traverse(column) }) const rows: any[] = [] for (let i = 0; i < maxLevel; i++) { rows.push([]) } const allColumns = getConvertColumns(originColumns) allColumns.forEach((column) => { if (column.childNodes && column.childNodes.length) { column._rowSpan = 1 } else { column._rowSpan = maxLevel - column._level + 1 } rows[column._level - 1].push(column) }) return rows } export default { methods: { /** * 导出文件,支持 csv/html/xml/txt * 如果是树表格,则默认是导出所有节点 * 如果是启用了虚拟滚动,则只能导出数据源,可以配合 dataFilterMethod 函数转换数据 * @param {Object} options 参数 */ _exportData (options?: VxeTablePropTypes.ExportConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const props = $xeTable const reactData = $xeTable as unknown as TableReactData const internalData = $xeTable as unknown as TableInternalData const $xeGrid = $xeTable.$xeGrid const $xeGantt = $xeTable.$xeGantt const { treeConfig, showHeader, showFooter } = props const { isGroup } = reactData const { tableFullColumn, afterFullData, afterTreeFullData, collectColumn, mergeBodyList, mergeFooterList } = internalData const exportOpts = $xeTable.computeExportOpts const treeOpts = $xeTable.computeTreeOpts const proxyOpts = $xeGrid ? $xeGrid.computeProxyOpts : {} const hasMerge = !!(mergeBodyList.length || mergeFooterList.length) const opts = Object.assign({ message: true, isHeader: showHeader, isTitle: showHeader, isFooter: showFooter, isColgroup: isGroup, isMerge: hasMerge, useStyle: true, current: 'current', modes: (proxyOpts.ajax && proxyOpts.ajax.queryAll ? ['all'] : []).concat(['current', 'selected', 'empty']), download: true, type: 'csv' // filename: '', // sheetName: '', // original: false, // isAllExpand: false, // data: null, // remote: false, // dataFilterMethod: null, // footerFilterMethod: null, // exportMethod: null, // columnFilterMethod: null, // beforeExportMethod: null, // afterExportMethod: null }, exportOpts, options) let { filename, sheetName, type, mode, columns, original, columnFilterMethod, beforeExportMethod, includeFields, excludeFields } = opts let groups: any[] = [] const selectRecords = $xeTable.getCheckboxRecords() if (!mode) { mode = selectRecords.length ? 'selected' : 'current' } let isCustomCol = false let customCols = [] if (columns && columns.length) { isCustomCol = true customCols = columns } else { customCols = XEUtils.searchTree(collectColumn, column => { const isColGroup = column.children && column.children.length > 0 let isChecked = false if (columns && columns.length) { isChecked = handleFilterColumns(opts, column, columns) } else if (excludeFields || includeFields) { isChecked = handleFilterFields(opts, column, includeFields, excludeFields) } else { isChecked = column.visible && (isColGroup || defaultFilterExportColumn(column)) } return isChecked }, { children: 'children', mapChildren: 'childNodes', original: true }) } const handleOptions: VxeTablePropTypes.ExportHandleOptions = Object.assign({ } as { data: any[], colgroups: any[], columns: any[] }, opts, { filename: '', sheetName: '' }) // 如果设置源数据,则默认导出设置了字段的列 if (!isCustomCol && !columnFilterMethod) { columnFilterMethod = ({ column }) => { if (excludeFields) { if (XEUtils.includes(excludeFields, column.field)) { return false } } if (includeFields) { if (XEUtils.includes(includeFields, column.field)) { return true } return false } return original ? !!column.field : defaultFilterExportColumn(column) } handleOptions.columnFilterMethod = columnFilterMethod } if (customCols) { handleOptions._isCustomColumn = true groups = XEUtils.searchTree( XEUtils.mapTree(customCols, (item) => { let targetColumn if (item) { if (isColumnInfo(item)) { targetColumn = item } else if (XEUtils.isString(item)) { targetColumn = $xeTable.getColumnByField(item) } else { const colid = item.id || item.colId const type = item.type const field = item.field if (colid) { targetColumn = $xeTable.getColumnById(colid) } else if (field && type) { targetColumn = tableFullColumn.find((column: VxeTableDefines.ColumnInfo) => column.field === field && column.type === type) } else if (field) { targetColumn = $xeTable.getColumnByField(field) } else if (type) { targetColumn = tableFullColumn.find((column: VxeTableDefines.ColumnInfo) => column.type === type) } } return targetColumn || {} } }, { children: 'childNodes', mapChildren: '_children' }), (column, index) => isColumnInfo(column) && (!columnFilterMethod || columnFilterMethod({ $table: $xeTable, column: column as any, $columnIndex: index })), { children: '_children', mapChildren: 'childNodes', original: true } ) } else { groups = XEUtils.searchTree(isGroup ? collectColumn : tableFullColumn, (column, index) => column.visible && (!columnFilterMethod || columnFilterMethod({ $table: $xeTable, column, $columnIndex: index })), { children: 'children', mapChildren: 'childNodes', original: true }) } // 获取所有列 const cols: VxeTableDefines.ColumnInfo[] = [] XEUtils.eachTree(groups, column => { const isColGroup = column.children && column.children.length if (!isColGroup) { cols.push(column) } }, { children: 'childNodes' }) // 构建分组层级 handleOptions.columns = cols handleOptions.colgroups = convertToRows(groups) if (filename) { if (XEUtils.isFunction(filename)) { handleOptions.filename = filename({ options: opts, $table: $xeTable, $grid: $xeGrid }) } else { handleOptions.filename = `${filename}` } } if (!handleOptions.filename) { handleOptions.filename = getI18n(handleOptions.original ? 'vxe.table.expOriginFilename' : 'vxe.table.expFilename', [XEUtils.toDateString(Date.now(), 'yyyyMMddHHmmss')]) } if (sheetName) { if (XEUtils.isFunction(sheetName)) { handleOptions.sheetName = sheetName({ options: opts, $table: $xeTable, $grid: $xeGrid }) } else { handleOptions.sheetName = `${sheetName}` } } if (!handleOptions.sheetName) { handleOptions.sheetName = document.title || '' } // 检查类型,如果为自定义导出,则不需要校验类型 if (!handleOptions.exportMethod && !XEUtils.includes(XEUtils.keys(exportOpts._typeMaps), type)) { errLog('vxe.error.notType', [type]) if (['xlsx', 'pdf'].includes(type)) { warnLog('vxe.error.reqPlugin', [4, 'plugin-export-xlsx']) } const params = { status: false } return Promise.reject(params) } if (!handleOptions.print) { if (beforeExportMethod) { beforeExportMethod({ options: handleOptions, $table: $xeTable, $grid: $xeGrid }) } } if (!handleOptions.data) { handleOptions.data = [] if (mode === 'selected') { if (['html', 'pdf'].indexOf(type) > -1 && treeConfig) { handleOptions.data = XEUtils.searchTree($xeTable.getTableData().fullData, item => $xeTable.findRowIndexOf(selectRecords, item) > -1, Object.assign({}, treeOpts, { data: '_row' })) } else { handleOptions.data = selectRecords } } else if (mode === 'all') { if (!$xeGrid) { errLog('vxe.error.errProp', ['all', 'mode=current,selected']) } if ($xeGrid && !handleOptions.remote) { const gridReactData = $xeGrid as unknown as GridReactData const proxyOpts = $xeGrid.computeProxyOpts const { sortData } = gridReactData const { beforeQueryAll, afterQueryAll, ajax = {} } = proxyOpts const resConfigs = proxyOpts.response || proxyOpts.props || {} const ajaxMethods = ajax.queryAll const queryAllSuccessMethods = ajax.queryAllSuccess const queryAllErrorMethods = ajax.queryAllError if (!ajaxMethods) { errLog('vxe.error.notFunc', ['proxy-config.ajax.queryAll']) } if (ajaxMethods) { const params = { $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, sort: sortData.length ? sortData[0] : {} as any, sorts: sortData as any[], filters: gridReactData.filterData, form: gridReactData.formData, options: handleOptions } return Promise.resolve((beforeQueryAll || ajaxMethods)(params)) .then(rest => { const listProp = resConfigs.list handleOptions.data = (listProp ? (XEUtils.isFunction(listProp) ? listProp({ data: rest, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }) : XEUtils.get(rest, listProp)) : rest) || [] if (afterQueryAll) { afterQueryAll(params) } if (queryAllSuccessMethods) { queryAllSuccessMethods({ ...params, response: rest }) } return handleExport($xeTable, handleOptions) }) .catch((rest) => { if (queryAllErrorMethods) { queryAllErrorMethods({ ...params, response: rest }) } }) } } } else if (mode === 'current') { handleOptions.data = treeConfig ? afterTreeFullData : afterFullData } } else { handleOptions._isCustomData = true } return handleExport($xeTable, handleOptions) }, _importByFile (file: File, options: VxeTablePropTypes.ImportConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const opts = Object.assign({}, options) const { beforeImportMethod } = opts if (beforeImportMethod) { beforeImportMethod({ options: opts, $table: $xeTable }) } return handleFileImport($xeTable, file, opts) }, _importData (options?: VxeTablePropTypes.ImportConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const { importOpts } = this const opts = Object.assign({ types: XEUtils.keys(importOpts._typeMaps) // beforeImportMethod: null, // afterImportMethod: null }, importOpts, options) const { beforeImportMethod, afterImportMethod } = opts if (beforeImportMethod) { beforeImportMethod({ options: opts, $table: this }) } return VxeUI.readFile(opts).catch(e => { if (afterImportMethod) { afterImportMethod({ status: false, options: opts, $table: $xeTable }) } return Promise.reject(e) }).then((params) => { const { file } = params return handleFileImport($xeTable, file, opts) }) }, _saveFile (options: { filename: string type: string content: string | Blob }) { return VxeUI.saveFile(options) }, _readFile (options?: { multiple?: boolean types?: string[] message?: boolean }) { return VxeUI.readFile(options) }, _print (options?: VxeTablePropTypes.PrintConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const $xeGrid = $xeTable.$xeGrid const opts = Object.assign({ original: false // beforePrintMethod }, this.printOpts, options, { type: 'html', download: false, remote: false, print: true }) const { sheetName } = opts let printTitle = '' if (sheetName) { if (XEUtils.isFunction(sheetName)) { printTitle = sheetName({ options: opts, $table: $xeTable, $grid: $xeGrid }) } else { printTitle = `${sheetName}` } } if (!printTitle) { printTitle = document.title || '' } const beforePrintMethod = opts.beforePrintMethod const tableHtml = opts.html || opts.content return new Promise((resolve, reject) => { if (VxeUI.print) { if (tableHtml) { resolve( VxeUI.print({ title: printTitle, html: tableHtml, customStyle: opts.style, beforeMethod: beforePrintMethod ? ({ html }) => { return beforePrintMethod({ html, content: html, options: opts, $table: $xeTable }) } : undefined }) ) } else { resolve( $xeTable.exportData(opts).then(({ content }: any) => { return VxeUI.print({ title: printTitle, html: content, customStyle: opts.style, beforeMethod: beforePrintMethod ? ({ html }) => { return beforePrintMethod({ html, content: html, options: opts, $table: $xeTable }) } : undefined }) }) ) } } else { const e = { status: false } reject(e) } }) }, _getPrintHtml (options: VxeTablePropTypes.PrintConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const printOpts = $xeTable.computePrintOpts const opts = Object.assign({ original: false // beforePrintMethod }, printOpts, options, { type: 'html', download: false, remote: false, print: true }) return $xeTable.exportData(opts).then(({ content }: any) => { return { html: content } }) }, _closeImport () { if (VxeUI.modal) { return VxeUI.modal.close('VXE_IMPORT_MODAL') } return Promise.resolve() }, _openImport (options?: VxeTablePropTypes.ImportConfig) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const props = $xeTable const reactData = $xeTable as unknown as TableReactData const { treeConfig, importConfig } = props const { initStore, importStore, importParams } = reactData const importOpts = $xeTable.computeImportOpts const defOpts = Object.assign({ mode: 'insertTop', message: true, types: XEUtils.keys(importOpts._typeMaps), modes: ['insertTop', 'covering'] }, importOpts, options) const types = defOpts.types || [] const modes = defOpts.modes || [] const isTree = !!treeConfig if (isTree) { if (defOpts.message) { VxeUI.modal.message({ content: getI18n('vxe.error.treeNotImp'), status: 'error' }) } return } if (!importConfig) { errLog('vxe.error.reqProp', ['import-config']) } // 处理类型 const typeList = types.map((value: any) => { return { value, label: getI18n(`vxe.export.types.${value}`) } }) const modeList = modes.map((item: any) => { if (item && item.value) { return { value: item.value, label: item.label || item.value } } return { value: item, label: getI18n(`vxe.import.modes.${item}`) } }) Object.assign(importStore, { file: null, type: '', filename: '', modeList, typeList, visible: true }) Object.assign(importParams, defOpts) if (!modeList.some(item => item.value === importParams.mode)) { importParams.mode = modeList[0].value } initStore.import = true }, _closeExport: handleCloseExport, _openExport (options: any) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const props = $xeTable const exportOpts = $xeTable.computeExportOpts const defOpts = Object.assign({ message: true, types: XEUtils.keys(exportOpts._typeMaps) }, exportOpts, options) if (!props.exportConfig) { errLog('vxe.error.reqProp', ['export-config']) } return handleExportAndPrint($xeTable, defOpts) }, _closePrint: handleCloseExport, _openPrint (options: any) { const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods const props = $xeTable const printOpts = $xeTable.computePrintOpts const defOpts = Object.assign({ message: true }, printOpts, options) if (!props.printConfig) { errLog('vxe.error.reqProp', ['print-config']) } return handleExportAndPrint($xeTable, defOpts, true) } } as any } as any