feat: 增加表格导出功能

This commit is contained in:
张传龙 2023-04-13 12:13:32 +08:00
parent 8304970a59
commit 681b3144d1
4 changed files with 92 additions and 5 deletions

View File

@ -25,7 +25,8 @@
"pinia": "^2.0.33",
"vite": "^4.2.1",
"vue": "^3.2.47",
"vue-router": "^4.1.6"
"vue-router": "^4.1.6",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^17.4.4",

62
pnpm-lock.yaml generated
View File

@ -45,6 +45,7 @@ specifiers:
vite-plugin-svg-icons: ^2.0.1
vue: ^3.2.47
vue-router: ^4.1.6
xlsx: ^0.18.5
dependencies:
'@vueuse/core': 9.13.0_vue@3.2.47
@ -59,6 +60,7 @@ dependencies:
vite: 4.2.1_sass@1.58.3
vue: 3.2.47
vue-router: 4.1.6_vue@3.2.47
xlsx: 0.18.5
devDependencies:
'@commitlint/cli': 17.4.4
@ -1406,6 +1408,11 @@ packages:
hasBin: true
dev: true
/adler-32/1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
engines: {node: '>=0.8'}
dev: false
/aggregate-error/3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@ -1744,6 +1751,14 @@ packages:
engines: {node: '>=6'}
dev: true
/cfb/1.2.2:
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
engines: {node: '>=0.8'}
dependencies:
adler-32: 1.3.1
crc-32: 1.2.2
dev: false
/chalk/1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
engines: {node: '>=0.10.0'}
@ -1880,6 +1895,11 @@ packages:
engines: {node: '>=0.8'}
dev: true
/codepage/1.15.0:
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
engines: {node: '>=0.8'}
dev: false
/collection-visit/1.0.0:
resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
engines: {node: '>=0.10.0'}
@ -2078,6 +2098,12 @@ packages:
path-type: 4.0.0
dev: true
/crc-32/1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true
dev: false
/create-require/1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
@ -3001,6 +3027,11 @@ packages:
mime-types: 2.1.35
dev: false
/frac/1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
engines: {node: '>=0.8'}
dev: false
/fragment-cache/0.2.1:
resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
engines: {node: '>=0.10.0'}
@ -5202,6 +5233,13 @@ packages:
readable-stream: 3.6.0
dev: true
/ssf/0.11.2:
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
engines: {node: '>=0.8'}
dependencies:
frac: 1.1.2
dev: false
/ssr-window/3.0.0:
resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
dev: false
@ -6130,11 +6168,21 @@ packages:
resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==}
dev: false
/wmf/1.0.2:
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
engines: {node: '>=0.8'}
dev: false
/word-wrap/1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}
dev: true
/word/0.3.0:
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
engines: {node: '>=0.8'}
dev: false
/wrap-ansi/6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
@ -6157,6 +6205,20 @@ packages:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
/xlsx/0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
engines: {node: '>=0.8'}
hasBin: true
dependencies:
adler-32: 1.3.1
cfb: 1.2.2
codepage: 1.15.0
crc-32: 1.2.2
ssf: 0.11.2
wmf: 1.0.2
word: 0.3.0
dev: false
/y18n/5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}

View File

@ -17,6 +17,8 @@
</template>
<script setup>
import { utils, writeFile } from 'xlsx'
const props = defineProps({
/**
* @remote true: 后端分页 false 前端分页
@ -73,7 +75,7 @@ const props = defineProps({
},
})
const emit = defineEmits(['update:queryItems', 'onChecked'])
const emit = defineEmits(['update:queryItems', 'onChecked', 'onDataChange'])
const loading = ref(false)
const initQuery = { ...props.queryItems }
const tableData = ref([])
@ -94,6 +96,7 @@ async function handleQuery() {
tableData.value = []
pagination.itemCount = 0
} finally {
emit('onDataChange', tableData.value)
loading.value = false
}
}
@ -122,9 +125,21 @@ function onChecked(rowKeys) {
emit('onChecked', rowKeys)
}
}
function handleExport(columns = props.columns, data = tableData.value) {
if (!data?.length) return $message.warning('没有数据')
const columnsData = columns.filter((item) => !!item.title && !item.hideInExcel)
const thKeys = columnsData.map((item) => item.key)
const thData = columnsData.map((item) => item.title)
const trData = data.map((item) => thKeys.map((key) => item[key]))
const sheet = utils.aoa_to_sheet([thData, ...trData])
const workBook = utils.book_new()
utils.book_append_sheet(workBook, sheet, '数据报表')
writeFile(workBook, '数据报表.xlsx')
}
defineExpose({
handleSearch,
handleReset,
handleExport,
})
</script>

View File

@ -1,9 +1,14 @@
<template>
<CommonPage show-footer title="文章">
<template #action>
<n-button type="primary" @click="handleAdd">
<div>
<n-button type="primary" secondary @click="$table?.handleExport()">
<TheIcon icon="mdi:download" :size="18" class="mr-5" /> 导出
</n-button>
<n-button type="primary" class="ml-16" @click="handleAdd">
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" /> 新建文章
</n-button>
</div>
</template>
<CrudTable
@ -14,6 +19,7 @@
:columns="columns"
:get-data="api.getPosts"
@on-checked="onChecked"
@on-data-change="(data) => (tableData = data)"
>
<template #queryBar>
<QueryBarItem label="标题" :label-width="50">
@ -89,6 +95,8 @@ import api from './api'
defineOptions({ name: 'Crud' })
const $table = ref(null)
/** 表格数据,触发搜索的时候会更新这个值 */
const tableData = ref([])
/** QueryBar筛选参数可选 */
const queryItems = ref({})
/** 补充参数(可选) */
@ -141,6 +149,7 @@ const columns = [
width: 240,
align: 'center',
fixed: 'right',
hideInExcel: true,
render(row) {
return [
h(