話少說,直入主題
基本思路為 創建一個臨時文件 寫入數據 導出數據 刪除臨時文件
首先需要兩個jar包
antlr和stringtemplate
創建數據庫中的類Row
private String name1; private String name2; private String name3; public String getName1() { return name1; } public void setName1(String name1) { this.name1=name1; } public String getName2() { return name2; } public void setName2(String name2) { this.name2=name2; } public String getName3() { return name3; } public void setName3(String name3) { this.name3=name3; }
然后需要創建對應的Worksheet類
<span style="font-size:10px;">private int columnNum; private int rowNum; private List<Row> rows; private List<SfOrder> orders; public List<SfOrder> getOrders() { return orders; } public void setOrders(List<SfOrder> orders) { this.orders=orders; } public String getSheet() { return sheet; } public void setSheet(String sheet) { this.sheet=sheet; } public List<Row> getRows() { return rows; } public void setRows(List<Row> rows) { this.rows=rows; } public int getColumnNum() { return columnNum; } public void setColumnNum(int columnNum) { this.columnNum=columnNum; } public int getRowNum() { return rowNum; } public void setRowNum(int rowNum) { this.rowNum=rowNum; }</span>
然后需要寫兩個文件分別為
head.st
<?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <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"> <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"> <Created>1996-12-17T01:32:42Z</Created> <LastSaved>2013-08-02T09:21:24Z</LastSaved> <Version>11.9999</Version> </DocumentProperties> <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"> <RemovePersonalInformation/> </OfficeDocumentSettings> <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"> <WindowHeight>4530</WindowHeight> <WindowWidth>8505</WindowWidth> <WindowTopX>480</WindowTopX> <WindowTopY>120</WindowTopY> <AcceptLabelsInFormulas/> <ProtectStructure>False</ProtectStructure> <ProtectWindows>False</ProtectWindows> </ExcelWorkbook> <Styles> <Style ss:ID="Default" ss:Name="Normal"> <Alignment ss:Vertical="Bottom"/> <Borders/> <Font ss:FontName="宋體" x:CharSet="134" ss:Size="12"/> <Interior/> <NumberFormat/> <Protection/> </Style> </Styles> 和body.st $worksheet:{ <Worksheet ss:Name="$it.sheet$"> <Table ss:ExpandedColumnCount="$it.columnNum$" ss:ExpandedRowCount="$it.rowNum$" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25"> $it.rows:{ <Row> <Cell><Data ss:Type="String">$it.name1$</Data></Cell> </Row> }$ </Table> </Worksheet> }$
下面就是程序代碼
首先你要從數據中查詢數據,假設你查詢的數據為List數據
//我這里的list是從map中獲取的你可以直接獲取 List<Row> list=(List<Row>) result.get("list"); list=(List<SfOrder>) result.get("list"); if(list.size()==0){ return null; } long startTimne=System.currentTimeMillis(); StringTemplateGroup stGroup=new StringTemplateGroup("stringTemplate"); stGroup.setFileCharEncoding("UTF-8");//設置編碼,否則有亂碼,導出excel格式不正確 //寫入excel文件頭部信息 StringTemplate head=stGroup.getInstanceOf("/template/head"); String path=request.getSession().getServletContext().getRealPath("/upload/excel/test1.xls"); File file=new File(path); PrintWriter writer=new PrintWriter(new BufferedOutputStream(new FileOutputStream(file))); writer.print(head.toString()); writer.flush(); int sheets=10; //excel單表最大行數是65535 int maxRowNum=3000; int maxRowNumLast=0; //計算要分幾個sheet sheets=(list.size()%maxRowNum==0) ? list.size()/maxRowNum:list.size()/maxRowNum+1; //計算最后一個sheet有多少行 maxRowNumLast=list.size()-(sheets-1)*maxRowNum; 上面為預備階段,下面則直接向文件寫數據 //寫入excel文件數據信息 for(int i=0;i<sheets;i++){ Integer nums=0; StringTemplate body=stGroup.getInstanceOf("/template/body"); Worksheet worksheet=new Worksheet(); worksheet.setSheet(" "+(i+1)+" "); worksheet.setColumnNum(3); worksheet.setRowNum(maxRowNum); List<Row> orders=new ArrayList<Row>(); if(i==(sheets-1)){ for(int j=0;j<maxRowNumLast;j++){ nums=i*maxRowNumLast+j; SfOrder order=new SfOrder(); order.setOrderId(list.get(nums).getOrderId()); orders.add(order); } }else{ for(int j=0;j<maxRowNum;j++){ nums=i*maxRowNum+j; Row order=new Row(); order.setOrderId(list.get(nums).getOrderId()); orders.add(order); } } worksheet.setOrders(orders); body.setAttribute("worksheet", worksheet); writer.print(body.toString()); writer.flush(); orders.clear(); orders=null; worksheet=null; body=null; Runtime.getRuntime().gc(); System.out.println("正在生成excel文件的 sheet"+(i+1)); } 下面則寫入服務器磁盤 //寫入excel文件尾部 writer.print("</Workbook>"); writer.flush(); writer.close(); System.out.println("生成excel文件完成"); long endTime=System.currentTimeMillis(); System.out.println("用時="+((endTime-startTimne)/1000)+"秒"); 下面要讀取文件,即正式的導出 //創建file對象 File upload=new File(path); //設置response的編碼方式 response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition","attachment;filename=ceshi.xls"); //讀出文件到i/o流 FileInputStream fis=new FileInputStream(upload); BufferedInputStream buff=new BufferedInputStream(fis); byte [] b=new byte[1024];//相當于我們的緩存 long k=0;//該值用于計算當前實際下載了多少字節 //從response對象中得到輸出流,準備下載 OutputStream myout=response.getOutputStream(); //開始循環下載 while(k<upload.length()){ int j=buff.read(b,0,1024); k+=j; //將b中的數據寫到客戶端的內存 myout.write(b,0,j); } //將寫入到客戶端的內存的數據,刷新到磁盤 myout.flush(); myout.close(); 如果需要可以,比如這個文件是動態的我們可以讓這個文件是動態的,就要將此文件刪除 File fileDel=new File(path); // 如果文件路徑所對應的文件存在,并且是一個文件,則直接刪除 if (fileDel.exists() && file.isFile()) { fileDel.delete() }
這樣就大功告成啦,導出所用的時間大部分是花費在數據庫查詢中,當然這也是你數據庫功底的問題啦
篇文章主要介紹使用 exceljs、file-saver、jszip實現下載包含多層級文件夾、多個 excel、每個 excel 支持多個 sheet 的 zip 壓縮包。上一篇文章:前端復雜表格導出excel,一鍵導出 Antd Table 看這篇就夠了(附源碼)[1]詳細介紹了如何實現解析 Antd Table、組裝數據和調整表格的樣式,感興趣的可以先看看。本篇將接著上一篇,重點講方法的更高級抽象,和下載多層級文件夾的 zip 壓縮包。源碼地址:github.com/cachecats/e…[2]
實現效果
最終下載的是 壓縮包.zip,解壓之后包含多個文件夾,每個文件夾下又可以無限嵌套子文件夾,excel 文件可以自由選擇放到根目錄下,或者子文件夾下。實現效果如圖:
使用方法
使用方式也很簡單,經過高度封裝后,只需按照方法參數的規則傳入參數即可:
downloadFiles2ZipWithFolder({
zipName: '壓縮包',
folders: [
{
folderName: '文件夾1',
files: [
{
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
]
},
{
folderName: '文件夾2',
files: [
{
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
]
},
{
folderName: '文件夾2/文件夾2-1',
files: [
{
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
]
},
{
folderName: '文件夾2/文件夾2-1/文件夾2-1-1',
files: [
{
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
]
},
{
folderName: '',
files: [
{
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
},
{
sheetName: 'test2',
columns: columns,
dataSource: list
}
]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
]
}
]
})
復制代碼
這里會封裝三個方法,分別滿足不同場景下的導出需求:
一、封裝普通的下載導出 excel 方法
我們來封裝一個常用的,預定義好樣式,直接能開箱即用的導出方法,使用者不用關心具體細節,只管簡單的調用:
function onExportExcel() {
downloadExcel({
filename: 'test',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
})
}
復制代碼
如上,直接調用 downloadExcel方法,它傳入一個對象作為參數,分別有 filename和 sheets兩個屬性。
Sheet對象的定義:
export interface ISheet {
// sheet 的名字
sheetName: string;
// 這個 sheet 中表格的 column,類型同 antd 的 column
columns: ColumnType<any>[];
// 表格的數據
dataSource: any[];
}
復制代碼
核心代碼
downloadExcel方法關鍵源碼:
export interface IDownloadExcel {
filename: string;
sheets: ISheet[];
}
export interface ISheet {
// sheet 的名字
sheetName: string;
// 這個 sheet 中表格的 column,類型同 antd 的 column
columns: ColumnType<any>[];
// 表格的數據
dataSource: any[];
}
/**
* 下載導出簡單的表格
* @param params
*/
export function downloadExcel(params: IDownloadExcel) {
// 創建工作簿
const workbook = new ExcelJs.Workbook();
params?.sheets?.forEach((sheet) => handleEachSheet(workbook, sheet));
saveWorkbook(workbook, `${params.filename}.xlsx`);
}
function handleEachSheet(workbook: Workbook, sheet: ISheet) {
// 添加sheet
const worksheet = workbook.addWorksheet(sheet.sheetName);
// 設置 sheet 的默認行高。設置默認行高跟自動撐開單元格沖突
// worksheet.properties.defaultRowHeight = 20;
// 設置列
worksheet.columns = generateHeaders(sheet.columns);
handleHeader(worksheet);
handleData(worksheet, sheet);
}
export function saveWorkbook(workbook: Workbook, fileName: string) {
// 導出文件
workbook.xlsx.writeBuffer().then((data: any) => {
const blob = new Blob([data], {type: ''});
saveAs(blob, fileName);
});
}
復制代碼
generateHeaders方法是設置表格的列。handleHeader方法負責處理表頭,設置表頭的高度、背景色、字體等樣式。handleData方法處理每一行具體的數據。這三個方法的實現在上篇文章都有介紹,如需了解更多請查看源碼:github.com/cachecats/e…[3]
導出的 excel 效果如下圖,列寬會根據傳入的 width 動態計算,單元格高度會根據內容自動撐開。
二、導出包含多個 excel 的 zip 壓縮包
如果沒有多級目錄的需求,只想把多個 excel 文件打包到一個壓縮包里,可以用 downloadFiles2Zip這個方法,得到的目錄結構如下圖:
參數結構如下,支持導出多個 excel 文件,每個 excel 文件又可以包含多個 sheet。
export interface IDownloadFiles2Zip {
// 壓縮包的文件名
zipName: string;
files: IDownloadExcel[];
}
export interface IDownloadExcel {
filename: string;
sheets: ISheet[];
}
export interface ISheet {
// sheet 的名字
sheetName: string;
// 這個 sheet 中表格的 column,類型同 antd 的 column
columns: ColumnType<any>[];
// 表格的數據
dataSource: any[];
}
復制代碼
使用示例
function onExportZip() {
downloadFiles2Zip({
zipName: '壓縮包',
files: [
{
filename: 'test',
sheets: [
{
sheetName: 'test',
columns: columns,
dataSource: list
},
{
sheetName: 'test2',
columns: columns,
dataSource: list
}
]
},
{
filename: 'test2',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
},
{
filename: 'test3',
sheets: [{
sheetName: 'test',
columns: columns,
dataSource: list
}]
}
]
})
}
復制代碼
核心代碼
通過 handleEachFile()方法處理每個 fille 對象,每個 file 其實就是一個 excel 文件,即一個 workbook。給每個 excel 創建 workbook并將數據寫入,然后通過 JsZip庫寫入到壓縮文件內,最終用 file-saver庫提供的 saveAs方法導出壓縮文件。注意 12、13行,handleEachFile()方法返回的是一個 Promise,需要等所有異步方法都執行完之后再執行下面的生成 zip 方法,否則可能會遺漏文件。
import {saveAs} from 'file-saver';
import * as ExcelJs from 'exceljs';
import {Workbook, Worksheet, Row} from 'exceljs';
import JsZip from 'jszip'
/**
* 導出多個文件為zip壓縮包
*/
export async function downloadFiles2Zip(params: IDownloadFiles2Zip) {
const zip = new JsZip();
// 待每個文件都寫入完之后再生成 zip 文件
const promises = params?.files?.map(async param => await handleEachFile(param, zip, ''))
await Promise.all(promises);
zip.generateAsync({type: "blob"}).then(blob => {
saveAs(blob, `${params.zipName}.zip`)
})
}
async function handleEachFile(param: IDownloadExcel, zip: JsZip, folderName: string) {
// 創建工作簿
const workbook = new ExcelJs.Workbook();
param?.sheets?.forEach((sheet) => handleEachSheet(workbook, sheet));
// 生成 blob
const data = await workbook.xlsx.writeBuffer();
const blob = new Blob([data], {type: ''});
if (folderName) {
zip.folder(folderName)?.file(`${param.filename}.xlsx`, blob)
} else {
// 寫入 zip 中一個文件
zip.file(`${param.filename}.xlsx`, blob);
}
}
function handleEachSheet(workbook: Workbook, sheet: ISheet) {
// 添加sheet
const worksheet = workbook.addWorksheet(sheet.sheetName);
// 設置 sheet 的默認行高。設置默認行高跟自動撐開單元格沖突
// worksheet.properties.defaultRowHeight = 20;
// 設置列
worksheet.columns = generateHeaders(sheet.columns);
handleHeader(worksheet);
handleDataWithRender(worksheet, sheet);
}
復制代碼
render 渲染的單元格處理
數據處理還有一點需要注意,因為有的單元格是通過 render 函數渲染的,render 函數里可能進行了一系列復雜的計算,所以如果 column 中有 render 的話不能直接以 dataIndex 為 key 進行取值,要拿到 render 函數執行后的值才是正確的。比如 Table 的 columns 如下:
const columns: ColumnsType<any> = [
{
width: 50,
dataIndex: 'id',
key: 'id',
title: 'ID',
render: (text, row) => <div><p>{row.id + 20}</p></div>,
},
{
width: 100,
dataIndex: 'name',
key: 'name',
title: '姓名',
},
{
width: 50,
dataIndex: 'age',
key: 'age',
title: '年齡',
},
{
width: 80,
dataIndex: 'gender',
key: 'gender',
title: '性別',
},
];
復制代碼
第一列傳入了 render 函數 render: (text, row)=> <div><p>{row.id + 20}</p></div>,經過計算后,ID 列顯示的值應該是原來的 id + 20。構造的數據原來的 id 是 0-4,頁面上顯示的應該是 20-24,如下圖:
這時導出的 excel 應該跟頁面上顯示的一模一樣,這樣才是正確的。點擊【導出zip】按鈕,解壓后打開下載的其中一個 excel,驗證顯示的內容跟在線表格完全一致。
那么是如何做到的呢?主要看 handleDataWithRender()方法:
/**
* 如果 column 有 render 函數,則以 render 渲染的結果顯示
* @param worksheet
* @param sheet
*/
function handleDataWithRender(worksheet: Worksheet, sheet: ISheet) {
const {dataSource, columns} = sheet;
const rowsData = dataSource?.map(data => {
return columns?.map(column => {
// @ts-ignore
const renderResult = column?.render?.(data[column.dataIndex], data);
if (renderResult) {
// 如果不是 object 說明沒包裹標簽,是基本類型直接返回
if (typeof renderResult !== "object") {
return renderResult;
}
// 如果是 object 說明包裹了標簽,逐級取出值
return getValueFromRender(renderResult);
}
// @ts-ignore
return data[column.dataIndex];
})
})
// 添加行
const rows = worksheet.addRows(rowsData);
// 設置每行的樣式
addStyleToData(rows);
}
// 遞歸取出 render 里的值
// @ts-ignore
function getValueFromRender(renderResult: any) {
if (renderResult?.type) {
let children = renderResult?.props?.children;
if (children?.type) {
return getValueFromRender(children);
} else {
return children;
}
}
return ''
}
復制代碼
worksheet.addRows()可以添加數據對象,也可以添加由每行的每列組成的二維數組。由于我們要自己控制每個單元格顯示的內容,所以采用第二種方式,傳入一個二維數組來構造 row。結構如下圖所示:
循環 dataSource和 columns,就得到了每個單元格要顯示的內容,通過執行 render 函數,得到 render 執行后的結果:const renderResult=column?.render?.(data[column.dataIndex], data);注意 render 需要傳入兩個參數,一個是 text,一個是這行的數據對象,我們都能確定參數的值,所以直接傳入。然后判斷 renderResult的類型,如果是 object 類型,說明是個由 html 標簽包裹的 ReactNode,需要遞歸取出最終渲染的值。如果是非 object 類型,說明是 boolean 或者 string 這樣的基本類型,即沒有被標簽包裹,可以直接展示。由于我們采用了遞歸來取最后渲染的值,所以無論嵌套了多少層標簽,都可以正確的取到值。
三、導出包含多個子文件夾、多個excel文件的 zip 壓縮包
如果文件、文件夾嵌套比較深,可以使用 downloadFiles2ZipWithFolder()方法。文件結構如下圖:
核心代碼
export interface IDownloadFiles2ZipWithFolder {
zipName: string;
folders: IFolder[];
}
export interface IFolder {
folderName: string;
files: IDownloadExcel[];
}
export interface IDownloadExcel {
filename: string;
sheets: ISheet[];
}
export interface ISheet {
// sheet 的名字
sheetName: string;
// 這個 sheet 中表格的 column,類型同 antd 的 column
columns: ColumnType<any>[];
// 表格的數據
dataSource: any[];
}
/**
* 導出支持多級文件夾的壓縮包
* @param params
*/
export async function downloadFiles2ZipWithFolder(params: IDownloadFiles2ZipWithFolder) {
const zip = new JsZip();
const outPromises = params?.folders?.map(async folder => await handleFolder(zip, folder))
await Promise.all(outPromises);
zip.generateAsync({type: "blob"}).then(blob => {
saveAs(blob, `${params.zipName}.zip`)
})
}
async function handleFolder(zip: JsZip, folder: IFolder) {
console.log({folder})
let folderPromises: Promise<any>[] = [];
const promises = folder?.files?.map(async param => await handleEachFile(param, zip, folder.folderName));
await Promise.all([...promises, ...folderPromises]);
}
復制代碼
跟上一個方法 downloadFiles2Zip相比,參數的數據結構多了層 folders,其他的邏輯基本沒變。所以 downloadFiles2ZipWithFolder方法能實現downloadFiles2Zip方法的所有功能。
使用示例
如文章開頭的使用示例,為了方便看清結構,將每個對象的 files 值刪除,精簡之后得到如下結構:
downloadFiles2ZipWithFolder({
zipName: '壓縮包',
folders: [
{
folderName: '文件夾1',
files: []
},
{
folderName: '文件夾2',
files: []
},
{
folderName: '文件夾2/文件夾2-1',
files: []
},
{
folderName: '文件夾2/文件夾2-1/文件夾2-1-1',
files: []
},
{
folderName: '',
files: []
}
]
})
復制代碼
不管嵌套幾層文件夾,folders永遠是一個一維數組,每一項里面也不會嵌套 folders。多級目錄是通過文件名 folderName實現的。
如需查看 demo 完整代碼,源碼地址:github.com/cachecats/e…[4]
我的博客即將同步至騰訊云+社區,邀請大家一同入駐:cloud.tencent.com/developer/s…[5]
關于本文
https://juejin.cn/post/7080169896209809445
述可幫助您找到構建可處理大量數據的Web應用程序的正確解決方案。
在本文中,我將基于其功能和許可策略簡要概述五個流行的獨立JavaScript電子表格庫。這些圖書館可以免費在非營利項目中實施,也可以提供商業用途的付費許可。我希望此概述可以幫助您找到構建旨在處理大量數據的Web應用程序的正確解決方案。
Handsontable
Handsontable據說是一個帶有電子表格外觀的JavaScript網格。它是一個純JavaScript庫,支持React,Vue.js和Angular。其基本功能列表包括折疊列,調整大小,移動和隱藏列和行,添加注釋,顯示列摘要,導出數據,應用條件格式,使用數據驗證和添加下拉菜單的功能。也可以對數據進行排序和過濾并使用自動填充。
更有趣的是高級功能列表。例如,開發人員可以選擇在觸發表呈現時應使用哪個渲染器。此外,還可以創建自定義插件,使用自定義按鈕,并在單元格之間定義自定義邊框。此外,還有諸如多列排序,嵌套標題,修剪行 等功能。
Handsontable提供 三種類型的許可證:非商業免費許可證,開發人員(每個開發人員790美元),企業(自定義價格)許可證。
我的結論:Handsontable是非商業項目和那些準備為豐富功能付費的人的一個很好的選擇。
AG-網格
ag-Grid是一個JavaScript網格/電子表格組件,可以與Angular,Angular JS 1.x,React,Vue.js,Polymer和Web Components輕松集成,沒有第三方依賴。這個100,000行的演示判斷它的快速性能和數十 行。
分組和聚合功能允許用戶以他們想要的方式處理數據。可以按特定列對數據進行分組,并且可以在分組行中顯示各種聚合列值。ag-Grid提供快速過濾功能和自定義過濾器。延遲加載允許在用戶滾動時顯示所需的行數并請求其他數據,這有助于節省服務器資源。ag-Grid支持實時更新,每秒可處理數百個更新。您可以閱讀有關這些功能和其他功能的更多信息,并在功能概述頁面上查看一些演示 。
該庫有 兩個版本:社區和企業。社區版本由MIT許可證涵蓋,僅包括基本功能。具有所有可用功能的Enterprise許可證有三個選項:Single Application Developer(每個開發人員750美元),Multiple Application Developer(每個開發人員1,200美元)和部署許可證(每個生產環境750美元)。
我的結論是:ag-Grid提供了許多有用的功能,并且可以與不同的JS框架進行簡單的集成。對于大預算項目來說,這似乎是一個不錯的選擇,因為許可非常靈活而且價格昂貴。
dhtmlxSpreadsheet
dhtmlxSpreadsheet是一個可自定義的JavaScript電子表格組件,具有Material skin和類似Excel的界面。它為三個流行的框架提供了包裝器:React,Vue.js和Angular。
您可以根據其用戶指南自定義此電子表格的幾乎每個元素- 例如,使用工具欄,菜單和上下文菜單控件的自定義圖標字體包而不是Material-design控件。單元格格式功能允許您更改文本顏色和裝飾,背景顏色,設置文本對齊,將不同的數字格式應用于單元格值(百分比,貨幣甚至自定義格式),調整列大小等。
還可以鎖定單元格,通過在單元格中鍵入數據來自動填充包含內容的單元格,并拖動填充句柄以延長其他單元格中的一系列數字或字母。dhtmlxSpreadsheet提供了相當多的導航熱鍵列表。此外,該庫提供了從Excel文件導入和導出數據的機會。為此,dhtmlxSpreadsheet開發人員已經實現了他們自己的開源庫Excel2Json和Json2Excel。
dhtmlxSpreadsheet提供四個主要許可證:免費GNU GPL v2,商業許可證(最多5個開發人員149美元),企業許可證(最多20個開發人員團隊449美元),終極許可證(無限數量的開發人員669美元)。
我的判斷:dhtmlxSpreadsheet提供了一組基本功能和對流行框架的支持,可以被認為是物有所值的。
Clusterize.js
開發人員將Clusterize.js描述 為一個很小的插件,可以輕松顯示大型數據集。它的權重僅為2.3KB gzipped,但遺憾的是它沒有提供任何高級功能。其主要目的是使具有大量行的表在網頁上平滑運行。Clusterize.js不是用所有使用的標簽“污染”DOM,而是將列表拆分為簇,然后顯示當前滾動位置的元素,并在列表的頂部和底部添加額外的行以模擬表的完整高度,以便瀏覽器顯示完整列表的滾動條。此庫適用于現代瀏覽器,并支持所有主要的移動設備。
好消息是Clusterize.js非常實惠。在 可用的許可證中,您可以找到個人許可證(個人項目免費),商業許可證(25美元用于商業用途和無限數量的項目),以及擴展許可證(110美元;可以包含在待售產品中)。
我的結論是:Clusterize.js是那些尋找能夠快速運行并節省一些錢的單一用途工具的人的絕佳選擇。
SlickGrid
SlickGrid是一個簡潔而簡約的JavaScript電子表格組件。自適應虛擬滾動允許處理數十萬行而沒有任何延遲。該庫支持jQuery UI主題并支持廣泛的自定義。用戶可以調整列的大小,重新排序,顯示或隱藏,使用分組,過濾,自定義聚合器和其他功能。可插入單元格格式器和編輯器允許您擴展Web應用程序的功能。
如您所見,SlickGrid提供了一組基本功能,可以滿足普通用戶的需求。不幸的是,根據項目的 GitHub頁面,這個庫最近沒有受到開發人員的太多關注。好消息是SlickGrid是免費提供的。
我的判斷:如果你不是在尋找豐富的功能或者買不起商業許可證,那么SlickGrid可能是個不錯的選擇。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。