feat: 导入导出功能

This commit is contained in:
leijun 2023-08-23 16:05:44 +08:00
parent 21341a8c96
commit e01b72be6c
6 changed files with 1282 additions and 5 deletions

1066
main.css Normal file

File diff suppressed because one or more lines are too long

4
package-lock.json generated
View file

@ -1344,6 +1344,10 @@
"opencollective-postinstall": "^2.0.2"
}
},
"xlsx": {
"version": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz",
"integrity": "sha512-adg5edVTkXXGTnb0iWrc3Z47ViGgwmD47yx6VfSCZhfdKPtHcElYGs48OtcO4nwOu90Pjz5mmyl0HLcvbSRYTQ=="
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

View file

@ -24,6 +24,7 @@
"typescript": "4.7.4"
},
"dependencies": {
"x-data-spreadsheet": "^1.1.9"
"x-data-spreadsheet": "^1.1.9",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz"
}
}

View file

@ -1,14 +1,17 @@
import ExcelPlugin from "main";
import { TextFileView, WorkspaceLeaf } from "obsidian";
import Spreadsheet from "x-data-spreadsheet";
import * as XLSX from "xlsx";
import { stox, xtos } from "./utils/xlsxspread";
export const VIEW_TYPE_EXCEL = "excel-view";
export class ExcelView extends TextFileView {
public plugin: ExcelPlugin;
public ownerWindow: Window;
public ownerDocument: Document;
public sheet: any;
public importEle: HTMLElement;
public exportEle: HTMLElement;
constructor(leaf: WorkspaceLeaf, plugin: ExcelPlugin) {
super(leaf);
@ -46,6 +49,71 @@ export class ExcelView extends TextFileView {
});
this.sheet.validate();
// 导入导出
const importInput = this.contentEl.createEl("input", {
cls: "import-excel",
type: "file",
attr: { id: "import" },
});
importInput.addEventListener(
"change",
this.handleFile.bind(this),
false
);
// 添加顶部导入按钮
if (!this.importEle) {
this.importEle = this.addAction(
"download",
"import xlsx file",
(ev) => this.handleImportClick(ev)
);
}
if (!this.exportEle) {
this.exportEle = this.addAction(
"upload",
"export xlsx file",
(ev) => this.handleExportClick(ev)
);
}
}
// 处理顶部导入按钮点击事件
handleImportClick(ev: MouseEvent) {
const importEle = document.getElementById("import");
importEle?.click();
// 打开文件
console.log("handleImportClick", importEle);
}
handleFile(e: Event) {
console.log("handleFile", e);
//@ts-ignore
const files = e.target?.files;
var f = files[0];
var reader = new FileReader();
var instance = this;
reader.onload = (e) => {
const data = e.target?.result;
console.log(data, instance);
instance.process_wb(XLSX.read(data));
};
reader.readAsArrayBuffer(f);
}
process_wb(wb: XLSX.WorkBook) {
const sheetData = stox(wb);
this.sheet.loadData(sheetData);
}
handleExportClick(ev: MouseEvent) {
var new_wb = xtos(this.sheet.getData()) as XLSX.WorkBook;
var title = this.file?.basename ?? "sheet"
/* write file and trigger a download */
XLSX.writeFile(new_wb, title + '.xlsx', {});
}
onload(): void {

133
src/utils/xlsxspread.js Normal file
View file

@ -0,0 +1,133 @@
/*! xlsxspread.js (C) SheetJS LLC -- https://sheetjs.com/ */
/* eslint-env browser */
/*global XLSX */
/*exported stox, xtos */
import * as XLSX from 'xlsx'
/**
* Converts data from SheetJS to x-spreadsheet
*
* @param {Object} wb SheetJS workbook object
*
* @returns {Object[]} An x-spreadsheet data
*/
export function stox(wb) {
var out = [];
wb.SheetNames.forEach(function (name) {
var o = { name: name, rows: {} };
var ws = wb.Sheets[name];
if(!ws || !ws["!ref"]) return;
var range = XLSX.utils.decode_range(ws['!ref']);
// sheet_to_json will lost empty row and col at begin as default
range.s = { r: 0, c: 0 };
var aoa = XLSX.utils.sheet_to_json(ws, {
raw: false,
header: 1,
range: range
});
aoa.forEach(function (r, i) {
var cells = {};
r.forEach(function (c, j) {
cells[j] = { text: c };
var cellRef = XLSX.utils.encode_cell({ r: i, c: j });
if ( ws[cellRef] != null && ws[cellRef].f != null) {
cells[j].text = "=" + ws[cellRef].f;
}
});
o.rows[i] = { cells: cells };
});
o.merges = [];
(ws["!merges"]||[]).forEach(function (merge, i) {
//Needed to support merged cells with empty content
if (o.rows[merge.s.r] == null) {
o.rows[merge.s.r] = { cells: {} };
}
if (o.rows[merge.s.r].cells[merge.s.c] == null) {
o.rows[merge.s.r].cells[merge.s.c] = {};
}
o.rows[merge.s.r].cells[merge.s.c].merge = [
merge.e.r - merge.s.r,
merge.e.c - merge.s.c
];
o.merges[i] = XLSX.utils.encode_range(merge);
});
out.push(o);
});
return out;
}
/**
* Converts data from x-spreadsheet to SheetJS
*
* @param {Object[]} sdata An x-spreadsheet data object
*
* @returns {Object} A SheetJS workbook object
*/
export function xtos(sdata) {
var out = XLSX.utils.book_new();
sdata.forEach(function (xws) {
var ws = {};
var rowobj = xws.rows;
var minCoord = { r: 0, c: 0 }, maxCoord = { r: 0, c: 0 };
for (var ri = 0; ri < rowobj.len; ++ri) {
var row = rowobj[ri];
if (!row) continue;
Object.keys(row.cells).forEach(function (k) {
var idx = +k;
if (isNaN(idx)) return;
var lastRef = XLSX.utils.encode_cell({ r: ri, c: idx });
if (ri > maxCoord.r) maxCoord.r = ri;
if (idx > maxCoord.c) maxCoord.c = idx;
var cellText = row.cells[k].text, type = "s";
if (!cellText) {
cellText = "";
type = "z";
} else if (!isNaN(Number(cellText))) {
cellText = Number(cellText);
type = "n";
} else if (cellText.toLowerCase() === "true" || cellText.toLowerCase() === "false") {
cellText = Boolean(cellText);
type = "b";
}
ws[lastRef] = { v: cellText, t: type };
if (type == "s" && cellText[0] == "=") {
ws[lastRef].f = cellText.slice(1);
}
if (row.cells[k].merge != null) {
if (ws["!merges"] == null) ws["!merges"] = [];
ws["!merges"].push({
s: { r: ri, c: idx },
e: {
r: ri + row.cells[k].merge[0],
c: idx + row.cells[k].merge[1]
}
});
}
});
}
ws["!ref"] = minCoord ? XLSX.utils.encode_range({
s: minCoord,
e: maxCoord
}) : "A1";
XLSX.utils.book_append_sheet(out, ws, xws.name);
});
return out;
}

View file

@ -1,13 +1,18 @@
.sheet-box {
/* position: absolute;
left: 0;
top: 40px; */
width: 100%;
height: 100%;
margin: -16px;
padding: 0;
}
.import-excel {
position: absolute;
right: 0;
bottom: 0;
z-index: 100;
opacity: 0;
}
/* node_modules/x-data-spreadsheet/src/index.less */
body {
margin: 0;