前后端导出数据功能调试

This commit is contained in:
zhangtao
2024-12-12 19:43:03 +08:00
parent dd7f2b0a46
commit fda2b07b54
12 changed files with 228 additions and 129 deletions
@@ -93,7 +93,6 @@ async def export_obj_list(
data=bytes2file_response(export_result), data=bytes2file_response(export_result),
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers = { headers = {
'Content-Disposition': f'attachment; filename={urllib.parse.quote("导出公告通知.xlsx")}', 'Content-Disposition': 'attachment; filename=data.xlsx'
'Access-Control-Expose-Headers': 'Content-Disposition'
} }
) )
@@ -68,7 +68,6 @@ async def export_obj_list(
data=bytes2file_response(operation_log_export_result), data=bytes2file_response(operation_log_export_result),
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers = { headers = {
'Content-Disposition': f'attachment; filename={urllib.parse.quote("操作日志.xlsx")}', 'Content-Disposition': 'attachment; filename=data.xlsx'
'Access-Control-Expose-Headers': 'Content-Disposition'
} }
) )
@@ -100,7 +100,6 @@ async def export_obj_list(
data=bytes2file_response(position_export_result), data=bytes2file_response(position_export_result),
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers = { headers = {
'Content-Disposition': f'attachment; filename={urllib.parse.quote("导出岗位.xlsx")}', 'Content-Disposition': 'attachment; filename=data.xlsx'
'Access-Control-Expose-Headers': 'Content-Disposition'
} }
) )
@@ -111,7 +111,6 @@ async def export_obj_list(
data=bytes2file_response(role_export_result), data=bytes2file_response(role_export_result),
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers = { headers = {
'Content-Disposition': f'attachment; filename={urllib.parse.quote("导出角色.xlsx")}', 'Content-Disposition': 'attachment; filename=data.xlsx'
'Access-Control-Expose-Headers': 'Content-Disposition'
} }
) )
@@ -181,8 +181,7 @@ async def export_obj_list(
data=bytes2file_response(user_export_result), data=bytes2file_response(user_export_result),
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers = { headers = {
'Content-Disposition': f'attachment; filename={urllib.parse.quote("导出用户.xlsx")}', 'Content-Disposition': 'attachment; filename=data.xlsx'
'Access-Control-Expose-Headers': 'Content-Disposition'
} }
) )
Binary file not shown.
+10 -5
View File
@@ -24,10 +24,15 @@ export function deleteLog(query) {
}); });
} }
export function exportLog(body) { export function exportLog(query) {
return request({ return request({
url: "/api/v1/system/log/export", url: '/api/v1/system/log/export',
method: "post", method: 'post',
data: body, data: query,
}); responseType: 'blob',
// headers: {
// 'Content-Type': 'application/json',
// 'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
// }
})
} }
+42 -23
View File
@@ -123,8 +123,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, onMounted, h } from 'vue'; import { ref, reactive, onMounted, h } from 'vue';
import type { TableColumnsType } from 'ant-design-vue'; import type { TableColumnsType } from 'ant-design-vue';
import { SearchOutlined, DownOutlined} from '@ant-design/icons-vue'; import { SearchOutlined, DownOutlined, DownloadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import type { searchDataType, tableDataType, creatorType } from './types' import type { searchDataType, tableDataType, creatorType } from './types'
import PageHeader from '@/components/PageHeader.vue'; import PageHeader from '@/components/PageHeader.vue';
import { getLogList, deleteLog, exportLog } from '@/api/system/log' import { getLogList, deleteLog, exportLog } from '@/api/system/log'
@@ -336,28 +336,47 @@ const handleSelectorModalEvent = (selectedSelectorRowKeys?: creatorType['id'][],
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
// 构建查询参数 Modal.confirm({
const params = { title: '警告',
...queryState, content: '是否确认导出所有日志数据?',
page_no: 1, onOk() {
page_size: pagination.total // 导出所有数据 const body = {
}; ...queryState,
page_no: 1,
page_size: pagination.total
};
message.loading('正在导出数据,请稍候...', 0);
// 调用 exportLog 接口 return exportLog(body).then(response => {
exportLog(params).then(response => { const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const blob = new Blob([response.data], { type: 'application/vnd.ms-excel' }); console.log("blob", blob);
const url = window.URL.createObjectURL(blob); // 从响应头获取文件名
const a = document.createElement('a'); const contentDisposition = response.headers['content-disposition'];
a.href = url; let fileName = '系统日志.xlsx';
a.download = `log_${new Date().getTime()}.xlsx`; // 设置下载文件名 if (contentDisposition) {
document.body.appendChild(a); const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
a.click(); if (fileNameMatch) {
document.body.removeChild(a); fileName = decodeURIComponent(fileNameMatch[1]);
window.URL.revokeObjectURL(url); }
message.success('导出成功'); }
}).catch(error => { console.log(fileName);
console.error('导出失败:', error); const url = window.URL.createObjectURL(blob);
message.error('导出失败'); const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
message.destroy();
message.success('导出成功');
}).catch((error) => {
message.destroy();
console.error('导出错误:', error);
message.error('文件处理失败');
});
},
onCancel() {
message.info('已取消导出');
}
}); });
}; };
+59 -36
View File
@@ -10,7 +10,8 @@
<a-row> <a-row>
<a-col flex="0 1 450px"> <a-col flex="0 1 450px">
<a-form-item name="name" label="名称" style="max-width: 300px;"> <a-form-item name="name" label="名称" style="max-width: 300px;">
<a-input v-model:value="queryState.notice_title" placeholder="请输入标题" allowClear></a-input> <a-input v-model:value="queryState.notice_title" placeholder="请输入标题"
allowClear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col flex="0 1 450px"> <a-col flex="0 1 450px">
@@ -37,13 +38,9 @@
<a-card title="公告通知列表" :bordered="false" :headStyle="{ borderBottom: 'none', padding: '20px 24px' }" <a-card title="公告通知列表" :bordered="false" :headStyle="{ borderBottom: 'none', padding: '20px 24px' }"
:bodyStyle="{ padding: '0 24px', minHeight: 'calc(100vh - 400px)' }"> :bodyStyle="{ padding: '0 24px', minHeight: 'calc(100vh - 400px)' }">
<template #extra> <template #extra>
<a-button type="primary" <a-button type="primary" :icon="h(PlusOutlined)" @click="modalHandle('create')"
:icon="h(PlusOutlined)"
@click="modalHandle('create')"
style="margin-right: 10px;">新建</a-button> style="margin-right: 10px;">新建</a-button>
<a-button type="primary" <a-button type="primary" :icon="h(DownOutlined)" @click="handleExport"
:icon="h(DownOutlined)"
@click="handleExport"
style="margin-right: 10px;">导出 style="margin-right: 10px;">导出
</a-button> </a-button>
<a-dropdown> <a-dropdown>
@@ -105,7 +102,8 @@
<a-modal v-model:open="openModal" @ok="handleModalSumbit" :width="800" :destroyOnClose="true" <a-modal v-model:open="openModal" @ok="handleModalSumbit" :width="800" :destroyOnClose="true"
:confirmLoading="modalSubmitLoading" style="top: 30px"> :confirmLoading="modalSubmitLoading" style="top: 30px">
<template #title> <template #title>
<span>{{ modalTitle === 'create' ? '新建公告通知' : (modalTitle === 'view' ? '查看公告通知' : '修改公告通知') }}</span> <span>{{ modalTitle === 'create' ? '新建公告通知' : (modalTitle === 'view' ? '查看公告通知' : '修改公告通知')
}}</span>
</template> </template>
<div v-if="modalTitle === 'view'"> <div v-if="modalTitle === 'view'">
<a-spin :spinning="detailStateLoading"> <a-spin :spinning="detailStateLoading">
@@ -115,15 +113,18 @@
detailState.index + 1 }}</a-descriptions-item> detailState.index + 1 }}</a-descriptions-item>
<a-descriptions-item label="标题">{{ detailState.notice_title }}</a-descriptions-item> <a-descriptions-item label="标题">{{ detailState.notice_title }}</a-descriptions-item>
<a-descriptions-item label="类型"> <a-descriptions-item label="类型">
<a-tag :color="detailState.notice_type === 1 ? '#108ee9' : '#f8e231'">{{ detailState.notice_type === 1 ? '通知' : '公告' }}</a-tag> <a-tag :color="detailState.notice_type === 1 ? '#108ee9' : '#f8e231'">{{
detailState.notice_type
=== 1 ? '通知' : '公告' }}</a-tag>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="内容">{{ detailState.notice_content }}</a-descriptions-item> <a-descriptions-item label="内容">{{ detailState.notice_content }}</a-descriptions-item>
<a-descriptions-item label="状态"> <a-descriptions-item label="状态">
<a-badge :color="detailState.available ? 'green' : 'red'" />{{ detailState.available ? <a-badge :color="detailState.available ? 'green' : 'red'" />{{ detailState.available ?
'启用' : '启用' :
'禁用' }} '禁用' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="创建人">{{ detailState.creator ? detailState.creator.name : '-' }}</a-descriptions-item> <a-descriptions-item label="创建人">{{ detailState.creator ? detailState.creator.name : '-'
}}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{ detailState.created_at }}</a-descriptions-item> <a-descriptions-item label="创建时间">{{ detailState.created_at }}</a-descriptions-item>
<a-descriptions-item label="修改时间">{{ detailState.updated_at }}</a-descriptions-item> <a-descriptions-item label="修改时间">{{ detailState.updated_at }}</a-descriptions-item>
<a-descriptions-item label="备注" :span="2">{{ detailState.description <a-descriptions-item label="备注" :span="2">{{ detailState.description
@@ -144,7 +145,8 @@
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item name="notice_content" label="内容" :rules="[{ required: true, message: '请输入内容' }]"> <a-form-item name="notice_content" label="内容" :rules="[{ required: true, message: '请输入内容' }]">
<a-input v-model:value="createState.notice_content" placeholder="请输入内容" allowClear></a-input> <a-input v-model:value="createState.notice_content" placeholder="请输入内容"
allowClear></a-input>
</a-form-item> </a-form-item>
<a-form-item name="available" label="状态" :rules="[{ required: true, message: '请选择状态' }]"> <a-form-item name="available" label="状态" :rules="[{ required: true, message: '请选择状态' }]">
<a-radio-group v-model:value="createState.available"> <a-radio-group v-model:value="createState.available">
@@ -171,7 +173,8 @@
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item name="notice_content" label="内容" :rules="[{ required: true, message: '请输入内容' }]"> <a-form-item name="notice_content" label="内容" :rules="[{ required: true, message: '请输入内容' }]">
<a-input v-model:value="updateState.notice_content" placeholder="请输入内容" allowClear></a-input> <a-input v-model:value="updateState.notice_content" placeholder="请输入内容"
allowClear></a-input>
</a-form-item> </a-form-item>
<a-form-item name="available" label="状态" :rules="[{ required: true, message: '请选择状态' }]"> <a-form-item name="available" label="状态" :rules="[{ required: true, message: '请选择状态' }]">
<a-radio-group v-model:value="updateState.available"> <a-radio-group v-model:value="updateState.available">
@@ -197,7 +200,7 @@ import type { TableColumnsType, MenuProps } from 'ant-design-vue';
import { PlusOutlined, DownOutlined, CheckOutlined, StopOutlined } from '@ant-design/icons-vue'; import { PlusOutlined, DownOutlined, CheckOutlined, StopOutlined } from '@ant-design/icons-vue';
import { cloneDeep, isEmpty } from '@/utils/util'; import { cloneDeep, isEmpty } from '@/utils/util';
import PageHeader from '@/components/PageHeader.vue'; import PageHeader from '@/components/PageHeader.vue';
import { getNoticeList, createNotice, updateNotice, deleteNotice, batchAvailableNotice, exportNotice} from '@/api/system/notice' import { getNoticeList, createNotice, updateNotice, deleteNotice, batchAvailableNotice, exportNotice } from '@/api/system/notice'
import type { searchDataType, tableDataType } from './types' import type { searchDataType, tableDataType } from './types'
import XLSX from 'xlsx'; import XLSX from 'xlsx';
@@ -478,30 +481,50 @@ const handleModalSumbit = () => {
}) })
} }
} }
// 导出按钮操作 // 导出按钮操作
const handleExport = () => { const handleExport = () => {
// 构建查询参数 Modal.confirm({
const params = { title: '警告',
...queryState, content: '是否确认导出所有公告通知数据?',
page_no: 1, onOk() {
page_size: pagination.total // 导出所有数据 const body = {
}; ...queryState,
page_no: 1,
page_size: pagination.total
};
message.loading('正在导出数据,请稍候...', 0);
// 调用 exportLog 接口 return exportNotice(body).then(response => {
exportNotice(params).then(response => { const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const blob = new Blob([response.data], { type: 'application/vnd.ms-excel' }); console.log("blob", blob);
const url = window.URL.createObjectURL(blob); // 从响应头获取文件名
const a = document.createElement('a'); const contentDisposition = response.headers['content-disposition'];
a.href = url; let fileName = '公告通知.xlsx';
a.download = `notice_${new Date().getTime()}.xlsx`; // 设置下载文件名 if (contentDisposition) {
document.body.appendChild(a); const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
a.click(); if (fileNameMatch) {
document.body.removeChild(a); fileName = decodeURIComponent(fileNameMatch[1]);
window.URL.revokeObjectURL(url); }
message.success('导出成功'); }
}).catch(error => { console.log(fileName);
console.error('导出失败:', error); const url = window.URL.createObjectURL(blob);
message.error('导出失败'); const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
message.destroy();
message.success('导出成功');
}).catch((error) => {
message.destroy();
console.error('导出错误:', error);
message.error('文件处理失败');
});
},
onCancel() {
message.info('已取消导出');
}
}); });
}; };
+42 -23
View File
@@ -455,31 +455,50 @@ const handleModalSumbit = () => {
} }
} }
/** 导出按钮操作 */ // 导出按钮操作
const handleExport = () => { const handleExport = () => {
// 构建查询参数 Modal.confirm({
const params = { title: '警告',
...queryState, content: '是否确认导出所有岗位数据?',
page_no: 1, onOk() {
page_size: pagination.total // 导出所有数据 const body = {
}; ...queryState,
page_no: 1,
page_size: pagination.total
};
message.loading('正在导出数据,请稍候...', 0);
// 调用 exportLog 接口 return exportPosition(body).then(response => {
exportPosition(params).then(response => { const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const blob = new Blob([response.data], { type: 'application/vnd.ms-excel' }); console.log("blob", blob);
const url = window.URL.createObjectURL(blob); // 从响应头获取文件名
const a = document.createElement('a'); const contentDisposition = response.headers['content-disposition'];
a.href = url; let fileName = '岗位.xlsx';
a.download = `position_${new Date().getTime()}.xlsx`; // 设置下载文件名 if (contentDisposition) {
document.body.appendChild(a); const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
a.click(); if (fileNameMatch) {
document.body.removeChild(a); fileName = decodeURIComponent(fileNameMatch[1]);
window.URL.revokeObjectURL(url); }
message.success('导出成功'); }
}).catch(error => { console.log(fileName);
console.error('导出失败:', error); const url = window.URL.createObjectURL(blob);
message.error('导出失败'); const link = document.createElement('a');
}); link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
message.destroy();
message.success('导出成功');
}).catch((error) => {
message.destroy();
console.error('导出错误:', error);
message.error('文件处理失败');
});
},
onCancel() {
message.info('已取消导出');
}
});
}; };
</script> </script>
+43 -24
View File
@@ -464,33 +464,52 @@ const handleModalSumbit = () => {
} }
} }
/** 导出按钮操作 */
const handleExport = () => {
// 构建查询参数
const params = {
...queryState,
page_no: 1,
page_size: pagination.total // 导出所有数据
};
// 调用 exportLog 接口 // 导出按钮操作
exportRole(params).then(response => { const handleExport = () => {
const blob = new Blob([response.data], { type: 'application/vnd.ms-excel' }); Modal.confirm({
const url = window.URL.createObjectURL(blob); title: '警告',
const a = document.createElement('a'); content: '是否确认导出所有角色数据?',
a.href = url; onOk() {
a.download = `role_${new Date().getTime()}.xlsx`; // 设置下载文件名 const body = {
document.body.appendChild(a); ...queryState,
a.click(); page_no: 1,
document.body.removeChild(a); page_size: pagination.total
window.URL.revokeObjectURL(url); };
message.success('导出成功'); message.loading('正在导出数据,请稍候...', 0);
}).catch(error => {
console.error('导出失败:', error); return exportRole(body).then(response => {
message.error('导出失败'); const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
console.log("blob", blob);
// 从响应头获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName = '角色.xlsx';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
if (fileNameMatch) {
fileName = decodeURIComponent(fileNameMatch[1]);
}
}
console.log(fileName);
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
message.destroy();
message.success('导出成功');
}).catch((error) => {
message.destroy();
console.error('导出错误:', error);
message.error('文件处理失败');
});
},
onCancel() {
message.info('已取消导出');
}
}); });
}; };
// 授权弹窗事件 // 授权弹窗事件
const handlePermissionDrawerEvent = () => loadingData(); const handlePermissionDrawerEvent = () => loadingData();
</script> </script>
+26 -7
View File
@@ -820,23 +820,42 @@ const handleExport = () => {
title: '警告', title: '警告',
content: '是否确认导出所有用户数据?', content: '是否确认导出所有用户数据?',
onOk() { onOk() {
const params = { const body = {
...queryState, ...queryState,
page_no: 1, page_no: 1,
page_size: pagination.total page_size: pagination.total
}; };
return exportUser(params).then(response => { message.loading('正在导出数据,请稍候...', 0);
const blob = new Blob([response], { type: 'application/vnd.ms-excel' });
return exportUser(body).then(response => {
const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
console.log("blob", blob);
// 从响应头获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName = '用户.xlsx';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
if (fileNameMatch) {
fileName = decodeURIComponent(fileNameMatch[1]);
}
}
console.log(fileName);
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = `用户数据_${new Date().getTime()}.xlsx`; link.download = fileName;
document.body.appendChild(link);
link.click(); link.click();
window.URL.revokeObjectURL(url); message.destroy();
message.success('导出成功'); message.success('导出成功');
}).catch(() => { }).catch((error) => {
message.error('导出失败'); message.destroy();
console.error('导出错误:', error);
message.error('文件处理失败');
}); });
},
onCancel() {
message.info('已取消导出');
} }
}); });
}; };