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