添加表格导出功能

This commit is contained in:
GGJ
2024-12-26 15:56:32 +08:00
parent aed771578a
commit 46dc032c4c
14 changed files with 2453 additions and 2827 deletions

View File

@@ -42,6 +42,7 @@
"vue-draggable-resizable": "3.0.0-beta.2",
"vue-router": "4",
"vxe-table": "^4.5.17",
"vxe-table-plugin-export-xlsx": "^4.0.7",
"xe-utils": "^3.5.14"
},
"devDependencies": {

4987
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,8 @@
</el-button>
<el-button @click="onComSearch" v-if="showSearch" type="primary" :icon="Search">查询</el-button>
<el-button @click="onResetForm" v-if="showSearch && showReset" :icon="RefreshLeft">重置</el-button>
<el-button @click="onExport" v-if="showExport" :loading="tableStore.table.loading" type="primary"
icon="el-icon-Download">导出</el-button>
</template>
<slot name="operation"></slot>
</div>
@@ -55,6 +57,7 @@ interface Props {
nextFlag?: boolean //控制时间是否可以往后推
theCurrentTime?: boolean //控制时间前3天展示上个月时间
showReset?: boolean //是否显示重置
showExport?: boolean //导出控制
}
const props = withDefaults(defineProps<Props>(), {
@@ -63,7 +66,8 @@ const props = withDefaults(defineProps<Props>(), {
showSearch: true,
nextFlag: false,
theCurrentTime: true,
showReset: true
showReset: true,
showExport:false
})
// 动态计算table高度
let resizeObserver = new ResizeObserver(entries => {
@@ -169,7 +173,10 @@ const onResetForm = () => {
const setInterval = (val: any) => {
datePickerRef.value.setInterval(val)
}
// 导出
const onExport = () => {
tableStore.onTableAction('export', {showAllFlag:true})
}
defineExpose({ onComSearch, areaRef, setDatePicker, setInterval, datePickerRef, showSelectChange, computedSearchRow })
</script>

View File

@@ -1,40 +1,23 @@
<template>
<div :style="{ height: tableStore.table.height }">
<vxe-table
ref="tableRef"
height="auto"
:data="tableStore.table.data"
v-loading="tableStore.table.loading"
v-bind="Object.assign({}, defaultAttribute, $attrs)"
@checkbox-all="selectChangeEvent"
@checkbox-change="selectChangeEvent"
>
<vxe-table ref="tableRef" height="auto" :data="tableStore.table.data" v-loading="tableStore.table.loading"
v-bind="Object.assign({}, defaultAttribute, $attrs)" @checkbox-all="selectChangeEvent"
@checkbox-change="selectChangeEvent" :showOverflow="showOverflow">
<!-- Column 组件内部是 el-table-column -->
<template v-if="isGroup">
<GroupColumn :column="tableStore.table.column" />
</template>
<template v-else>
<Column
:attr="item"
:key="key + '-column'"
v-for="(item, key) in tableStore.table.column"
:tree-node="item.treeNode"
>
<Column :attr="item" :key="key + '-column'" v-for="(item, key) in tableStore.table.column"
:tree-node="item.treeNode">
<!-- tableStore 预设的列 render 方案 -->
<template v-if="item.render" #default="scope">
<FieldRender
:field="item"
:row="scope.row"
:column="scope.column"
:index="scope.rowIndex"
:key="
key +
'-' +
item.render +
'-' +
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '')
"
/>
<FieldRender :field="item" :row="scope.row" :column="scope.column" :index="scope.rowIndex" :key="key +
'-' +
item.render +
'-' +
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '')
" />
</template>
</Column>
</template>
@@ -43,22 +26,17 @@
</div>
<div v-if="tableStore.showPage" class="table-pagination">
<el-pagination
:currentPage="tableStore.table.params!.pageNum"
:page-size="tableStore.table.params!.pageSize"
:page-sizes="pageSizes"
background
<el-pagination :currentPage="tableStore.table.params!.pageNum" :page-size="tableStore.table.params!.pageSize"
:page-sizes="pageSizes" background
:layout="config.layout.shrink ? 'prev, next, jumper' : 'sizes,total, ->, prev, pager, next, jumper'"
:total="tableStore.table.total"
@size-change="onTableSizeChange"
@current-change="onTableCurrentChange"
></el-pagination>
:total="tableStore.table.total" @size-change="onTableSizeChange"
@current-change="onTableCurrentChange"></el-pagination>
</div>
<slot name="footer"></slot>
</template>
<script setup lang="ts">
import { ref, nextTick, inject, computed, onMounted } from 'vue'
import { ref, nextTick, inject, computed, onMounted, watch } from 'vue'
import type { ElTable } from 'element-plus'
import { VxeTableEvents, VxeTableInstance } from 'vxe-table'
import FieldRender from '@/components/table/fieldRender/index.vue'
@@ -66,19 +44,22 @@ import Column from '@/components/table/column/index.vue'
import GroupColumn from '@/components/table/column/groupColumn.vue'
import { useConfig } from '@/stores/config'
import type TableStoreClass from '@/utils/tableStore'
import { useRouter } from 'vue-router'
import { defaultAttribute } from '@/components/table/defaultAttribute'
const config = useConfig()
const tableRef = ref<VxeTableInstance>()
const tableStore = inject('tableStore') as TableStoreClass
const router = useRouter()
interface Props extends /* @vue-ignore */ Partial<InstanceType<typeof ElTable>> {
isGroup?: boolean
showOverflow?: boolean
}
const props = withDefaults(defineProps<Props>(), {
isGroup: false
isGroup: false,
showOverflow: true
})
onMounted(() => {
tableStore.table.ref = tableRef.value as VxeTableInstance
@@ -93,7 +74,7 @@ const onTableCurrentChange = (val: number) => {
}
const pageSizes = computed(() => {
let defaultSizes = [10, 20, 50, 100]
let defaultSizes = [10, 20, 50, 100, 200]
if (tableStore.table.params!.pageSize) {
if (!defaultSizes.includes(tableStore.table.params!.pageSize)) {
defaultSizes.push(tableStore.table.params!.pageSize)
@@ -113,6 +94,37 @@ const selectChangeEvent: VxeTableEvents.CheckboxChange<any> = ({ checked }) => {
const getRef = () => {
return tableRef.value
}
watch(
() => tableStore.table.allFlag,
newVal => {
if (tableStore.table.allFlag) {
tableRef.value?.exportData({
filename: document.querySelectorAll('.ba-nav-tab.active')[0].textContent || '', // 文件名字
sheetName: 'Sheet1',
type: 'xlsx', //导出文件类型 xlsx 和 csv
useStyle: true,
data: tableStore.table.allData, // 数据源 // 过滤那个字段导出
columnFilterMethod: function (column: any) {
return !(column.column.title === undefined || column.column.title === '序号' || column.column.title === '操作')
}
})
tableStore.table.allFlag = false
}
}
)
watch(
() => tableStore.table.data,
newVal => {
tableStore.onTableAction('selection-change', [])
}
)
defineExpose({
getRef

View File

@@ -14,7 +14,11 @@ import '@fortawesome/fontawesome-free/css/all.css'
import '@/styles/index.scss'
import '@/assets/font/iconfont.css'
import { ElDialog } from 'element-plus'
import ExcelJS from 'exceljs'
import VXETablePluginExportXLSX from 'vxe-table-plugin-export-xlsx'
VXETable.use(VXETablePluginExportXLSX, {
ExcelJS
})
window.XEUtils = XEUtils

35
src/utils/tableMethod.ts Normal file
View File

@@ -0,0 +1,35 @@
// 过滤表格导出数据
export const filtration = (arr: Array<{ dealFlag?: number; fileFlag?: number; onlineEvaluate?: number }>) => {
const dealFlagMap: Record<number, string> = {
0: '未处理',
1: '已处理',
2: '已处理,无结果',
3: '计算失败'
}
const fileFlagMap: Record<number, string> = {
0: '不存在',
1: '存在'
}
arr.forEach((item: any) => {
if (item.dealFlag !== undefined) {
item.dealFlag = dealFlagMap[item.dealFlag] || ''
}
if (item.fileFlag !== undefined) {
item.fileFlag = fileFlagMap[item.fileFlag] || ''
}
if (item.onlineEvaluate !== undefined) {
if (item.onlineEvaluate == null) {
} else if (item.onlineEvaluate == 3.14159) {
item.onlineEvaluate = '/'
} else if (item.onlineEvaluate * 100 > 90) {
item.onlineEvaluate = '优'
} else if (item.onlineEvaluate * 100 > 60) {
item.onlineEvaluate = '良'
} else {
item.onlineEvaluate = '差'
}
}
})
return arr
}

View File

@@ -3,19 +3,20 @@ import createAxios from '@/utils/request'
import { requestPayload } from '@/utils/request'
import { Method } from 'axios'
import { mainHeight } from '@/utils/layout'
import { filtration } from './tableMethod'
interface TableStoreParams {
url: string // 请求地址
pk?: string
column: TableColumn[]
params?: anyObj
method?: Method // 请求方式
method?: Method // 请求方式
isWebPaging?: boolean // 是否前端分页
showPage?: boolean //是否需要分页
paramsPOST?: boolean // post请求 params传参
publicHeight?: number //计算高度
resetCallback?: () => void // 重置
loadCallback?: () => void // 接口调用后的回调
loadCallback?: () => void // 接口调用后的回调
beforeSearchFun?: () => void // 接口调用前的回调
}
@@ -31,6 +32,8 @@ export default class TableStore {
ref: null,
selection: [],
data: [],
allData: [],
allFlag: false,
webPagingData: [],
total: 0,
params: {
@@ -78,26 +81,28 @@ export default class TableStore {
},
requestPayload(this.method, this.table.params, this.paramsPOST)
)
).then((res: any) => {
if (res.data) {
this.table.data = res.data.records || res.data
this.table.total = res.data?.total || res.data.length || 0
} else {
this.table.data = []
this.table.total = 0
}
if (Array.isArray(res)) {
this.table.data = res
}
if (this.isWebPaging) {
this.table.webPagingData = window.XEUtils.chunk(this.table.data, this.table.params.pageSize)
this.table.data = this.table.webPagingData[this.table.params.pageNum - 1]
}
this.table.loadCallback && this.table.loadCallback()
this.table.loading = false
}).catch(() => {
this.table.loading = false
})
)
.then((res: any) => {
if (res.data) {
this.table.data = res.data.records || res.data
this.table.total = res.data?.total || res.data.length || 0
} else {
this.table.data = []
this.table.total = 0
}
if (Array.isArray(res)) {
this.table.data = res
}
if (this.isWebPaging) {
this.table.webPagingData = window.XEUtils.chunk(this.table.data, this.table.params.pageSize)
this.table.data = this.table.webPagingData[this.table.params.pageNum - 1]
}
this.table.loadCallback && this.table.loadCallback()
this.table.loading = false
})
.catch(() => {
this.table.loading = false
})
}
/**
@@ -173,6 +178,25 @@ export default class TableStore {
() => {
console.warn('No action defined')
}
],
[
'export',
() => {
// this.index()
let params = { ...this.table.params, pageNum: 1, pageSize: this.table.total }
createAxios(
Object.assign(
{
url: this.url,
method: this.method
},
requestPayload(this.method, params, this.paramsPOST)
)
).then(res => {
this.table.allData = filtration(res.data.records || res.data)
this.table.allFlag = data.showAllFlag || true
})
}
]
])
const action = actionFun.get(event) || actionFun.get('default')

View File

@@ -1,6 +1,6 @@
<template>
<!-- 异常事件 -->
<TableHeader datePicker ref="refheader">
<TableHeader datePicker ref="refheader" showExport>
<!-- <template v-slot:select>
<el-form-item label="数据来源">
<el-cascader

View File

@@ -1,5 +1,5 @@
<template>
<TableHeader datePicker ref="refheader">
<TableHeader datePicker ref="refheader" showExport>
<template v-slot:select>
<el-form-item label="数据来源">
<el-cascader v-model.trim="tableStore.table.params.cascader" placeholder="请选择数据来源"

View File

@@ -1,5 +1,5 @@
<template>
<TableHeader datePicker ref="refheader">
<TableHeader datePicker ref="refheader" showExport>
<template v-slot:select>
<el-form-item label="数据来源">
<el-cascader v-model.trim="tableStore.table.params.cascader" placeholder="请选择数据来源"

View File

@@ -1,6 +1,6 @@
<template>
<div ref="refheader" v-if="!isWaveCharts">
<TableHeader datePicker>
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="数据来源">
<el-cascader placeholder="请选择数据来源" @change="sourceChange"

View File

@@ -223,9 +223,7 @@
{{ child.anotherName }}:
{{ child.dataValue === 3.1415926 ? '暂无数据' : child.dataValue }}
</div>
</div>
</div>
<div v-else-if="item.children.length" class="box-card-div">
<div style="display: flex; align-items: center">

View File

@@ -47,21 +47,30 @@
{{ deviceData.appCheck }}
</el-descriptions-item>
</el-descriptions>
<el-tabs v-model.trim="dataSet" type="border-card" class="device-manage-box-card" @tab-click="handleClick">
<el-tab-pane lazy :label="item.name" :name="item.id" v-for="(item, index) in deviceData.dataSetList"
:key="index"></el-tab-pane>
<div :style="{ height: tableHeight }" v-loading="tableLoading">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" style="width: 100%">
<vxe-column type="seq" title="序号" width="80"></vxe-column>
<vxe-column field="name" title="数据名称"></vxe-column>
<vxe-column field="phasic" title="相别"></vxe-column>
<vxe-column field="type" title="数据类型"></vxe-column>
<vxe-column field="unit" title="单位"></vxe-column>
<vxe-column field="startTimes" title="开始次数"></vxe-column>
<vxe-column field="endTimes" title="结束次数"></vxe-column>
</vxe-table>
</div>
</el-tabs>
<div style="position: relative">
<el-tabs v-model.trim="dataSet" type="border-card" class="device-manage-box-card"
@tab-click="handleClick">
<el-tab-pane lazy :label="item.name" :name="item.id" v-for="(item, index) in deviceData.dataSetList"
:key="index"></el-tab-pane>
<div :style="{ height: tableHeight }" v-loading="tableLoading">
<vxe-table v-bind="defaultAttribute" :data="tableData" height="auto" ref="xTableRef"
style="width: 100%">
<vxe-column type="seq" title="序号" width="80"></vxe-column>
<vxe-column field="name" title="数据名称"></vxe-column>
<vxe-column field="phasic" title="相别"></vxe-column>
<vxe-column field="type" title="数据类型"></vxe-column>
<vxe-column field="unit" title="单位"></vxe-column>
<vxe-column field="startTimes" title="开始次数"></vxe-column>
<vxe-column field="endTimes" title="结束次数"></vxe-column>
</vxe-table>
</div>
</el-tabs>
<el-button icon="el-icon-Download" type="primary" @click="exportData" class="export-btn">导出</el-button>
</div>
</div>
<el-empty v-else description="请选择设备" class="device-manage-right" />
<MangePopup ref="mangePopup" />
@@ -103,6 +112,7 @@ const tableData = ref([])
const tableHeight = mainHeight(260).height
const mangePopup = ref()
const activeName = ref(0)
const xTableRef = ref()
//治理设备和便携式设备切换判断
const deviceType = ref('0')
const deviceTypeChange = (val: any, obj: any) => {
@@ -250,6 +260,21 @@ const handleRestartDevice = () => {
deviceRestartLoading.value = false
})
}
const exportData = () => {
xTableRef.value.exportData({
filename: deviceData.value.dataSetList.filter((item: any) => item.id == dataSet.value)[0]?.name, // 文件名字
sheetName: 'Sheet1',
type: 'xlsx', //导出文件类型 xlsx 和 csv
useStyle: true,
download: false,
data: tableData.value, // 数据源 // 过滤那个字段导出
columnFilterMethod: function (column, $columnIndex) {
return !(column.$columnIndex === 0)
}
})
}
</script>
<style lang="scss">
@@ -279,4 +304,11 @@ const handleRestartDevice = () => {
-webkit-text-security: disc !important;
}
}
.export-btn {
position: absolute;
top: 4px;
right: 10px;
z-index: 10;
}
</style>

2
types/table.d.ts vendored
View File

@@ -8,6 +8,8 @@ declare global {
interface CnTable {
ref: VxeTableInstance | null
data: TableRow[] | any
allData: TableRow[] | any
allFlag: Boolean
// 前端分页数据
webPagingData: TableRow[][]
// 表格加载状态