no message
parent
316d40908d
commit
efb8bc00f8
|
|
@ -10,11 +10,12 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider
|
<a-config-provider
|
||||||
:locale="antdLocale"
|
:locale="antdLocale"
|
||||||
:theme="{
|
:theme="{
|
||||||
algorithm: compactFlag ? theme.compactAlgorithm : theme.defaultAlgorithm,
|
algorithm: compactFlag ? theme.compactAlgorithm : theme.defaultAlgorithm,
|
||||||
token: {
|
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,
|
colorPrimary: themeColors[colorIndex].primaryColor,
|
||||||
colorLink: themeColors[colorIndex].primaryColor,
|
colorLink: themeColors[colorIndex].primaryColor,
|
||||||
colorLinkActive: themeColors[colorIndex].activeColor,
|
colorLinkActive: themeColors[colorIndex].activeColor,
|
||||||
|
|
@ -33,68 +34,86 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
:transformCellText="transformCellText"
|
:transformCellText="transformCellText"
|
||||||
>
|
>
|
||||||
<!---全局loading--->
|
<!---全局loading--->
|
||||||
<a-spin :spinning="spinning" tip="稍等片刻,我在拼命加载中..." size="large">
|
<a-spin :spinning="spinning" tip="稍等片刻,我在拼命加载中..." size="large">
|
||||||
<!--- 路由 -->
|
<!--- 路由 -->
|
||||||
<RouterView />
|
<RouterView/>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-config-provider>
|
</a-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { computed, h, useSlots } from 'vue';
|
import {computed, h, useSlots, watch} from 'vue';
|
||||||
import { messages } from '/@/i18n';
|
import {messages} from '/@/i18n';
|
||||||
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
import {useAppConfigStore} from '/@/store/modules/system/app-config';
|
||||||
import { useSpinStore } from '/@/store/modules/system/spin';
|
import {useSpinStore} from '/@/store/modules/system/spin';
|
||||||
import { theme } from 'ant-design-vue';
|
import {theme} from 'ant-design-vue';
|
||||||
import { themeColors } from '/@/theme/color.js';
|
import {themeColors} from '/@/theme/color.js';
|
||||||
import { Popover } from 'ant-design-vue';
|
import {Popover} from 'ant-design-vue';
|
||||||
import SmartCopyIcon from '/@/components/framework/smart-copy-icon/index.vue';
|
import SmartCopyIcon from '/@/components/framework/smart-copy-icon/index.vue';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
|
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
|
||||||
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
|
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
|
||||||
dayjs.locale(dayjsLocale);
|
dayjs.locale(dayjsLocale);
|
||||||
|
|
||||||
// 全局loading
|
// 全局loading
|
||||||
let spinStore = useSpinStore();
|
let spinStore = useSpinStore();
|
||||||
const spinning = computed(() => spinStore.loading);
|
const spinning = computed(() => spinStore.loading);
|
||||||
// 是否紧凑
|
// 是否紧凑
|
||||||
const compactFlag = computed(() => useAppConfigStore().compactFlag);
|
const compactFlag = computed(() => useAppConfigStore().compactFlag);
|
||||||
// 主题颜色
|
// 主题颜色
|
||||||
const colorIndex = computed(() => {
|
const colorIndex = computed(() => {
|
||||||
return useAppConfigStore().colorIndex;
|
return useAppConfigStore().colorIndex;
|
||||||
});
|
});
|
||||||
// 圆角
|
// 圆角
|
||||||
const borderRadius = computed(() => {
|
const borderRadius = computed(() => {
|
||||||
return useAppConfigStore().borderRadius;
|
return useAppConfigStore().borderRadius;
|
||||||
});
|
});
|
||||||
function transformCellText({ text, column, record, index }) {
|
|
||||||
if (column && column.textEllipsisFlag === true) {
|
function transformCellText({text, column, record, index}) {
|
||||||
return h(
|
if (column && column.textEllipsisFlag === true) {
|
||||||
|
return h(
|
||||||
Popover,
|
Popover,
|
||||||
{ placement: 'bottom' },
|
{placement: 'bottom'},
|
||||||
{
|
{
|
||||||
default: () =>
|
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: () =>
|
content: () =>
|
||||||
h('div', { style: { display: 'flex' } }, [
|
h('div', {style: {display: 'flex'}}, [
|
||||||
h('div', text),
|
h('div', text),
|
||||||
h(SmartCopyIcon, { value: document.getElementById(`${column.dataIndex}${index}`).innerText }),
|
h(SmartCopyIcon, {value: document.getElementById(`${column.dataIndex}${index}`).innerText}),
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return text;
|
return text;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听主题颜色变化,更新CSS变量
|
||||||
|
/*watch(() => colorIndex.value, (newVal) => {
|
||||||
|
document.documentElement.style.setProperty('--primary-color', themeColors[newVal].primaryColor);
|
||||||
|
}, { immediate: true });*/
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
:deep(.ant-table-column-sorters) {
|
/*:deep(.ant-table-column-sorters) {
|
||||||
align-items: flex-start !important;
|
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>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* @Date: 2024-12-26 15:35:23
|
* @Date: 2024-12-26 15:35:23
|
||||||
* @Copyright 友仓
|
* @Copyright 友仓
|
||||||
*/
|
*/
|
||||||
import {postRequest, getRequest, getDownload} from '/@/lib/axios';
|
import {postRequest, getRequest, getDownload2} from '/@/lib/axios';
|
||||||
|
|
||||||
export const addressApi = {
|
export const addressApi = {
|
||||||
|
|
||||||
|
|
@ -71,8 +71,8 @@ export const addressApi = {
|
||||||
/**
|
/**
|
||||||
* 导出 @author hj
|
* 导出 @author hj
|
||||||
*/
|
*/
|
||||||
exportAddress: (taskId: string) => {
|
exportAddress: (taskId: string,signal?: AbortSignal) => {
|
||||||
return getDownload(`/address/exportAddress/${taskId}`, {});
|
return getDownload2(`/address/exportAddress/${taskId}`, {},signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* @Date: 2024-11-25 17:08:18
|
* @Date: 2024-11-25 17:08:18
|
||||||
* @Copyright 友仓
|
* @Copyright 友仓
|
||||||
*/
|
*/
|
||||||
import {postRequest, getRequest, getDownload} from '/@/lib/axios';
|
import {postRequest, getRequest, getDownload2} from '/@/lib/axios';
|
||||||
|
|
||||||
export const itemApi = {
|
export const itemApi = {
|
||||||
|
|
||||||
|
|
@ -62,8 +62,8 @@ export const itemApi = {
|
||||||
/**
|
/**
|
||||||
* 导出 @author hj
|
* 导出 @author hj
|
||||||
*/
|
*/
|
||||||
exportItems: (taskId: string) => {
|
exportItems: (taskId: string,signal?: AbortSignal) => {
|
||||||
return getDownload(`/item/exportItems/${taskId}`,{});
|
return getDownload2(`/item/exportItems/${taskId}`,{},signal);
|
||||||
}
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
* @Email: lab1024@163.com
|
* @Email: lab1024@163.com
|
||||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
* @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 axios from 'axios';
|
||||||
import { localRead } from '/@/utils/local-util';
|
import {localRead} from '/@/utils/local-util';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import {useUserStore} from '/@/store/modules/system/user';
|
||||||
import { decryptData, encryptData } from './encrypt';
|
import {decryptData, encryptData} from './encrypt';
|
||||||
import { DATA_TYPE_ENUM } from '../constants/common-const';
|
import {DATA_TYPE_ENUM} from '../constants/common-const';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
|
|
@ -21,109 +21,109 @@ const TOKEN_HEADER = 'Authorization';
|
||||||
|
|
||||||
// 创建axios对象
|
// 创建axios对象
|
||||||
const smartAxios = axios.create({
|
const smartAxios = axios.create({
|
||||||
baseURL: import.meta.env.VITE_APP_API_URL,
|
baseURL: import.meta.env.VITE_APP_API_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 退出系统
|
// 退出系统
|
||||||
function logout() {
|
function logout() {
|
||||||
useUserStore().logout();
|
useUserStore().logout();
|
||||||
location.href = '/';
|
location.href = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================= 请求拦截器 =================================
|
// ================================= 请求拦截器 =================================
|
||||||
|
|
||||||
smartAxios.interceptors.request.use(
|
smartAxios.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
// 在发送请求之前消息头加入token token
|
// 在发送请求之前消息头加入token token
|
||||||
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers[TOKEN_HEADER] = 'Bearer ' + token;
|
config.headers[TOKEN_HEADER] = 'Bearer ' + token;
|
||||||
} else {
|
} else {
|
||||||
delete config.headers[TOKEN_HEADER];
|
delete config.headers[TOKEN_HEADER];
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
// 对请求错误做些什么
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// ================================= 响应拦截器 =================================
|
// ================================= 响应拦截器 =================================
|
||||||
|
|
||||||
// 添加响应拦截器
|
// 添加响应拦截器
|
||||||
smartAxios.interceptors.response.use(
|
smartAxios.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
// 根据content-type ,判断是否为 json 数据
|
// 根据content-type ,判断是否为 json 数据
|
||||||
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
|
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
|
||||||
if (contentType.indexOf('application/json') === -1) {
|
if (contentType.indexOf('application/json') === -1) {
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是json数据
|
// 如果是json数据
|
||||||
if (response.data && response.data instanceof Blob) {
|
if (response.data && response.data instanceof Blob) {
|
||||||
return Promise.reject(response.data);
|
return Promise.reject(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是加密数据
|
// 如果是加密数据
|
||||||
if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
|
if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
|
||||||
response.data.encryptData = response.data.data;
|
response.data.encryptData = response.data.data;
|
||||||
let decryptStr = decryptData(response.data.data);
|
let decryptStr = decryptData(response.data.data);
|
||||||
if (decryptStr) {
|
if (decryptStr) {
|
||||||
response.data.data = JSON.parse(decryptStr);
|
response.data.data = JSON.parse(decryptStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = response.data;
|
const res = response.data;
|
||||||
if (res.code && res.code !== 1) {
|
if (res.code && res.code !== 1) {
|
||||||
// `token` 过期或者账号已在别处登录
|
// `token` 过期或者账号已在别处登录
|
||||||
if (res.code === 30007 || res.code === 30008) {
|
if (res.code === 30007 || res.code === 30008) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error('您没有登录,请重新登录');
|
message.error('您没有登录,请重新登录');
|
||||||
setTimeout(logout, 300);
|
setTimeout(logout, 300);
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等保安全的登录提醒
|
// 等保安全的登录提醒
|
||||||
if (res.code === 30010 || res.code === 30011) {
|
if (res.code === 30010 || res.code === 30011) {
|
||||||
Modal.error({
|
Modal.error({
|
||||||
title: '重要提醒',
|
title: '重要提醒',
|
||||||
content: res.msg,
|
content: res.msg,
|
||||||
});
|
});
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长时间未操作系统,需要重新登录
|
// 长时间未操作系统,需要重新登录
|
||||||
if (res.code === 30012) {
|
if (res.code === 30012) {
|
||||||
Modal.error({
|
Modal.error({
|
||||||
title: '重要提醒',
|
title: '重要提醒',
|
||||||
content: res.msg,
|
content: res.msg,
|
||||||
onOk: logout,
|
onOk: logout,
|
||||||
});
|
});
|
||||||
setTimeout(logout, 3000);
|
setTimeout(logout, 3000);
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
}
|
}
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error(res.msg);
|
message.error(res.msg);
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(res);
|
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等 =================================
|
// ================================= 对外提供请求方法:通用请求,get, post, 下载download等 =================================
|
||||||
|
|
@ -132,7 +132,7 @@ smartAxios.interceptors.response.use(
|
||||||
* get请求
|
* get请求
|
||||||
*/
|
*/
|
||||||
export const getRequest = (url, params) => {
|
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
|
* @param config
|
||||||
*/
|
*/
|
||||||
export const request = (config) => {
|
export const request = (config) => {
|
||||||
return smartAxios.request(config);
|
return smartAxios.request(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post请求
|
* post请求
|
||||||
*/
|
*/
|
||||||
export const postRequest = (url, data) => {
|
export const postRequest = (url, data) => {
|
||||||
return request({
|
return request({
|
||||||
data,
|
data,
|
||||||
url,
|
url,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// ================================= 加密 =================================
|
// ================================= 加密 =================================
|
||||||
|
|
@ -160,91 +160,107 @@ export const postRequest = (url, data) => {
|
||||||
* 加密请求参数的post请求
|
* 加密请求参数的post请求
|
||||||
*/
|
*/
|
||||||
export const postEncryptRequest = (url, data) => {
|
export const postEncryptRequest = (url, data) => {
|
||||||
return request({
|
return request({
|
||||||
data: { encryptData: encryptData(data) },
|
data: {encryptData: encryptData(data)},
|
||||||
url,
|
url,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// ================================= 下载 =================================
|
// ================================= 下载 =================================
|
||||||
|
|
||||||
export const postDownload = function (url, data) {
|
export const postDownload = function (url, data) {
|
||||||
request({
|
request({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
handleDownloadData(data);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.then((data) => {
|
||||||
handleDownloadError(error);
|
handleDownloadData(data);
|
||||||
});
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
handleDownloadError(error);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件下载
|
* 文件下载
|
||||||
*/
|
*/
|
||||||
export const getDownload = function (url, params) {
|
export const getDownload = function (url, params) {
|
||||||
request({
|
request({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url,
|
url,
|
||||||
params,
|
params,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
handleDownloadData(data);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.then((data) => {
|
||||||
handleDownloadError(error);
|
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) {
|
function handleDownloadError(error) {
|
||||||
if (error instanceof Blob) {
|
if (error instanceof Blob) {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fileReader.readAsText(error);
|
fileReader.readAsText(error);
|
||||||
fileReader.onload = () => {
|
fileReader.onload = () => {
|
||||||
const msg = fileReader.result;
|
const msg = fileReader.result;
|
||||||
const jsonMsg = JSON.parse(msg);
|
const jsonMsg = JSON.parse(msg);
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error(jsonMsg.msg);
|
message.error(jsonMsg.msg);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error('网络发生错误', error);
|
message.error('网络发生错误', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDownloadData(response) {
|
function handleDownloadData(response) {
|
||||||
if (!response) {
|
if (!response) {
|
||||||
return;
|
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 url = window.URL.createObjectURL(new Blob([response.data], {type: contentType}));
|
||||||
let link = document.createElement('a');
|
let link = document.createElement('a');
|
||||||
link.style.display = 'none';
|
link.style.display = 'none';
|
||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
||||||
// 从消息头获取文件名
|
// 从消息头获取文件名
|
||||||
let str = _.isUndefined(response.headers['content-disposition'])
|
let str = _.isUndefined(response.headers['content-disposition'])
|
||||||
? response.headers['Content-Disposition'].split(';')[1]
|
? response.headers['Content-Disposition'].split(';')[1]
|
||||||
: 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];
|
let filename = _.isUndefined(str.split('fileName=')[1]) ? str.split('filename=')[1] : str.split('fileName=')[1];
|
||||||
link.setAttribute('download', decodeURIComponent(filename));
|
link.setAttribute('download', decodeURIComponent(filename));
|
||||||
|
|
||||||
// 触发点击下载
|
// 触发点击下载
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
// 下载完释放
|
// 下载完释放
|
||||||
document.body.removeChild(link); // 下载完成移除元素
|
document.body.removeChild(link); // 下载完成移除元素
|
||||||
window.URL.revokeObjectURL(url); // 释放掉blob对象
|
window.URL.revokeObjectURL(url); // 释放掉blob对象
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,63 @@
|
||||||
export const themeColors = [
|
export const themeColors = [
|
||||||
// 蓝色
|
// 蓝色
|
||||||
{
|
{
|
||||||
primaryColor: '#1677ff',
|
primaryColor: '#1677ff',
|
||||||
activeColor: '#0958d9',
|
activeColor: '#0958d9',
|
||||||
hoverColor: '#4096ff',
|
hoverColor: '#4096ff',
|
||||||
},
|
},
|
||||||
// 蓝色2
|
// 蓝色2
|
||||||
{
|
{
|
||||||
primaryColor: '#2F54EB',
|
primaryColor: '#2F54EB',
|
||||||
activeColor: '#1d39c4',
|
activeColor: '#1d39c4',
|
||||||
hoverColor: '#597ef7',
|
hoverColor: '#597ef7',
|
||||||
},
|
},
|
||||||
// 绿色
|
// 绿色
|
||||||
{
|
{
|
||||||
primaryColor: '#00b96b',
|
primaryColor: '#00b96b',
|
||||||
activeColor: '#00945b',
|
activeColor: '#00945b',
|
||||||
hoverColor: '#20c77c',
|
hoverColor: '#20c77c',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 红色
|
// 红色
|
||||||
{
|
{
|
||||||
primaryColor: '#F5222D',
|
primaryColor: '#F5222D',
|
||||||
activeColor: '#cf1322',
|
activeColor: '#cf1322',
|
||||||
hoverColor: '#ff4d4f',
|
hoverColor: '#ff4d4f',
|
||||||
},
|
},
|
||||||
// 青色
|
// 青色
|
||||||
{
|
{
|
||||||
primaryColor: '#13c2c2',
|
primaryColor: '#13c2c2',
|
||||||
activeColor: '#08979c',
|
activeColor: '#08979c',
|
||||||
hoverColor: '#36cfc9',
|
hoverColor: '#36cfc9',
|
||||||
},
|
},
|
||||||
// 粉色
|
// 粉色
|
||||||
{
|
{
|
||||||
primaryColor: '#EB2F96',
|
primaryColor: '#EB2F96',
|
||||||
activeColor: '#c41d7f',
|
activeColor: '#c41d7f',
|
||||||
hoverColor: '#f759ab',
|
hoverColor: '#f759ab',
|
||||||
},
|
},
|
||||||
// 紫色
|
// 紫色
|
||||||
{
|
{
|
||||||
primaryColor: '#722ED1',
|
primaryColor: '#722ED1',
|
||||||
activeColor: '#531dab',
|
activeColor: '#531dab',
|
||||||
hoverColor: '#9254de',
|
hoverColor: '#9254de',
|
||||||
},
|
},
|
||||||
// 极光绿
|
// 极光绿
|
||||||
{
|
{
|
||||||
primaryColor: '#52c41a',
|
primaryColor: '#52c41a',
|
||||||
activeColor: '#389e0d',
|
activeColor: '#389e0d',
|
||||||
hoverColor: '#73d13d',
|
hoverColor: '#73d13d',
|
||||||
},
|
},
|
||||||
// 深绿
|
// 深绿
|
||||||
{
|
{
|
||||||
primaryColor: '#009688',
|
primaryColor: '#009688',
|
||||||
activeColor: '#007069',
|
activeColor: '#007069',
|
||||||
hoverColor: '#1aa391',
|
hoverColor: '#1aa391',
|
||||||
},
|
},
|
||||||
// 橙色
|
// 橙色
|
||||||
{
|
{
|
||||||
primaryColor: '#fa541c',
|
primaryColor: '#fa541c',
|
||||||
activeColor: '#d4380d',
|
activeColor: '#d4380d',
|
||||||
hoverColor: '#ff7a45',
|
hoverColor: '#ff7a45',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="open"
|
v-model:open="open"
|
||||||
@cancel="onClose"
|
@cancel="onCancel"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:destroyOnClose="true">
|
:destroyOnClose="true">
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button @click="onClose">关闭</a-button>
|
<a-button @click="onCancel">取消</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
bordered
|
bordered
|
||||||
:loading="tableLoading"
|
:loading="tableLoading"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1500, y: 350 }"
|
:scroll="{ x: 1500, y: 450 }"
|
||||||
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
|
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ text, record, column }">
|
<template #bodyCell="{ text, record, column }">
|
||||||
|
|
@ -443,53 +443,102 @@ async function onImportAddress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//导出
|
//导出
|
||||||
const progressTitle = ref('文件下载中,请稍等...');
|
const progressTitle = ref('文件下载中,请稍等...');//标题
|
||||||
const progressPercent = ref(0);//进度条初始值
|
const progressPercent = ref(0);//进度百分比
|
||||||
const progressStatus = ref('active');//进度条状态
|
const progressStatus = ref('active');
|
||||||
const currentTaskId = ref('');//当前任务ID
|
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 () => {
|
const onExportAddress = async () => {
|
||||||
|
if (isExporting.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
isExporting.value = true;
|
||||||
|
abortController.value = new AbortController(); // 创建控制器
|
||||||
open.value = true;
|
open.value = true;
|
||||||
//获取导出任务ID
|
resetProgressState(); // 重置进度状态
|
||||||
|
|
||||||
|
// 创建导出任务
|
||||||
const {data: taskId} = await addressApi.createExportTask();
|
const {data: taskId} = await addressApi.createExportTask();
|
||||||
currentTaskId.value = taskId;
|
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 {
|
try {
|
||||||
//获取当前任务的进度条
|
|
||||||
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
|
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
|
||||||
|
|
||||||
progressPercent.value = progress;
|
progressPercent.value = progress;
|
||||||
|
|
||||||
if (progress >= 100) {
|
if (progress >= 100) {
|
||||||
clearInterval(timer);
|
handleExportSuccess();
|
||||||
progressStatus.value = 'success';
|
|
||||||
progressTitle.value = '文件下载完成';
|
|
||||||
// 2秒后重置状态
|
|
||||||
setTimeout(() => {
|
|
||||||
open.value = false;
|
|
||||||
progressPercent.value = 0;
|
|
||||||
progressTitle.value = '文件下载中,请稍等...';
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clearInterval(timer);
|
handleExportError();
|
||||||
progressStatus.value = 'exception';
|
|
||||||
progressPercent.value = 0;
|
|
||||||
}
|
}
|
||||||
}, 1000); // 每秒轮询一次
|
}, 1000);
|
||||||
|
|
||||||
} catch (error) {
|
} 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;
|
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);
|
onMounted(queryData);
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
|
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="open"
|
v-model:open="open"
|
||||||
@cancel="onClose"
|
@cancel="onCancel"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:destroyOnClose="true">
|
:destroyOnClose="true">
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button @click="onClose">关闭</a-button>
|
<a-button @click="onCancel">取消</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
@ -498,97 +498,103 @@ async function onImportItems() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//导出
|
//导出
|
||||||
const progressTitle = ref('文件下载中,请稍等...');
|
const progressTitle = ref('文件下载中,请稍等...');//标题
|
||||||
const progressPercent = ref(0);
|
const progressPercent = ref(0);//进度百分比
|
||||||
const progressStatus = ref('active');
|
const progressStatus = ref('active');
|
||||||
const currentTaskId = ref('');
|
const currentTaskId = ref('');//当前任务ID
|
||||||
const open = ref(false);
|
const open = ref(false);//打开进度条窗口
|
||||||
const pollTimer = ref<NodeJS.Timeout>(); // 使用ref保存定时器引用
|
const timerId = ref<NodeJS.Timeout | null>(null); // 定时器引用
|
||||||
|
const isExporting = ref(false); // 防止重复提交
|
||||||
|
const abortController = ref<AbortController | null>(null);// 用于取消请求
|
||||||
|
|
||||||
// 重置所有进度状态
|
const onExportItems = async () => {
|
||||||
const resetProgress = () => {
|
if (isExporting.value) return;
|
||||||
progressPercent.value = 0;
|
|
||||||
progressStatus.value = 'active';
|
|
||||||
progressTitle.value = '文件下载中,请稍等...';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 轮询检查进度
|
|
||||||
const startProgressPolling = async (taskId: string) => {
|
|
||||||
try {
|
try {
|
||||||
// 启动轮询(调整为更合理的1秒间隔)
|
isExporting.value = true;
|
||||||
pollTimer.value = setInterval(async () => {
|
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 {
|
try {
|
||||||
const { data: progress } = await addressApi.getExportProgress(taskId);
|
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
|
||||||
|
|
||||||
progressPercent.value = progress;
|
progressPercent.value = progress;
|
||||||
|
|
||||||
if (progress >= 100) {
|
if (progress >= 100) {
|
||||||
handleComplete();
|
handleExportSuccess();
|
||||||
clearInterval(pollTimer.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handlePollingError();
|
handleExportError();
|
||||||
clearInterval(pollTimer.value);
|
|
||||||
}
|
}
|
||||||
}, 200); // 调整为1秒减少请求压力
|
}, 1000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handlePollingError();
|
handleExportError();
|
||||||
|
} finally {
|
||||||
|
isExporting.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理完成状态
|
// 处理导出成功
|
||||||
const handleComplete = () => {
|
const handleExportSuccess = () => {
|
||||||
|
if (timerId.value) {
|
||||||
|
clearInterval(timerId.value);
|
||||||
|
}
|
||||||
progressStatus.value = 'success';
|
progressStatus.value = 'success';
|
||||||
progressTitle.value = '文件下载完成';
|
progressTitle.value = '文件下载完成';
|
||||||
|
|
||||||
// 立即关闭模态框,使用提示告知用户
|
setTimeout(() => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
resetProgress();
|
resetProgressState();
|
||||||
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理轮询错误
|
// 处理导出失败
|
||||||
const handlePollingError = () => {
|
const handleExportError = () => {
|
||||||
|
if (timerId.value) clearInterval(timerId.value);
|
||||||
progressStatus.value = 'exception';
|
progressStatus.value = 'exception';
|
||||||
progressPercent.value = 0;
|
progressPercent.value = 0;
|
||||||
progressTitle.value = '获取进度失败';
|
open.value = false;
|
||||||
|
resetProgressState();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onExportItems = async () => {
|
// 重置进度状态
|
||||||
try {
|
const resetProgressState = () => {
|
||||||
// 清除已有定时器防止重复请求
|
progressPercent.value = 0;
|
||||||
if (pollTimer.value) clearInterval(pollTimer.value);
|
progressStatus.value = 'active';
|
||||||
|
progressTitle.value = '文件下载中,请稍等...';
|
||||||
|
currentTaskId.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
// 重置状态(包括清除旧任务ID)
|
//取消
|
||||||
resetProgress();
|
const onCancel = () => {
|
||||||
currentTaskId.value = '';
|
// 中止进行中的请求
|
||||||
open.value = true;
|
open.value = false;
|
||||||
|
if (abortController.value) {
|
||||||
// 获取任务ID
|
abortController.value.abort();
|
||||||
const { data: taskId } = await addressApi.createExportTask();
|
abortController.value = null;
|
||||||
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 onClose = () => {
|
|
||||||
resetProgress();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(queryData);
|
onMounted(queryData);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue