forked from sheetjs/sheetjs
vue-modify demo [ci skip]
This commit is contained in:
parent
9a3294c955
commit
2cbc28d6ed
24
demos/vue-modify/.gitignore
vendored
Normal file
24
demos/vue-modify/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
10
demos/vue-modify/README.md
Normal file
10
demos/vue-modify/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# vue-modify
|
||||
|
||||
This demo shows import an export with `vue3-table-light` table component.
|
||||
|
||||
In this directory, run
|
||||
|
||||
```bash
|
||||
npm i
|
||||
npm run dev
|
||||
```
|
21
demos/vue-modify/package.json
Normal file
21
demos/vue-modify/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "vue-modify",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.2.25",
|
||||
"vue3-table-lite": "^1.1.7-1",
|
||||
"xlsx": "^0.18.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.2.0",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.8.0",
|
||||
"vue-tsc": "^0.29.8"
|
||||
}
|
||||
}
|
241
demos/vue-modify/src/App.vue
Normal file
241
demos/vue-modify/src/App.vue
Normal file
@ -0,0 +1,241 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { read, utils, writeFile, WorkBook } from "xlsx";
|
||||
|
||||
import VueTableLite from "vue3-table-lite/ts";
|
||||
|
||||
type DataSet = {
|
||||
[index: string]: WorkBook;
|
||||
};
|
||||
|
||||
type Row = any[];
|
||||
|
||||
type Column = {
|
||||
field: string;
|
||||
label: string;
|
||||
display: (row: Row) => string;
|
||||
};
|
||||
|
||||
const currFileName = ref<string>("");
|
||||
const currSheet = ref<string>("");
|
||||
const sheets = ref<string[]>([]);
|
||||
const workBook = ref<DataSet>({} as DataSet);
|
||||
const rows = ref<Row[]>([]);
|
||||
const columns = ref<Column[]>([]);
|
||||
|
||||
const exportTypes: string[] = ["xlsx", "xlsb", "csv", "html"];
|
||||
|
||||
let cell = 0;
|
||||
|
||||
function resetCell() {
|
||||
cell = 0;
|
||||
}
|
||||
|
||||
function display(col: number): (row: Row) => string {
|
||||
return function (row: Row) {
|
||||
return `<span
|
||||
style="user-select: none; display: block"
|
||||
position="${Math.floor(cell++ / columns.value.length)}.${col}"
|
||||
onblur="endEdit(event)"
|
||||
ondblclick="startEdit(event)"
|
||||
onkeydown="endEdit(event)">${row[col] ?? " "}</span>`;
|
||||
};
|
||||
}
|
||||
|
||||
window.startEdit = function (ev) {
|
||||
ev.target.contentEditable = true;
|
||||
ev.target.focus();
|
||||
};
|
||||
|
||||
window.endEdit = function (ev) {
|
||||
if (ev.key === undefined || ev.key === "Enter") {
|
||||
const pos = ev.target.getAttribute("position").split(".");
|
||||
|
||||
ev.target.contentEditable = false;
|
||||
|
||||
rows.value[pos[0]][pos[1]] = ev.target.innerText;
|
||||
|
||||
workBook.value[currSheet.value] = utils.json_to_sheet(rows.value, {
|
||||
header: columns.value.map((col: Column) => col.field),
|
||||
skipHeader: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getRowsCols(
|
||||
data: DataSet,
|
||||
sheetName: string
|
||||
): {
|
||||
rows: Row[];
|
||||
cols: Column[];
|
||||
} {
|
||||
const rows: Row[] = utils.sheet_to_json(data[sheetName], { header: 1 });
|
||||
let cols: Column[] = [];
|
||||
|
||||
for (let row of rows) {
|
||||
const keys: string[] = Object.keys(row);
|
||||
|
||||
if (keys.length > cols.length) {
|
||||
cols = keys.map((key) => {
|
||||
return {
|
||||
field: key,
|
||||
label: utils.encode_col(+key),
|
||||
display: display(key),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { rows, cols };
|
||||
}
|
||||
|
||||
async function importFile(ev: ChangeEvent<HTMLInputElement>): Promise<void> {
|
||||
const file = ev.target.files[0];
|
||||
const data = read(await file.arrayBuffer());
|
||||
|
||||
currFileName.value = file.name;
|
||||
currSheet.value = data.SheetNames?.[0];
|
||||
sheets.value = data.SheetNames;
|
||||
workBook.value = data.Sheets;
|
||||
|
||||
selectSheet(currSheet.value);
|
||||
}
|
||||
|
||||
function exportFile(type: string): void {
|
||||
const wb = utils.book_new();
|
||||
|
||||
sheets.value.forEach((sheet) => {
|
||||
utils.book_append_sheet(wb, workBook.value[sheet], sheet);
|
||||
});
|
||||
|
||||
writeFile(wb, `sheet.${type}`);
|
||||
}
|
||||
|
||||
function selectSheet(sheet: string): void {
|
||||
const { rows: newRows, cols: newCols } = getRowsCols(workBook.value, sheet);
|
||||
|
||||
resetCell();
|
||||
|
||||
rows.value = newRows;
|
||||
columns.value = newCols;
|
||||
currSheet.value = sheet;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="imp-exp">
|
||||
<div class="import">
|
||||
<input type="file" id="import" @change="importFile" />
|
||||
<label for="import">import</label>
|
||||
</div>
|
||||
<span>{{ currFileName || "vue-modify demo" }}</span>
|
||||
<div class="export">
|
||||
<span>export</span>
|
||||
<ul>
|
||||
<li v-for="type in exportTypes" @click="exportFile(type)">
|
||||
{{ `.${type}` }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<div class="sheets">
|
||||
<span
|
||||
v-for="sheet in sheets"
|
||||
@click="selectSheet(sheet)"
|
||||
:class="[currSheet === sheet ? 'selected' : '']"
|
||||
>
|
||||
{{ sheet }}
|
||||
</span>
|
||||
</div>
|
||||
<vue-table-lite
|
||||
:is-static-mode="true"
|
||||
:page-size="50"
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
></vue-table-lite>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.imp-exp {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem;
|
||||
font-family: mono;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.import {
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
.import input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.import label {
|
||||
background-color: white;
|
||||
border: 1px solid;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.export: hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.export:hover ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.export span {
|
||||
padding: 0.3rem;
|
||||
border: 1px solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.export ul {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
background-color: white;
|
||||
list-style: none;
|
||||
padding: 0.3rem;
|
||||
border: 1px solid;
|
||||
margin-top: 0.3rem;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.export ul li {
|
||||
padding: 0.3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.export ul li:hover {
|
||||
background-color: lightgray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sheets {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0.3rem;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.sheets span {
|
||||
border: 1px solid;
|
||||
padding: 0.5rem;
|
||||
margin: 0.3rem;
|
||||
}
|
||||
|
||||
.sheets span:hover:not(.selected) {
|
||||
background-color: lightgray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #343a40;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
8
demos/vue-modify/src/env.d.ts
vendored
Normal file
8
demos/vue-modify/src/env.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
4
demos/vue-modify/src/main.ts
Normal file
4
demos/vue-modify/src/main.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
createApp(App).mount('#app');
|
16
demos/vue-modify/tsconfig.json
Normal file
16
demos/vue-modify/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
8
demos/vue-modify/tsconfig.node.json
Normal file
8
demos/vue-modify/tsconfig.node.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
7
demos/vue-modify/vite.config.ts
Normal file
7
demos/vue-modify/vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()]
|
||||
})
|
Loading…
Reference in New Issue
Block a user