added excel demo
This commit is contained in:
parent
b5b8970ab5
commit
7e6bb6998e
12
README.md
12
README.md
@ -1,6 +1,6 @@
|
||||
# vue3-element-admin
|
||||
|
||||
这个模板使用了最新的 vue3 和 element-plus UI框架,vite 构建工具、pinia 状态管理、vue-router 路由管理、mockjs 数据模拟。功能从Vue Element Admin 移植而来,详细使用可以参考[该文档](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html)。
|
||||
这个模板使用了最新的 vue3 和 element-plus UI 框架,vite 构建工具、pinia 状态管理、vue-router 路由管理、mockjs 数据模拟。功能从 Vue Element Admin 移植而来,详细使用可以参考[该文档](https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html)。
|
||||
|
||||
# 在线示例
|
||||
|
||||
@ -8,13 +8,13 @@
|
||||
|
||||
[element plus](https://element-plus.midfar.com/)
|
||||
|
||||
## 推荐的IDE工具和插件
|
||||
## 推荐的 IDE 工具和插件
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (禁用 Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Vite构建工具配置
|
||||
## Vite 构建工具配置
|
||||
|
||||
参考 [Vite配置](https://vitejs.dev/config/).
|
||||
参考 [Vite 配置](https://vitejs.dev/config/).
|
||||
|
||||
## 安装依赖
|
||||
|
||||
@ -44,10 +44,10 @@ npm run lint
|
||||
|
||||
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
|
||||
|
||||
<img src="https://vue3-element-admin.midfar.com/midfar_pay.jpg" alt="捐赠" style="zoom: 20%;" />
|
||||
wechat: midfar-sun
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2022-present, Midfar Sun
|
||||
Copyright (c) 2022-present, Midfar Sun
|
||||
|
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -77,6 +77,7 @@ declare module '@vue/runtime-core' {
|
||||
Sticky: typeof import('./src/components/Sticky/index.vue')['default']
|
||||
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
|
||||
Tinymce: typeof import('./src/components/Tinymce/index.vue')['default']
|
||||
UploadExcel: typeof import('./src/components/UploadExcel/index.vue')['default']
|
||||
VueCountTo: typeof import('./src/components/vue-count-to/vue-countTo.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
|
147
src/components/UploadExcel/index.vue
Normal file
147
src/components/UploadExcel/index.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
|
||||
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
Drop excel file here or
|
||||
<el-button :loading="loading" style="margin-left:16px;" size="small" type="primary" @click="handleUpload">
|
||||
Browse
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
beforeUpload: {
|
||||
type: Function,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
default: () => {}
|
||||
},
|
||||
onSuccess: {
|
||||
type: Function,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
excelData: {
|
||||
header: null,
|
||||
results: null
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
generateData({ header, results }) {
|
||||
this.excelData.header = header;
|
||||
this.excelData.results = results;
|
||||
this.onSuccess && this.onSuccess(this.excelData);
|
||||
},
|
||||
handleDrop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (this.loading) return;
|
||||
const files = e.dataTransfer.files;
|
||||
if (files.length !== 1) {
|
||||
ElMessage.error('Only support uploading one file!');
|
||||
return;
|
||||
}
|
||||
const rawFile = files[0]; // only use files[0]
|
||||
|
||||
if (!this.isExcel(rawFile)) {
|
||||
ElMessage.error('Only supports upload .xlsx, .xls, .csv suffix files');
|
||||
return false;
|
||||
}
|
||||
this.upload(rawFile);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
handleDragover(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
},
|
||||
handleUpload() {
|
||||
this.$refs['excel-upload-input'].click();
|
||||
},
|
||||
handleClick(e) {
|
||||
const files = e.target.files;
|
||||
const rawFile = files[0]; // only use files[0]
|
||||
if (!rawFile) return;
|
||||
this.upload(rawFile);
|
||||
},
|
||||
upload(rawFile) {
|
||||
this.$refs['excel-upload-input'].value = null; // fix can't select the same excel
|
||||
|
||||
if (!this.beforeUpload) {
|
||||
this.readerData(rawFile);
|
||||
return;
|
||||
}
|
||||
const before = this.beforeUpload(rawFile);
|
||||
if (before) {
|
||||
this.readerData(rawFile);
|
||||
}
|
||||
},
|
||||
readerData(rawFile) {
|
||||
this.loading = true;
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
const data = e.target.result;
|
||||
const workbook = XLSX.read(data, { type: 'array' });
|
||||
const firstSheetName = workbook.SheetNames[0];
|
||||
const worksheet = workbook.Sheets[firstSheetName];
|
||||
const header = this.getHeaderRow(worksheet);
|
||||
const results = XLSX.utils.sheet_to_json(worksheet);
|
||||
this.generateData({ header, results });
|
||||
this.loading = false;
|
||||
resolve();
|
||||
};
|
||||
reader.readAsArrayBuffer(rawFile);
|
||||
});
|
||||
},
|
||||
getHeaderRow(sheet) {
|
||||
const headers = [];
|
||||
const range = XLSX.utils.decode_range(sheet['!ref']);
|
||||
let C;
|
||||
const R = range.s.r;
|
||||
/* start in the first row */
|
||||
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
|
||||
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
|
||||
/* find the cell in the first row */
|
||||
let hdr = 'UNKNOWN ' + C; // <-- replace with your desired default
|
||||
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
|
||||
headers.push(hdr);
|
||||
}
|
||||
return headers;
|
||||
},
|
||||
isExcel(file) {
|
||||
return /\.(xlsx|xls|csv)$/.test(file.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.excel-upload-input{
|
||||
display: none;
|
||||
z-index: -9999;
|
||||
}
|
||||
.drop{
|
||||
border: 2px dashed #bbb;
|
||||
width: 600px;
|
||||
height: 160px;
|
||||
line-height: 160px;
|
||||
margin: 0 auto;
|
||||
font-size: 24px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
@ -7,8 +7,11 @@
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }">
|
||||
|
||||
<!-- <item :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" :title="onlyOneChild.meta.title" /> -->
|
||||
<svg-icon v-if="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"
|
||||
:icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
|
||||
<template v-if="get2MetaIconPath(onlyOneChild, item)">
|
||||
<svg-icon v-if="typeof get2MetaIconPath(onlyOneChild, item) === 'string'"
|
||||
:icon-class="get2MetaIconPath(onlyOneChild, item)" />
|
||||
<component v-else :is="get2MetaIconPath(onlyOneChild, item)" class="svg-icon el-svg-icon" />
|
||||
</template>
|
||||
<template #title>
|
||||
<span class="text text-one">{{ onlyOneChild.meta.title }}</span>
|
||||
</template>
|
||||
@ -19,7 +22,11 @@
|
||||
<el-sub-menu class="left-sub-menu" v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
|
||||
<template v-if="item.meta" #title>
|
||||
<!-- <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> -->
|
||||
<svg-icon :icon-class="(item.meta && item.meta.icon) || 'sub-el-icon'" />
|
||||
<template v-if="getMetaIconPath(item)">
|
||||
<svg-icon v-if="typeof getMetaIconPath(item) === 'string'" :icon-class="getMetaIconPath(item)" />
|
||||
<component v-else :is="getMetaIconPath(item)" class="svg-icon el-svg-icon" />
|
||||
</template>
|
||||
<svg-icon v-else icon-class="sub-el-icon" />
|
||||
<span class="text text-two">{{ item.meta.title }}</span>
|
||||
</template>
|
||||
<sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child"
|
||||
@ -73,6 +80,12 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMetaIconPath(item) {
|
||||
return item.meta && item.meta.icon;
|
||||
},
|
||||
get2MetaIconPath(onlyOneChild, item) {
|
||||
return onlyOneChild.meta.icon || (item.meta && item.meta.icon);
|
||||
},
|
||||
hasOneShowingChild(children = [], parent) {
|
||||
const showingChildren = children.filter(item => {
|
||||
if (this.isItemHidden) {
|
||||
@ -119,4 +132,12 @@ export default defineComponent({
|
||||
.left-sub-menu :deep(.el-sub-menu__title) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.el-svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { markRaw } from 'vue';
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'; // createWebHashHistory, createWebHistory
|
||||
import type { Router, RouteRecordRaw, RouteComponent } from 'vue-router';
|
||||
import { Help as IconHelp } from '@element-plus/icons-vue';
|
||||
|
||||
/* Layout */
|
||||
const Layout = ():RouteComponent => import('@/layout/index.vue');
|
||||
@ -178,7 +180,7 @@ export const asyncRoutes:RouteRecordRaw[] = [
|
||||
name: 'Example',
|
||||
meta: {
|
||||
title: 'Example',
|
||||
icon: 'el-icon-s-help'
|
||||
icon: markRaw(IconHelp)
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@ -253,42 +255,42 @@ export const asyncRoutes:RouteRecordRaw[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// {
|
||||
// path: '/excel',
|
||||
// component: Layout,
|
||||
// redirect: '/excel/export-excel',
|
||||
// name: 'Excel',
|
||||
// meta: {
|
||||
// title: 'Excel',
|
||||
// icon: 'excel'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'export-excel',
|
||||
// component: () => import('@/views/excel/export-excel'),
|
||||
// name: 'ExportExcel',
|
||||
// meta: { title: 'Export Excel' }
|
||||
// },
|
||||
// {
|
||||
// path: 'export-selected-excel',
|
||||
// component: () => import('@/views/excel/select-excel'),
|
||||
// name: 'SelectExcel',
|
||||
// meta: { title: 'Export Selected' }
|
||||
// },
|
||||
// {
|
||||
// path: 'export-merge-header',
|
||||
// component: () => import('@/views/excel/merge-header'),
|
||||
// name: 'MergeHeader',
|
||||
// meta: { title: 'Merge Header' }
|
||||
// },
|
||||
// {
|
||||
// path: 'upload-excel',
|
||||
// component: () => import('@/views/excel/upload-excel'),
|
||||
// name: 'UploadExcel',
|
||||
// meta: { title: 'Upload Excel' }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
path: '/excel',
|
||||
component: Layout,
|
||||
redirect: '/excel/export-excel',
|
||||
name: 'Excel',
|
||||
meta: {
|
||||
title: 'Excel',
|
||||
icon: 'excel'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'export-excel',
|
||||
component: () => import('@/views/excel/export-excel.vue'),
|
||||
name: 'ExportExcel',
|
||||
meta: { title: 'Export Excel' }
|
||||
},
|
||||
{
|
||||
path: 'export-selected-excel',
|
||||
component: () => import('@/views/excel/select-excel.vue'),
|
||||
name: 'SelectExcel',
|
||||
meta: { title: 'Export Selected' }
|
||||
},
|
||||
{
|
||||
path: 'export-merge-header',
|
||||
component: () => import('@/views/excel/merge-header.vue'),
|
||||
name: 'MergeHeader',
|
||||
meta: { title: 'Merge Header' }
|
||||
},
|
||||
{
|
||||
path: 'upload-excel',
|
||||
component: () => import('@/views/excel/upload-excel.vue'),
|
||||
name: 'UploadExcel',
|
||||
meta: { title: 'Upload Excel' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/zip',
|
||||
|
36
src/views/excel/components/AutoWidthOption.vue
Normal file
36
src/views/excel/components/AutoWidthOption.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<label class="radio-label">Cell Auto-Width: </label>
|
||||
<el-radio-group v-model="autoWidth">
|
||||
<el-radio :label="true" border>
|
||||
True
|
||||
</el-radio>
|
||||
<el-radio :label="false" border>
|
||||
False
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
autoWidth: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:modelValue', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
41
src/views/excel/components/BookTypeOption.vue
Normal file
41
src/views/excel/components/BookTypeOption.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<label class="radio-label">Book Type: </label>
|
||||
<el-select v-model="bookType" style="width:120px;">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: 'xlsx'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: ['xlsx', 'csv', 'txt']
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
bookType: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:modelValue', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
35
src/views/excel/components/FilenameOption.vue
Normal file
35
src/views/excel/components/FilenameOption.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<label class="radio-label" style="padding-left:0;">Filename: </label>
|
||||
<el-input v-model="filename" placeholder="Please enter the file name (default excel-list)" style="width:345px;" :prefix-icon="IconDocument" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { Document as IconDocument } from '@element-plus/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
IconDocument: markRaw(IconDocument)
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filename: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:modelValue', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
120
src/views/excel/export-excel.vue
Normal file
120
src/views/excel/export-excel.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<div style="margin: 0 0 20px 0;">
|
||||
<FilenameOption v-model="filename" />
|
||||
<AutoWidthOption v-model="autoWidth" />
|
||||
<BookTypeOption v-model="bookType" />
|
||||
<el-button :loading="downloadLoading" style="margin:0 0 0 20px;" type="primary" :icon="IconDocument" @click="handleDownload">
|
||||
Export Excel
|
||||
</el-button>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
|
||||
<el-tag type="info" size="large">Documentation</el-tag>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" element-loading-text="Loading..." border fit highlight-current-row>
|
||||
<el-table-column align="center" label="Id" width="95">
|
||||
<template v-slot="scope">
|
||||
{{ scope.$index }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Title">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Author" width="110" align="center">
|
||||
<template v-slot="scope">
|
||||
<el-tag>{{ scope.row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Readings" width="115" align="center">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.pageviews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Date" width="220">
|
||||
<template v-slot="scope">
|
||||
<el-icon><IconTimer /></el-icon>
|
||||
<span>{{ parseTime(scope.row.timestamp, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { fetchList } from '@/api/article';
|
||||
import { parseTime } from '@/utils';
|
||||
// options components
|
||||
import FilenameOption from './components/FilenameOption';
|
||||
import AutoWidthOption from './components/AutoWidthOption';
|
||||
import BookTypeOption from './components/BookTypeOption';
|
||||
import { Document as IconDocument, Timer as IconTimer } from '@element-plus/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExportExcel',
|
||||
components: { FilenameOption, AutoWidthOption, BookTypeOption, IconTimer },
|
||||
data() {
|
||||
return {
|
||||
IconDocument: markRaw(IconDocument),
|
||||
list: null,
|
||||
listLoading: true,
|
||||
downloadLoading: false,
|
||||
filename: '',
|
||||
autoWidth: true,
|
||||
bookType: 'xlsx'
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
fetchData() {
|
||||
this.listLoading = true;
|
||||
fetchList().then(response => {
|
||||
this.list = response.data.items;
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
handleDownload() {
|
||||
this.downloadLoading = true;
|
||||
import('@/vendor/Export2Excel').then(excel => {
|
||||
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date'];
|
||||
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
|
||||
const list = this.list;
|
||||
const data = this.formatJson(filterVal, list);
|
||||
excel.export_json_to_excel({
|
||||
header: tHeader,
|
||||
data,
|
||||
filename: this.filename,
|
||||
autoWidth: this.autoWidth,
|
||||
bookType: this.bookType
|
||||
});
|
||||
this.downloadLoading = false;
|
||||
});
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map(v => filterVal.map(j => {
|
||||
if (j === 'timestamp') {
|
||||
return parseTime(v[j]);
|
||||
} else {
|
||||
return v[j];
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.radio-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 40px;
|
||||
padding: 0 12px 0 30px;
|
||||
}
|
||||
</style>
|
106
src/views/excel/merge-header.vue
Normal file
106
src/views/excel/merge-header.vue
Normal file
@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" :icon="IconDocument" @click="handleDownload">Export</el-button>
|
||||
|
||||
<el-table
|
||||
ref="multipleTable"
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column align="center" label="Id" width="95">
|
||||
<template v-slot="scope">
|
||||
{{ scope.$index }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Main Information" align="center">
|
||||
<el-table-column label="Title">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Author" width="110" align="center">
|
||||
<template v-slot="scope">
|
||||
<el-tag>{{ scope.row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Readings" width="115" align="center">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.pageviews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="Date" width="220">
|
||||
<template v-slot="scope">
|
||||
<el-icon><IconTimer /></el-icon>
|
||||
<span>{{ parseTime(scope.row.timestamp, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { fetchList } from '@/api/article';
|
||||
import { parseTime } from '@/utils';
|
||||
import { Document as IconDocument, Timer as IconTimer } from '@element-plus/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MergeHeader',
|
||||
components: { IconTimer },
|
||||
data() {
|
||||
return {
|
||||
IconDocument: markRaw(IconDocument),
|
||||
list: null,
|
||||
listLoading: true,
|
||||
downloadLoading: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
fetchData() {
|
||||
this.listLoading = true;
|
||||
fetchList(this.listQuery).then(response => {
|
||||
this.list = response.data.items;
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
handleDownload() {
|
||||
this.downloadLoading = true;
|
||||
import('@/vendor/Export2Excel').then(excel => {
|
||||
const multiHeader = [['Id', 'Main Information', '', '', 'Date']];
|
||||
const header = ['', 'Title', 'Author', 'Readings', ''];
|
||||
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
|
||||
const list = this.list;
|
||||
const data = this.formatJson(filterVal, list);
|
||||
const merges = ['A1:A2', 'B1:D1', 'E1:E2'];
|
||||
excel.export_json_to_excel({
|
||||
multiHeader,
|
||||
header,
|
||||
merges,
|
||||
data
|
||||
});
|
||||
this.downloadLoading = false;
|
||||
});
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map(v => filterVal.map(j => {
|
||||
if (j === 'timestamp') {
|
||||
return parseTime(v[j]);
|
||||
} else {
|
||||
return v[j];
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
113
src/views/excel/select-excel.vue
Normal file
113
src/views/excel/select-excel.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div style="margin-bottom:20px">
|
||||
<el-input v-model="filename" placeholder="Please enter the file name (default excel-list)" style="width:350px;" :prefix-icon="IconDocument" />
|
||||
<el-button :loading="downloadLoading" type="primary" :icon="IconDocument" @click="handleDownload">
|
||||
Export Selected Items
|
||||
</el-button>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
|
||||
<el-tag type="info" size="large">Documentation</el-tag>
|
||||
</a>
|
||||
</div>
|
||||
<el-table
|
||||
ref="multipleTable"
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="拼命加载中"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" align="center" />
|
||||
<el-table-column align="center" label="Id" width="95">
|
||||
<template v-slot="scope">
|
||||
{{ scope.$index }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Title">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.title }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Author" width="110" align="center">
|
||||
<template v-slot="scope">
|
||||
<el-tag>{{ scope.row.author }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Readings" width="115" align="center">
|
||||
<template v-slot="scope">
|
||||
{{ scope.row.pageviews }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="PDate" width="220">
|
||||
<template v-slot="scope">
|
||||
<el-icon><IconTimer /></el-icon>
|
||||
<span>{{ scope.row.display_time }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { fetchList } from '@/api/article';
|
||||
import { Document as IconDocument, Timer as IconTimer } from '@element-plus/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SelectExcel',
|
||||
components: { IconTimer },
|
||||
data() {
|
||||
return {
|
||||
IconDocument: markRaw(IconDocument),
|
||||
list: null,
|
||||
listLoading: true,
|
||||
multipleSelection: [],
|
||||
downloadLoading: false,
|
||||
filename: ''
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true;
|
||||
fetchList(this.listQuery).then(response => {
|
||||
this.list = response.data.items;
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
handleDownload() {
|
||||
if (this.multipleSelection.length) {
|
||||
this.downloadLoading = true;
|
||||
import('@/vendor/Export2Excel').then(excel => {
|
||||
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date'];
|
||||
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
|
||||
const list = this.multipleSelection;
|
||||
const data = this.formatJson(filterVal, list);
|
||||
excel.export_json_to_excel({
|
||||
header: tHeader,
|
||||
data,
|
||||
filename: this.filename
|
||||
});
|
||||
this.$refs.multipleTable.clearSelection();
|
||||
this.downloadLoading = false;
|
||||
});
|
||||
} else {
|
||||
ElMessage({
|
||||
message: 'Please select at least one item',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map(v => filterVal.map(j => v[j]));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
43
src/views/excel/upload-excel.vue
Normal file
43
src/views/excel/upload-excel.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
|
||||
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
|
||||
<el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import UploadExcelComponent from '@/components/UploadExcel/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UploadExcel',
|
||||
components: { UploadExcelComponent },
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
tableHeader: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
beforeUpload(file) {
|
||||
const isLt1M = file.size / 1024 / 1024 < 1;
|
||||
|
||||
if (isLt1M) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ElMessage({
|
||||
message: 'Please do not upload files larger than 1m in size.',
|
||||
type: 'warning'
|
||||
});
|
||||
return false;
|
||||
},
|
||||
handleSuccess({ results, header }) {
|
||||
this.tableData = results;
|
||||
this.tableHeader = header;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user