瀏覽代碼

导出表格当前的数据

yuliang 3 年之前
父節點
當前提交
a5541304cb
共有 3 個文件被更改,包括 420 次插入2 次删除
  1. 2 1
      package.json
  2. 28 1
      src/controls/grid.js
  3. 390 0
      src/xlsx.ts

+ 2 - 1
package.json

@@ -50,6 +50,7 @@
     "recast": "^0.20.4",
     "jquery": "^3.6.0",
     "json5": "^2.2.0",
-    "lay-excel": "^1.7.5"
+    "lay-excel": "^1.7.5",
+    "xlsx": "^0.17.4"
   }
 }

+ 28 - 1
src/controls/grid.js

@@ -305,6 +305,21 @@ export default function () {
             }
         },
 
+        exportCurrentExcelClick() {
+            const me = this
+            const {config} = me
+            const scope = lookupScope(me)
+            let excelFileName = config.excelFileName || scope.vjson.title || _.uniqueId("excel-")
+            if (excelFileName.endsWith(".xlsx")) {
+                excelFileName = excelFileName.split(".xlsx")[0]
+            }
+            excelFileName += ".xlsx"
+            const rowsAll = this.getStore().getData().items?.map(r => r.data);
+            const excelData = me.makeExcelData(rowsAll)
+            LAY_EXCEL.exportExcel(excelData, excelFileName, 'xlsx')
+
+        },
+
         exportExcelClick(excelExportParams) {
             const me = this
             const {config} = me
@@ -567,6 +582,14 @@ export default function () {
                     width: 300,
                     docked: 'left',
                     items: [{
+                        text: '导出表格当前的数据',
+                        iconCls: 'x-fa fa-download',
+                        listeners: {
+                            click: (sender, value) => {
+                                grid.exportCurrentExcelClick()
+                            }
+                        }
+                    }, {
                         xtype: "textfield",
                         fieldLabel: '当前导出页',
                         maskRe: /[0-9]/,
@@ -606,6 +629,11 @@ export default function () {
                                     v = defaultGrid.exportExcelPageSize
                                     sender.setValue(v);
                                 }
+                                if (v >= 10000) {
+                                    window['system'].msg("导出页大小不能大于10000")
+                                    v = 10000
+                                    sender.setValue(v);
+                                }
                                 let page = parseInt(grid.exportExcelCurrentPage)
                                 const total = parseInt(grid.exportExcelTotal)
                                 if (page > total / v) {
@@ -621,7 +649,6 @@ export default function () {
                         value: grid.exportExcelTotal,
                         readOnly: true
                     }, {
-
                         text: '导出',
                         iconCls: 'x-fa fa-download',
                         listeners: {

+ 390 - 0
src/xlsx.ts

@@ -0,0 +1,390 @@
+// import XLSX from 'xlsx'
+// import _ from 'lodash'
+// // import ExcelImportDialog from "./renderers/excelImportDialog";
+// import * as path from "path";
+// // import {clearLoading, loading} from "./renderers";
+// // @ts-ignore
+// // import XLSX_EXPORT from 'xlsx_export';
+// // @ts-ignore
+// const XLSX_EXPORT = window.LAY_EXCEL
+//
+// export {XLSX};
+// export {XLSX_EXPORT}
+//
+// export function readExcel(file: File): Promise<any> {
+//     return new Promise((resolve, reject) => {
+//         var reader = new FileReader();
+//         reader.onload = function (e) {
+//             if (e.target) {
+//                 const data = e.target.result;
+//                 const workbook = XLSX.read(data, {type: 'binary'});
+//                 resolve(workbook);
+//             } else {
+//                 reject("文件读取失败!!!")
+//             }
+//         };
+//         try {
+//             reader.readAsBinaryString(file);
+//         } catch (e) {
+//             reject(e)
+//         }
+//     })
+// }
+//
+// export declare interface ErrorMsgDataItem {
+//     /** 错误id 由allData 的__importID__字段值 + "_" + field的值 表示哪一行的哪一列出错,行校验错误时候直接为allData 的__importID__ + "" */
+//     errorId: string
+//     /** 对应数据行 allData 的__importID__字段值 */
+//     importID: number
+//     /** 字段 */
+//     field: string
+//     /** 字段值 */
+//     title: string
+//     /** 字段值 */
+//     value: string
+//     /** 错误信息 */
+//     errormessage: string
+// }
+//
+// export declare interface ImportResult {
+//     /** 所有导入的数据,如果有字典会格式化到字典,格式化错误的保持原始值 */
+//     allData: any[]
+//     /** 导入正确的数据 */
+//     okData: any[]
+//     /** 导入错误的数据 */
+//     errorData: any[]
+//     /** 导入错误数据的错误明细 */
+//     errorMsgData: ErrorMsgDataItem[]
+// }
+//
+// /** 定义参数列 */
+// export declare interface Field {
+//     /** 字段 */
+//     dataIndex: string
+//     /** 字段名 */
+//     header: string
+//     /** 校验方法,校验通过返回true, 否则返回错误信息 会记录到错误列表里面 errorMsgData */
+//     validate?: ((v: ValidateObject) => true | string) | string
+//     /** 格式化,表格显示 兼容默认弹出框表格的formatter */
+//     fix?: ((v: any) => any) | string
+//     /** 导入格式化 返回null或者undefined 表示格式化错误,会记录到错误列表里面 errorMsgData */
+//     importFormatter?: ((v: ValidateObject) => null | undefined | string | number) | string
+//     /** 字典, 参与数据校验和表格显示格式化, 校验不通过的,会记录到错误列表里面 errorMsgData 同时兼容默认弹出框表格的字典 */
+//     data?: any | { id: string | number, text: string }
+// }
+//
+// /** 格式化及校验的参数 */
+// export declare interface ValidateObject {
+//     field: Field
+//     ov: any,
+//     nv: any,
+//     rowIndex: number,
+//     data: any,
+//     rowDatas: any[],
+// }
+//
+// /** 行数据校验的参数 */
+// export declare interface RowValidateObject {
+//     fields: Field[],
+//     data: any,
+//     rowIndex: number,
+//     rowDatas: any[]
+// }
+//
+// /**
+//  * 定义切口,可以在外部修改数据
+//  */
+// export declare type AfterClientValidate =
+//     ((importResult: ImportResult, resolve: (value?: (ImportResult | PromiseLike<ImportResult> | undefined)) => void) => ImportResult)
+//     | undefined
+//
+// export function readExcelWithFieldSet(file: File,
+//                                       fieldSet: any[],
+//                                       dataStartRow: number = 2,
+//                                       titleRowNumber: number = 1,
+//                                       rowValidate: ((rv: RowValidateObject) => true | string) | undefined = undefined,
+//                                       otherValidate: AfterClientValidate = undefined,
+//                                       fieldValidate: ((fields: Field[], columnTitles: string[]) => boolean) | undefined = undefined): Promise<ImportResult> {
+//     return new Promise<ImportResult>((resolve, reject) => {
+//         setTimeout(()=>{
+//             readExcel(file).then(workbook => {
+//                 const sheetNames = workbook.SheetNames; // 工作表名称集合
+//                 const worksheet = workbook.Sheets[sheetNames[0]]; // 这里我们只读取第一张sheet
+//                 const titleRowValue: any[] = [];
+//                 const titleRowKey: any[] = [];
+//                 let needLoop: boolean = false; // 是否需要迭代
+//                 let fields: any[] = [];
+//                 if (worksheet && worksheet["!ref"]) {
+//                     const t = worksheet["!ref"];
+//                     const tempArr = t.split(':');
+//                     if (tempArr.length >= 2) {
+//                         let firstRowNumber = tempArr[0].replace(/[^0-9]/ig, "");
+//
+//                         // 选取title的行,删除之前的行
+//                         if (titleRowNumber > firstRowNumber) {
+//                             for (let i = firstRowNumber; i < titleRowNumber; i++) {
+//                                 for (const key in worksheet) {
+//                                     if (key.endsWith(i) && key.replace(/[^0-9]/ig, "") === i) {
+//                                         delete worksheet[key];
+//                                     }
+//                                 }
+//                             }
+//                             const fc = tempArr[0].replace(/[^a-z]/ig, "");
+//                             firstRowNumber = titleRowNumber + '';
+//                             worksheet["!ref"] = fc+firstRowNumber + ":" + tempArr[1]
+//                         }
+//                         const lastRowNumber = tempArr[1].replace(/[^0-9]/ig, "");
+//                         // const firstColNumber = tempArr[0].split(''+firstRowNumber)[0];
+//                         // const lastColNumber = tempArr[1].split(''+lastRowNumber)[0];
+//                         for (const key in worksheet) {
+//                             if (key.endsWith(firstRowNumber) && key.replace(/[^0-9]/ig, "") === firstRowNumber) {
+//                                 titleRowKey.push(key)
+//                                 titleRowValue.push(worksheet[key].v)
+//                             }
+//                         }
+//                         if (fieldSet && fieldSet.length > 0) {
+//                             let length = fieldSet.length;
+//                             length = length <= titleRowKey.length ? length : titleRowKey.length;
+//                             for (let i = 0; i < length; i++) {
+//                                 const vk = titleRowKey[i];
+//                                 const item = fieldSet[i];
+//                                 const field: any = {}
+//                                 if (typeof item === 'string') {
+//                                     field.field = item;
+//                                     field.title = worksheet[vk].v;
+//                                     worksheet[vk] = {t: "s", v: item, h: item, w: item}
+//                                 } else if (typeof item.field === "string") {
+//                                     field.field = item.field;
+//                                     field.title = worksheet[vk].v;
+//                                     worksheet[vk] = {t: "s", v: item.field, h: item.field, w: item.field};
+//                                     if (typeof item.validate === "function") {
+//                                         field.validate = item.validate
+//                                         needLoop = true;
+//                                     }
+//                                     if (typeof item.importFormatter === "function") {
+//                                         field.importFormatter = item.importFormatter
+//                                         needLoop = true;
+//                                     }
+//                                     if ((item.data instanceof Array && item.data.length > 0)
+//                                         || _.isPlainObject(item.data) && Object.keys(item.data).length > 0) {
+//                                         field.data = item.data
+//                                         needLoop = true;
+//                                     }
+//                                 }
+//                                 fields.push(field)
+//                             }
+//                         }
+//                     }
+//                 }
+//
+//                 if (fieldValidate && typeof fieldValidate === "function") {
+//                     if (fieldValidate(fields, titleRowValue) !== true) {
+//                         reject("fields validate error");
+//                         return
+//                     }
+//                 }
+//
+//                 const allData = XLSX.utils.sheet_to_json(worksheet);
+//                 let okData: any[] = [], errorData: any[] = [], errorMsgData: ErrorMsgDataItem[] = [];
+//                 const needItemLoop = needLoop;
+//                 if (rowValidate && typeof rowValidate === "function") {
+//                     needLoop = true;
+//                 }
+//                 if (needLoop === true) {
+//                     for (let index = 0; index < allData.length; index++) {
+//                         const row: any = allData[index];
+//                         const rowNumber = dataStartRow + index;
+//                         row.__importID__ = rowNumber
+//                         let isRowOk: boolean = true;
+//                         if (needItemLoop) {
+//                             for (let num = 0; num < fields.length; num++) {
+//                                 const field = fields[num];
+//                                 const ov = row[field.field];
+//                                 let nv: any = ov;
+//
+//                                 let hasError: boolean = false;
+//                                 const rowNumber = dataStartRow + index;
+//                                 const ei: ErrorMsgDataItem = {
+//                                     errorId: rowNumber + '_' + field.field,
+//                                     importID: rowNumber,
+//                                     field: field.field,
+//                                     title: field.title,
+//                                     value: ov,
+//                                     errormessage: ""
+//                                 }
+//
+//                                 // 格式化
+//                                 if (field.data instanceof Array) {
+//                                     nv = undefined;
+//                                     ei.errormessage = "字典匹配失败"
+//                                     _.forEach(field.data, (v, index) => {
+//                                         if (v.text === ov) {
+//                                             nv = v.id;
+//                                             return
+//                                         }
+//                                     })
+//                                 } else if (_.isPlainObject(field.data)) {
+//                                     nv = undefined;
+//                                     ei.errormessage = "字典匹配失败"
+//                                     _.forEach(field.data, (value, key) => {
+//                                         if (value === ov) {
+//                                             nv = key;
+//                                             return
+//                                         }
+//                                     })
+//                                 }
+//
+//                                 if (field.importFormatter) {
+//                                     const vdata: ValidateObject = {
+//                                         field,
+//                                         ov,
+//                                         nv,
+//                                         rowIndex: index,
+//                                         data: row,
+//                                         rowDatas: allData
+//                                     }
+//                                     nv = undefined;
+//                                     ei.errormessage = "格式化失败";
+//                                     nv = field.importFormatter(vdata)
+//                                 }
+//
+//                                 if (nv === undefined || nv === null) {
+//                                     hasError = true;
+//                                 } else {
+//                                     row[field.field] = nv;
+//                                     ei.errormessage = "";
+//                                 }
+//
+//                                 // 校验
+//                                 if (field.validate) {
+//                                     const vdata: ValidateObject = {
+//                                         field,
+//                                         ov,
+//                                         nv,
+//                                         rowIndex: index,
+//                                         data: row,
+//                                         rowDatas: allData
+//                                     }
+//                                     const errormessage: true | string = field.validate(vdata);
+//                                     if (errormessage === true) {
+//
+//                                     } else {
+//                                         hasError = true;
+//                                         ei.errormessage = ei.errormessage + "/" + errormessage;
+//                                     }
+//                                 }
+//                                 if (hasError === true) {
+//                                     isRowOk = false;
+//                                     errorMsgData.push(ei);
+//                                 }
+//                             }
+//                         }
+//                         if (rowValidate && typeof rowValidate === "function") {
+//                             const errormessage: true | string = rowValidate({
+//                                 fields,
+//                                 data: row,
+//                                 rowIndex: index,
+//                                 rowDatas: allData
+//                             })
+//                             if (errormessage === true) {
+//
+//                             } else {
+//                                 isRowOk = false;
+//
+//                                 const ei: ErrorMsgDataItem = {
+//                                     errorId: rowNumber + '',
+//                                     field: "row",
+//                                     title: "数据行",
+//                                     value: "当前行的数据",
+//                                     importID: rowNumber,
+//                                     errormessage
+//                                 }
+//                                 errorMsgData.push(ei);
+//                             }
+//                         }
+//                         row.__hasError__ = !isRowOk
+//                         if (isRowOk === true) {
+//                             okData.push(row)
+//                         } else {
+//                             errorData.push(row)
+//                         }
+//                     }
+//
+//                 } else {
+//                     // 添加数据的唯一标识
+//                     for (let index = 0; index < allData.length; index++) {
+//                         const row: any = allData[index];
+//                         const rowNumber = dataStartRow + index;
+//                         row.__importID__ = rowNumber;
+//                         row.__hasError__ = false
+//                     }
+//                     okData = allData;
+//                 }
+//                 if (otherValidate && typeof otherValidate === "function") {
+//                     otherValidate({allData, okData, errorData, errorMsgData}, resolve);
+//                 } else {
+//                     resolve({allData, okData, errorData, errorMsgData});
+//                 }
+//                 // clearLoading()
+//             })
+//         }, 50);
+//     })
+// }
+//
+// export declare interface ImportExcelOption {
+//     // 字段设置
+//     fields: Field[],
+//     // 字段校验方法
+//     fieldValidate?: ((fields: Field[], columnTitles: string[]) => boolean) | undefined,
+//     // 数据行校验方法
+//     rowValidate?: ((rv: RowValidateObject) => true | string) | undefined,
+//     // 客户端校验完成后调用
+//     afterClientValidate?: AfterClientValidate,
+//     // Excel起始数据行的的行号 默认给定为2
+//     dataStartRow?: number,
+//     // Excel标题行的的行号 默认给定为1
+//     titleRowNumber?: number,
+//     // 导入完成后调用 回传结果数据
+//     onImportAfter?: (result: ImportResult) => {}
+//     // 隐藏所有数据表格, 默认false
+//     hidAllDataGrid?: boolean,
+//     // 隐藏正确数据表格 默认 false
+//     hidOKDataGrid?: boolean,
+//     // 隐藏错误数据的表格 默认false
+//     hidErrorDataGrid?: boolean,
+//     // 隐藏错误信息表格 默认false
+//     hidErrorMsgGrid?: boolean,
+//     // 所有数据表格的分页大小, 0 为不分页
+//     dataGridPageSize?: 0 | 10 | 20 | 50 | 100 | 200 | 500 | 1000,
+//     // 错误信息表格的分页大小, 0 为不分页
+//     errorMsgGridPageSize?: 0 | 10 | 20 | 50 | 100 | 200 | 500 | 1000,
+//     // 导入模版的名称
+//     templateName?: string,
+//     // 导入模板下载地址
+//     dowLoadUrl?: string,
+//     // 标题
+//     title?: string,
+//     // 弹窗高
+//     height?: string,
+//     // 弹窗宽
+//     width?: string,
+//     // 默认显示的表格
+//     defaultShowData?: "allData" | "okData" | "errorData",
+//     // 自定义错误信息表格的列宽
+//     errMsgGridColWidths?: number[],
+//     // 外部定义的控件
+//     toolBar?: any[],
+//     // 关闭回调
+//     onClose?: string | (() => void)
+// }
+//
+// /**
+//  * 打开excel导入的方法
+//  * @param option 参数配置
+//  * @param sender
+//  */
+// export function importExcel(option: ImportExcelOption, sender: any): ExcelImportDialog {
+//     const dialog = new ExcelImportDialog({option})
+//     dialog.showDialog(sender);
+//     return dialog
+// }