| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- <template>
- <el-button plain size="small" @click="internalVisible = true">
- 导出数据
- </el-button>
- <el-dialog
- v-model="internalVisible"
- center
- :close-on-click-modal="false"
- destroy-on-close
- :title="dialogTitle"
- width="480px"
- :before-close="handleClose"
- >
- <el-form ref="formRef" :model="form" :rules="rules" @submit.prevent>
- <el-row justify="center">
- <el-form-item prop="startPage">
- <el-input
- v-model="form.startPage"
- placeholder="请输入开始页码"
- clearable
- style="max-width: 120px"
- @keyup.enter="handleExport"
- />
- </el-form-item>
- <span style="margin: 5px 10px 0">至</span>
- <el-form-item prop="endPage">
- <el-input
- v-model="form.endPage"
- placeholder="请输入结束页码"
- clearable
- style="max-width: 120px"
- @keyup.enter="handleExport"
- />
- </el-form-item>
- </el-row>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="handleClose">取消</el-button>
- <el-button type="primary" :loading="loading" @click="handleExport">
- 导出
- </el-button>
- </div>
- </template>
- </el-dialog>
- </template>
- <script setup lang="ts">
- import { ref, reactive, computed } from "vue";
- import type { FormInstance, FormRules } from "element-plus";
- import { ElMessage } from "element-plus";
- import dayjs from "dayjs";
- import { http } from "@/utils/http";
- // 定义接口
- interface ExportForm {
- startPage: any;
- endPage: any;
- }
- // Props 定义
- const props = withDefaults(
- defineProps<{
- title?: string;
- url: string;
- fileName: string;
- queryParams?: any;
- likeTable?: boolean;
- }>(),
- {
- title: "导出数据",
- likeTable: false
- }
- );
- // 响应式数据
- const internalVisible = ref(false);
- const loading = ref(false);
- const formRef = ref<FormInstance>();
- // 计算属性
- const dialogTitle = computed(() => props.title);
- // 表单数据
- const form = reactive<ExportForm>({
- startPage: "1",
- endPage: "10"
- });
- /**
- * @description 从1开始正整数校验
- * @param val
- * @returns {boolean}
- */
- const isNumbAvailable = (val: any): boolean => {
- let nubReg = /^[1-9]\d*$/;
- return nubReg.test(val);
- };
- // 自定义验证函数
- const validatePageNumber = (rule: any, value: string, callback: any) => {
- if (!value) {
- callback(new Error("请输入页码"));
- return;
- }
- if (!isNumbAvailable(value)) {
- callback(new Error("请输入有效的正整数"));
- return;
- }
- callback();
- };
- const validateEndPage = (rule: any, value: string, callback: any) => {
- if (!value) {
- callback(new Error("请输入结束页码"));
- return;
- }
- if (!isNumbAvailable(value)) {
- callback(new Error("请输入有效的正整数"));
- return;
- }
- const startPageNum = parseInt(form.startPage);
- const endPageNum = parseInt(value);
- if (endPageNum < startPageNum) {
- callback(new Error("结束页不能小于开始页"));
- return;
- }
- callback();
- };
- // 表单验证规则
- const rules = reactive<FormRules<ExportForm>>({
- startPage: [{ validator: validatePageNumber, trigger: "blur" }],
- endPage: [{ validator: validateEndPage, trigger: "blur" }]
- });
- /**
- * 下载文件
- * @param blob 文件blob对象
- * @param fileName 文件名
- */
- const downloadFile = (blob: Blob, fileName: string) => {
- try {
- const url = window.URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.href = url;
- link.download = fileName;
- link.style.display = "none";
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(url);
- } catch (error) {
- console.error("文件下载失败:", error);
- ElMessage.error("文件下载失败");
- }
- };
- /**
- * 生成文件名
- * @param baseName 基础文件名
- * @param extension 文件扩展名
- * @returns 完整文件名
- */
- const generateFileName = (
- baseName: string,
- extension: string = "xlsx"
- ): string => {
- const timestamp = dayjs().format("YYYYMMDD_HHmmss");
- return `${baseName}_${timestamp}.${extension}`;
- };
- // 关闭对话框
- const handleClose = () => {
- internalVisible.value = false;
- resetForm();
- };
- // 重置表单
- const resetForm = () => {
- if (formRef.value) {
- formRef.value.resetFields();
- }
- form.startPage = "1";
- form.endPage = "10";
- loading.value = false;
- };
- const calculateNum = (param: any) => {
- if (!param.pageSize) {
- param.pageSize = 10;
- return true;
- }
- if (param.startPage < 0) {
- ElMessage.warning("行数必须大于0");
- return false;
- }
- if (Number(param.startPage) > Number(param.endPage)) {
- ElMessage.warning("结束行不能小于开始行");
- return false;
- }
- if ((param.endPage - param.startPage) * param.pageSize > 500000) {
- ElMessage.warning("导出行数不能超过5万");
- return false;
- }
- return true;
- };
- // 处理导出
- const handleExport = async () => {
- if (!formRef.value) return;
- try {
- // 验证表单
- await formRef.value.validate();
- loading.value = true;
- let params = { ...props.queryParams };
- if (!props.likeTable) {
- params.endPage = form.endPage;
- params.startPage = form.startPage;
- if (!calculateNum(params)) {
- return;
- }
- } else {
- let rows =
- (form.endPage - form.startPage + 1) * props.queryParams.pageSize;
- params.pageNum = form.startPage;
- params.pageSize = Math.abs(rows);
- }
- if (params.reportDate) {
- // 将param.queryTime转为字符串,隔开
- params.reportDate = params.reportDate.join();
- }
- if (params.multipleDate) {
- params.multipleDate = params.multipleDate.join();
- }
- // 调用导出接口
- const blob = await http.postExport<Blob, any>(props.url, { data: params });
- // 生成文件名
- const fullFileName = generateFileName(props.fileName);
- // 下载文件
- downloadFile(blob, fullFileName);
- // 显示成功消息
- ElMessage.success(`导出成功!文件名:${fullFileName}`);
- // 关闭对话框
- handleClose();
- } catch (error) {
- console.error("导出失败:", error);
- // 根据错误类型显示不同的错误信息
- let errorMessage = "导出失败,请稍后重试";
- if (error?.response) {
- // HTTP错误
- const status = error.response.status;
- switch (status) {
- case 400:
- errorMessage = "请求参数错误";
- break;
- case 401:
- errorMessage = "未授权,请重新登录";
- break;
- case 403:
- errorMessage = "没有导出权限";
- break;
- case 404:
- errorMessage = "导出接口不存在";
- break;
- case 500:
- errorMessage = "服务器内部错误";
- break;
- default:
- errorMessage = `导出失败 (${status})`;
- }
- } else if (error?.code === "NETWORK_ERROR") {
- errorMessage = "网络连接失败,请检查网络";
- } else if (error?.message) {
- errorMessage = error.message;
- }
- ElMessage.error(errorMessage);
- } finally {
- loading.value = false;
- }
- };
- </script>
- <style scoped>
- .dialog-footer {
- display: flex;
- gap: 12px;
- justify-content: flex-end;
- }
- :deep(.el-form-item__label) {
- font-weight: 500;
- }
- :deep(.el-input__inner) {
- text-align: center;
- }
- </style>
|