no message

main
HUOJIN\霍先森 2025-05-10 14:43:49 +08:00
parent 316d40908d
commit efb8bc00f8
7 changed files with 454 additions and 364 deletions

View File

@ -10,11 +10,12 @@
<template>
<a-config-provider
:locale="antdLocale"
:theme="{
:locale="antdLocale"
:theme="{
algorithm: compactFlag ? theme.compactAlgorithm : theme.defaultAlgorithm,
token: {
fontFamily: '微软雅黑, Arial, sans-serif',
fontFamily:'-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji',
fontsize: '14px',
colorPrimary: themeColors[colorIndex].primaryColor,
colorLink: themeColors[colorIndex].primaryColor,
colorLinkActive: themeColors[colorIndex].activeColor,
@ -33,68 +34,86 @@
},
},
}"
:transformCellText="transformCellText"
:transformCellText="transformCellText"
>
<!---全局loading--->
<a-spin :spinning="spinning" tip="稍等片刻,我在拼命加载中..." size="large">
<!--- 路由 -->
<RouterView />
<RouterView/>
</a-spin>
</a-config-provider>
</template>
<script setup lang="ts">
import dayjs from 'dayjs';
import { computed, h, useSlots } from 'vue';
import { messages } from '/@/i18n';
import { useAppConfigStore } from '/@/store/modules/system/app-config';
import { useSpinStore } from '/@/store/modules/system/spin';
import { theme } from 'ant-design-vue';
import { themeColors } from '/@/theme/color.js';
import { Popover } from 'ant-design-vue';
import SmartCopyIcon from '/@/components/framework/smart-copy-icon/index.vue';
import _ from 'lodash';
import dayjs from 'dayjs';
import {computed, h, useSlots, watch} from 'vue';
import {messages} from '/@/i18n';
import {useAppConfigStore} from '/@/store/modules/system/app-config';
import {useSpinStore} from '/@/store/modules/system/spin';
import {theme} from 'ant-design-vue';
import {themeColors} from '/@/theme/color.js';
import {Popover} from 'ant-design-vue';
import SmartCopyIcon from '/@/components/framework/smart-copy-icon/index.vue';
import _ from 'lodash';
const slots = useSlots();
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
dayjs.locale(dayjsLocale);
const slots = useSlots();
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
dayjs.locale(dayjsLocale);
// loading
let spinStore = useSpinStore();
const spinning = computed(() => spinStore.loading);
//
const compactFlag = computed(() => useAppConfigStore().compactFlag);
//
const colorIndex = computed(() => {
return useAppConfigStore().colorIndex;
});
//
const borderRadius = computed(() => {
return useAppConfigStore().borderRadius;
});
function transformCellText({ text, column, record, index }) {
if (column && column.textEllipsisFlag === true) {
return h(
// loading
let spinStore = useSpinStore();
const spinning = computed(() => spinStore.loading);
//
const compactFlag = computed(() => useAppConfigStore().compactFlag);
//
const colorIndex = computed(() => {
return useAppConfigStore().colorIndex;
});
//
const borderRadius = computed(() => {
return useAppConfigStore().borderRadius;
});
function transformCellText({text, column, record, index}) {
if (column && column.textEllipsisFlag === true) {
return h(
Popover,
{ placement: 'bottom' },
{placement: 'bottom'},
{
default: () =>
h('div', { style: { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }, id: `${column.dataIndex}${index}` }, text),
h('div', {
style: {whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'},
id: `${column.dataIndex}${index}`
}, text),
content: () =>
h('div', { style: { display: 'flex' } }, [
h('div', text),
h(SmartCopyIcon, { value: document.getElementById(`${column.dataIndex}${index}`).innerText }),
]),
h('div', {style: {display: 'flex'}}, [
h('div', text),
h(SmartCopyIcon, {value: document.getElementById(`${column.dataIndex}${index}`).innerText}),
]),
}
);
} else {
return text;
}
);
} else {
return text;
}
}
// CSS
/*watch(() => colorIndex.value, (newVal) => {
document.documentElement.style.setProperty('--primary-color', themeColors[newVal].primaryColor);
}, { immediate: true });*/
</script>
<style scoped lang="less">
:deep(.ant-table-column-sorters) {
align-items: flex-start !important;
}
/*:deep(.ant-table-column-sorters) {
align-items: flex-start !important;
}*/
/*:deep(.ant-table-thead > tr > th) {
background-color: var(--primary-color) !important;
color: black !important;
}*/
:deep(.ant-table-thead > tr > th) {
background-color: #FAFAFA !important;
}
</style>

View File

@ -5,7 +5,7 @@
* @Date: 2024-12-26 15:35:23
* @Copyright
*/
import {postRequest, getRequest, getDownload} from '/@/lib/axios';
import {postRequest, getRequest, getDownload2} from '/@/lib/axios';
export const addressApi = {
@ -71,8 +71,8 @@ export const addressApi = {
/**
* @author hj
*/
exportAddress: (taskId: string) => {
return getDownload(`/address/exportAddress/${taskId}`, {});
exportAddress: (taskId: string,signal?: AbortSignal) => {
return getDownload2(`/address/exportAddress/${taskId}`, {},signal);
}
};

View File

@ -5,7 +5,7 @@
* @Date: 2024-11-25 17:08:18
* @Copyright
*/
import {postRequest, getRequest, getDownload} from '/@/lib/axios';
import {postRequest, getRequest, getDownload2} from '/@/lib/axios';
export const itemApi = {
@ -62,8 +62,8 @@ export const itemApi = {
/**
* @author hj
*/
exportItems: (taskId: string) => {
return getDownload(`/item/exportItems/${taskId}`,{});
}
exportItems: (taskId: string,signal?: AbortSignal) => {
return getDownload2(`/item/exportItems/${taskId}`,{},signal);
},
};

View File

@ -7,12 +7,12 @@
* @Email: lab1024@163.com
* @Copyright 1024 https://1024lab.net Since 2012
*/
import { message, Modal } from 'ant-design-vue';
import {message, Modal} from 'ant-design-vue';
import axios from 'axios';
import { localRead } from '/@/utils/local-util';
import { useUserStore } from '/@/store/modules/system/user';
import { decryptData, encryptData } from './encrypt';
import { DATA_TYPE_ENUM } from '../constants/common-const';
import {localRead} from '/@/utils/local-util';
import {useUserStore} from '/@/store/modules/system/user';
import {decryptData, encryptData} from './encrypt';
import {DATA_TYPE_ENUM} from '../constants/common-const';
import _ from 'lodash';
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
@ -21,109 +21,109 @@ const TOKEN_HEADER = 'Authorization';
// 创建axios对象
const smartAxios = axios.create({
baseURL: import.meta.env.VITE_APP_API_URL,
baseURL: import.meta.env.VITE_APP_API_URL,
});
// 退出系统
function logout() {
useUserStore().logout();
location.href = '/';
useUserStore().logout();
location.href = '/';
}
// ================================= 请求拦截器 =================================
smartAxios.interceptors.request.use(
(config) => {
// 在发送请求之前消息头加入token token
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
if (token) {
config.headers[TOKEN_HEADER] = 'Bearer ' + token;
} else {
delete config.headers[TOKEN_HEADER];
(config) => {
// 在发送请求之前消息头加入token token
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
if (token) {
config.headers[TOKEN_HEADER] = 'Bearer ' + token;
} else {
delete config.headers[TOKEN_HEADER];
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// ================================= 响应拦截器 =================================
// 添加响应拦截器
smartAxios.interceptors.response.use(
(response) => {
// 根据content-type ,判断是否为 json 数据
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
if (contentType.indexOf('application/json') === -1) {
return Promise.resolve(response);
}
(response) => {
// 根据content-type ,判断是否为 json 数据
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
if (contentType.indexOf('application/json') === -1) {
return Promise.resolve(response);
}
// 如果是json数据
if (response.data && response.data instanceof Blob) {
return Promise.reject(response.data);
}
// 如果是json数据
if (response.data && response.data instanceof Blob) {
return Promise.reject(response.data);
}
// 如果是加密数据
if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
response.data.encryptData = response.data.data;
let decryptStr = decryptData(response.data.data);
if (decryptStr) {
response.data.data = JSON.parse(decryptStr);
}
}
// 如果是加密数据
if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
response.data.encryptData = response.data.data;
let decryptStr = decryptData(response.data.data);
if (decryptStr) {
response.data.data = JSON.parse(decryptStr);
}
}
const res = response.data;
if (res.code && res.code !== 1) {
// `token` 过期或者账号已在别处登录
if (res.code === 30007 || res.code === 30008) {
message.destroy();
message.error('您没有登录,请重新登录');
setTimeout(logout, 300);
return Promise.reject(response);
}
const res = response.data;
if (res.code && res.code !== 1) {
// `token` 过期或者账号已在别处登录
if (res.code === 30007 || res.code === 30008) {
message.destroy();
message.error('您没有登录,请重新登录');
setTimeout(logout, 300);
return Promise.reject(response);
}
// 等保安全的登录提醒
if (res.code === 30010 || res.code === 30011) {
Modal.error({
title: '重要提醒',
content: res.msg,
});
return Promise.reject(response);
}
// 等保安全的登录提醒
if (res.code === 30010 || res.code === 30011) {
Modal.error({
title: '重要提醒',
content: res.msg,
});
return Promise.reject(response);
}
// 长时间未操作系统,需要重新登录
if (res.code === 30012) {
Modal.error({
title: '重要提醒',
content: res.msg,
onOk: logout,
});
setTimeout(logout, 3000);
return Promise.reject(response);
}
message.destroy();
message.error(res.msg);
return Promise.reject(response);
} else {
return Promise.resolve(res);
// 长时间未操作系统,需要重新登录
if (res.code === 30012) {
Modal.error({
title: '重要提醒',
content: res.msg,
onOk: logout,
});
setTimeout(logout, 3000);
return Promise.reject(response);
}
message.destroy();
message.error(res.msg);
return Promise.reject(response);
} else {
return Promise.resolve(res);
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf('timeout') !== -1) {
message.destroy();
message.error('网络超时');
} else if (error.message === 'Network Error') {
message.destroy();
message.error('网络连接错误');
} else if (error.message.indexOf('Request') !== -1) {
message.destroy();
message.error('网络发生错误');
}
return Promise.reject(error);
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf('timeout') !== -1) {
message.destroy();
message.error('网络超时');
} else if (error.message === 'Network Error') {
message.destroy();
message.error('网络连接错误');
} else if (error.message.indexOf('Request') !== -1) {
message.destroy();
message.error('网络发生错误');
}
return Promise.reject(error);
}
);
// ================================= 对外提供请求方法通用请求get post, 下载download等 =================================
@ -132,7 +132,7 @@ smartAxios.interceptors.response.use(
* get
*/
export const getRequest = (url, params) => {
return request({ url, method: 'get', params });
return request({url, method: 'get', params});
};
/**
@ -140,18 +140,18 @@ export const getRequest = (url, params) => {
* @param config
*/
export const request = (config) => {
return smartAxios.request(config);
return smartAxios.request(config);
};
/**
* post
*/
export const postRequest = (url, data) => {
return request({
data,
url,
method: 'post',
});
return request({
data,
url,
method: 'post',
});
};
// ================================= 加密 =================================
@ -160,91 +160,107 @@ export const postRequest = (url, data) => {
* post
*/
export const postEncryptRequest = (url, data) => {
return request({
data: { encryptData: encryptData(data) },
url,
method: 'post',
});
return request({
data: {encryptData: encryptData(data)},
url,
method: 'post',
});
};
// ================================= 下载 =================================
export const postDownload = function (url, data) {
request({
method: 'post',
url,
data,
responseType: 'blob',
})
.then((data) => {
handleDownloadData(data);
request({
method: 'post',
url,
data,
responseType: 'blob',
})
.catch((error) => {
handleDownloadError(error);
});
.then((data) => {
handleDownloadData(data);
})
.catch((error) => {
handleDownloadError(error);
});
};
/**
*
*/
export const getDownload = function (url, params) {
request({
method: 'get',
url,
params,
responseType: 'blob',
})
.then((data) => {
handleDownloadData(data);
request({
method: 'get',
url,
params,
responseType: 'blob',
})
.catch((error) => {
handleDownloadError(error);
});
.then((data) => {
handleDownloadData(data);
})
.catch((error) => {
handleDownloadError(error);
});
};
export const getDownload2 = function (url: string, params: any, signal?: AbortSignal) {
request({
method: 'get',
url,
params,
responseType: 'blob',
signal // 注入取消信号
})
.then((data) => {
handleDownloadData(data);
})
.catch((error) => {
message.success('取消成功');
});
};
function handleDownloadError(error) {
if (error instanceof Blob) {
const fileReader = new FileReader();
fileReader.readAsText(error);
fileReader.onload = () => {
const msg = fileReader.result;
const jsonMsg = JSON.parse(msg);
message.destroy();
message.error(jsonMsg.msg);
};
} else {
message.destroy();
message.error('网络发生错误', error);
}
if (error instanceof Blob) {
const fileReader = new FileReader();
fileReader.readAsText(error);
fileReader.onload = () => {
const msg = fileReader.result;
const jsonMsg = JSON.parse(msg);
message.destroy();
message.error(jsonMsg.msg);
};
} else {
message.destroy();
message.error('网络发生错误', error);
}
}
function handleDownloadData(response) {
if (!response) {
return;
}
if (!response) {
return;
}
// 获取返回类型
let contentType = _.isUndefined(response.headers['content-type']) ? response.headers['Content-Type'] : response.headers['content-type'];
// 获取返回类型
let contentType = _.isUndefined(response.headers['content-type']) ? response.headers['Content-Type'] : response.headers['content-type'];
// 构建下载数据
let url = window.URL.createObjectURL(new Blob([response.data], { type: contentType }));
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
// 构建下载数据
let url = window.URL.createObjectURL(new Blob([response.data], {type: contentType}));
let link = document.createElement('a');
link.style.display = 'none';
link.href = url;
// 从消息头获取文件名
let str = _.isUndefined(response.headers['content-disposition'])
? response.headers['Content-Disposition'].split(';')[1]
: response.headers['content-disposition'].split(';')[1];
// 从消息头获取文件名
let str = _.isUndefined(response.headers['content-disposition'])
? response.headers['Content-Disposition'].split(';')[1]
: response.headers['content-disposition'].split(';')[1];
let filename = _.isUndefined(str.split('fileName=')[1]) ? str.split('filename=')[1] : str.split('fileName=')[1];
link.setAttribute('download', decodeURIComponent(filename));
let filename = _.isUndefined(str.split('fileName=')[1]) ? str.split('filename=')[1] : str.split('fileName=')[1];
link.setAttribute('download', decodeURIComponent(filename));
// 触发点击下载
document.body.appendChild(link);
link.click();
// 触发点击下载
document.body.appendChild(link);
link.click();
// 下载完释放
document.body.removeChild(link); // 下载完成移除元素
window.URL.revokeObjectURL(url); // 释放掉blob对象
// 下载完释放
document.body.removeChild(link); // 下载完成移除元素
window.URL.revokeObjectURL(url); // 释放掉blob对象
}

View File

@ -1,63 +1,63 @@
export const themeColors = [
// 蓝色
{
primaryColor: '#1677ff',
activeColor: '#0958d9',
hoverColor: '#4096ff',
},
// 蓝色2
{
primaryColor: '#2F54EB',
activeColor: '#1d39c4',
hoverColor: '#597ef7',
},
// 绿色
{
primaryColor: '#00b96b',
activeColor: '#00945b',
hoverColor: '#20c77c',
},
// 蓝色
{
primaryColor: '#1677ff',
activeColor: '#0958d9',
hoverColor: '#4096ff',
},
// 蓝色2
{
primaryColor: '#2F54EB',
activeColor: '#1d39c4',
hoverColor: '#597ef7',
},
// 绿色
{
primaryColor: '#00b96b',
activeColor: '#00945b',
hoverColor: '#20c77c',
},
// 红色
{
primaryColor: '#F5222D',
activeColor: '#cf1322',
hoverColor: '#ff4d4f',
},
// 青色
{
primaryColor: '#13c2c2',
activeColor: '#08979c',
hoverColor: '#36cfc9',
},
// 粉色
{
primaryColor: '#EB2F96',
activeColor: '#c41d7f',
hoverColor: '#f759ab',
},
// 紫色
{
primaryColor: '#722ED1',
activeColor: '#531dab',
hoverColor: '#9254de',
},
// 极光绿
{
primaryColor: '#52c41a',
activeColor: '#389e0d',
hoverColor: '#73d13d',
},
// 深绿
{
primaryColor: '#009688',
activeColor: '#007069',
hoverColor: '#1aa391',
},
// 橙色
{
primaryColor: '#fa541c',
activeColor: '#d4380d',
hoverColor: '#ff7a45',
},
// 红色
{
primaryColor: '#F5222D',
activeColor: '#cf1322',
hoverColor: '#ff4d4f',
},
// 青色
{
primaryColor: '#13c2c2',
activeColor: '#08979c',
hoverColor: '#36cfc9',
},
// 粉色
{
primaryColor: '#EB2F96',
activeColor: '#c41d7f',
hoverColor: '#f759ab',
},
// 紫色
{
primaryColor: '#722ED1',
activeColor: '#531dab',
hoverColor: '#9254de',
},
// 极光绿
{
primaryColor: '#52c41a',
activeColor: '#389e0d',
hoverColor: '#73d13d',
},
// 深绿
{
primaryColor: '#009688',
activeColor: '#007069',
hoverColor: '#1aa391',
},
// 橙色
{
primaryColor: '#fa541c',
activeColor: '#d4380d',
hoverColor: '#ff7a45',
},
];

View File

@ -59,7 +59,7 @@
<a-modal
v-model:open="open"
@cancel="onClose"
@cancel="onCancel"
:closable="false"
:maskClosable="false"
:destroyOnClose="true">
@ -75,7 +75,7 @@
</template>
<template #footer>
<a-space>
<a-button @click="onClose"></a-button>
<a-button @click="onCancel"></a-button>
</a-space>
</template>
</a-modal>
@ -101,7 +101,7 @@
bordered
:loading="tableLoading"
:pagination="false"
:scroll="{ x: 1500, y: 350 }"
:scroll="{ x: 1500, y: 450 }"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
>
<template #bodyCell="{ text, record, column }">
@ -443,53 +443,102 @@ async function onImportAddress() {
}
//
const progressTitle = ref('文件下载中,请稍等...');
const progressPercent = ref(0);//
const progressStatus = ref('active');//
const progressTitle = ref('文件下载中,请稍等...');//
const progressPercent = ref(0);//
const progressStatus = ref('active');
const currentTaskId = ref('');//ID
const open = ref<boolean>(false);//
const open = ref(false);//
const timerId = ref<NodeJS.Timeout | null>(null); //
const isExporting = ref(false); //
const abortController = ref<AbortController | null>(null);//
const onExportAddress = async () => {
if (isExporting.value) return;
try {
isExporting.value = true;
abortController.value = new AbortController(); //
open.value = true;
//ID
resetProgressState(); //
//
const {data: taskId} = await addressApi.createExportTask();
currentTaskId.value = taskId;
progressStatus.value = 'active';
//
await addressApi.exportAddress(currentTaskId.value);
//
try {
addressApi.exportAddress(taskId, abortController.value.signal)
} catch (error) {
handleExportError();
}
//
if (timerId.value) {
clearInterval(timerId.value);
}
//
const timer = setInterval(async () => {
timerId.value = setInterval(async () => {
try {
//
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
progressPercent.value = progress;
if (progress >= 100) {
clearInterval(timer);
progressStatus.value = 'success';
progressTitle.value = '文件下载完成';
// 2
setTimeout(() => {
open.value = false;
progressPercent.value = 0;
progressTitle.value = '文件下载中,请稍等...';
}, 2000);
handleExportSuccess();
}
} catch (error) {
clearInterval(timer);
progressStatus.value = 'exception';
progressPercent.value = 0;
handleExportError();
}
}, 1000); //
}, 1000);
} catch (error) {
message.error('导出失败');
handleExportError();
} finally {
isExporting.value = false;
}
};
const onClose = () => {
//
const handleExportSuccess = () => {
if (timerId.value) {
clearInterval(timerId.value);
}
progressStatus.value = 'success';
progressTitle.value = '文件下载完成';
setTimeout(() => {
open.value = false;
resetProgressState();
}, 1000);
};
//
const handleExportError = () => {
if (timerId.value) clearInterval(timerId.value);
progressStatus.value = 'exception';
progressPercent.value = 0;
open.value = false;
resetProgressState();
};
//
const resetProgressState = () => {
progressPercent.value = 0;
progressStatus.value = 'active';
progressTitle.value = '文件下载中,请稍等...';
currentTaskId.value = '';
};
//
const onCancel = () => {
//
open.value = false;
if (abortController.value) {
abortController.value.abort();
abortController.value = null;
}
};
onMounted(queryData);

View File

@ -65,7 +65,7 @@
<a-modal
v-model:open="open"
@cancel="onClose"
@cancel="onCancel"
:closable="false"
:maskClosable="false"
:destroyOnClose="true">
@ -81,7 +81,7 @@
</template>
<template #footer>
<a-space>
<a-button @click="onClose"></a-button>
<a-button @click="onCancel"></a-button>
</a-space>
</template>
</a-modal>
@ -498,97 +498,103 @@ async function onImportItems() {
}
//
const progressTitle = ref('文件下载中,请稍等...');
const progressPercent = ref(0);
const progressTitle = ref('文件下载中,请稍等...');//
const progressPercent = ref(0);//
const progressStatus = ref('active');
const currentTaskId = ref('');
const open = ref(false);
const pollTimer = ref<NodeJS.Timeout>(); // 使ref
const currentTaskId = ref('');//ID
const open = ref(false);//
const timerId = ref<NodeJS.Timeout | null>(null); //
const isExporting = ref(false); //
const abortController = ref<AbortController | null>(null);//
//
const resetProgress = () => {
progressPercent.value = 0;
progressStatus.value = 'active';
progressTitle.value = '文件下载中,请稍等...';
};
const onExportItems = async () => {
if (isExporting.value) return;
//
const startProgressPolling = async (taskId: string) => {
try {
// 1
pollTimer.value = setInterval(async () => {
isExporting.value = true;
abortController.value = new AbortController(); //
open.value = true;
resetProgressState(); //
//
const {data: taskId} = await addressApi.createExportTask();
currentTaskId.value = taskId;
//
try {
itemApi.exportItems(taskId, abortController.value.signal)
} catch (error) {
handleExportError();
}
//
if (timerId.value) {
clearInterval(timerId.value);
}
//
timerId.value = setInterval(async () => {
try {
const { data: progress } = await addressApi.getExportProgress(taskId);
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
progressPercent.value = progress;
if (progress >= 100) {
handleComplete();
clearInterval(pollTimer.value);
handleExportSuccess();
}
} catch (error) {
handlePollingError();
clearInterval(pollTimer.value);
handleExportError();
}
}, 200); // 1
}, 1000);
} catch (error) {
handlePollingError();
handleExportError();
} finally {
isExporting.value = false;
}
};
//
const handleComplete = () => {
//
const handleExportSuccess = () => {
if (timerId.value) {
clearInterval(timerId.value);
}
progressStatus.value = 'success';
progressTitle.value = '文件下载完成';
// 使
open.value = false;
resetProgress();
setTimeout(() => {
open.value = false;
resetProgressState();
}, 1000);
};
//
const handlePollingError = () => {
//
const handleExportError = () => {
if (timerId.value) clearInterval(timerId.value);
progressStatus.value = 'exception';
progressPercent.value = 0;
progressTitle.value = '获取进度失败';
open.value = false;
resetProgressState();
};
const onExportItems = async () => {
try {
//
if (pollTimer.value) clearInterval(pollTimer.value);
//
const resetProgressState = () => {
progressPercent.value = 0;
progressStatus.value = 'active';
progressTitle.value = '文件下载中,请稍等...';
currentTaskId.value = '';
};
// ID
resetProgress();
currentTaskId.value = '';
open.value = true;
// ID
const { data: taskId } = await addressApi.createExportTask();
currentTaskId.value = taskId;
//
await Promise.race([
itemApi.exportItems(taskId),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('导出超时')), 30_000)
)
]);
//
await startProgressPolling(taskId);
} catch (error: any) {
open.value = false;
message.error('导出失败: ' + error.message);
//
if (pollTimer.value) clearInterval(pollTimer.value);
resetProgress();
//
const onCancel = () => {
//
open.value = false;
if (abortController.value) {
abortController.value.abort();
abortController.value = null;
}
};
const onClose = () => {
resetProgress();
};
onMounted(queryData);
</script>