no message
							parent
							
								
									316d40908d
								
							
						
					
					
						commit
						efb8bc00f8
					
				| 
						 | 
					@ -14,7 +14,8 @@
 | 
				
			||||||
      :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,
 | 
				
			||||||
| 
						 | 
					@ -45,7 +46,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<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';
 | 
				
			||||||
| 
						 | 
					@ -73,6 +74,7 @@
 | 
				
			||||||
const borderRadius = computed(() => {
 | 
					const borderRadius = computed(() => {
 | 
				
			||||||
  return useAppConfigStore().borderRadius;
 | 
					  return useAppConfigStore().borderRadius;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function transformCellText({text, column, record, index}) {
 | 
					function transformCellText({text, column, record, index}) {
 | 
				
			||||||
  if (column && column.textEllipsisFlag === true) {
 | 
					  if (column && column.textEllipsisFlag === true) {
 | 
				
			||||||
    return h(
 | 
					    return h(
 | 
				
			||||||
| 
						 | 
					@ -80,7 +82,10 @@
 | 
				
			||||||
        {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),
 | 
				
			||||||
| 
						 | 
					@ -92,9 +97,23 @@
 | 
				
			||||||
    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);
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,6 +202,22 @@ export const getDownload = function (url, params) {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
					 | 
				
			||||||
    // 启动轮询
 | 
					 | 
				
			||||||
    const timer = setInterval(async () => {
 | 
					 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        //获取当前任务的进度条
 | 
					      addressApi.exportAddress(taskId, abortController.value.signal)
 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
        clearInterval(timer);
 | 
					      handleExportError();
 | 
				
			||||||
        progressStatus.value = 'exception';
 | 
					    }
 | 
				
			||||||
        progressPercent.value = 0;
 | 
					
 | 
				
			||||||
 | 
					    // 清理之前的定时器
 | 
				
			||||||
 | 
					    if (timerId.value) {
 | 
				
			||||||
 | 
					      clearInterval(timerId.value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 启动轮询
 | 
				
			||||||
 | 
					    timerId.value = setInterval(async () => {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        progressPercent.value = progress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (progress >= 100) {
 | 
				
			||||||
 | 
					          handleExportSuccess();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }, 1000); // 每秒轮询一次
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      } catch (error) {
 | 
					      } catch (error) {
 | 
				
			||||||
    message.error('导出失败');
 | 
					        handleExportError();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  } catch (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;
 | 
					    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);
 | 
					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,96 +498,102 @@ 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 resetProgress = () => {
 | 
					 | 
				
			||||||
  progressPercent.value = 0;
 | 
					 | 
				
			||||||
  progressStatus.value = 'active';
 | 
					 | 
				
			||||||
  progressTitle.value = '文件下载中,请稍等...';
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 轮询检查进度
 | 
					 | 
				
			||||||
const startProgressPolling = async (taskId: string) => {
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    // 启动轮询(调整为更合理的1秒间隔)
 | 
					 | 
				
			||||||
    pollTimer.value = setInterval(async () => {
 | 
					 | 
				
			||||||
      try {
 | 
					 | 
				
			||||||
        const { data: progress } = await addressApi.getExportProgress(taskId);
 | 
					 | 
				
			||||||
        progressPercent.value = progress;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (progress >= 100) {
 | 
					 | 
				
			||||||
          handleComplete();
 | 
					 | 
				
			||||||
          clearInterval(pollTimer.value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } catch (error) {
 | 
					 | 
				
			||||||
        handlePollingError();
 | 
					 | 
				
			||||||
        clearInterval(pollTimer.value);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }, 200); // 调整为1秒减少请求压力
 | 
					 | 
				
			||||||
  } catch (error) {
 | 
					 | 
				
			||||||
    handlePollingError();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 处理完成状态
 | 
					 | 
				
			||||||
const handleComplete = () => {
 | 
					 | 
				
			||||||
  progressStatus.value = 'success';
 | 
					 | 
				
			||||||
  progressTitle.value = '文件下载完成';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // 立即关闭模态框,使用提示告知用户
 | 
					 | 
				
			||||||
  open.value = false;
 | 
					 | 
				
			||||||
  resetProgress();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 处理轮询错误
 | 
					 | 
				
			||||||
const handlePollingError = () => {
 | 
					 | 
				
			||||||
  progressStatus.value = 'exception';
 | 
					 | 
				
			||||||
  progressPercent.value = 0;
 | 
					 | 
				
			||||||
  progressTitle.value = '获取进度失败';
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onExportItems = async () => {
 | 
					const onExportItems = async () => {
 | 
				
			||||||
 | 
					  if (isExporting.value) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // 清除已有定时器防止重复请求
 | 
					    isExporting.value = true;
 | 
				
			||||||
    if (pollTimer.value) clearInterval(pollTimer.value);
 | 
					    abortController.value = new AbortController(); // 创建控制器
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 重置状态(包括清除旧任务ID)
 | 
					 | 
				
			||||||
    resetProgress();
 | 
					 | 
				
			||||||
    currentTaskId.value = '';
 | 
					 | 
				
			||||||
    open.value = true;
 | 
					    open.value = true;
 | 
				
			||||||
 | 
					    resetProgressState(); // 重置进度状态
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 获取任务ID
 | 
					    // 创建导出任务
 | 
				
			||||||
    const {data: taskId} = await addressApi.createExportTask();
 | 
					    const {data: taskId} = await addressApi.createExportTask();
 | 
				
			||||||
    currentTaskId.value = taskId;
 | 
					    currentTaskId.value = taskId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 启动导出(添加超时处理)
 | 
					    // 发起导出请求
 | 
				
			||||||
    await Promise.race([
 | 
					    try {
 | 
				
			||||||
      itemApi.exportItems(taskId),
 | 
					      itemApi.exportItems(taskId, abortController.value.signal)
 | 
				
			||||||
      new Promise((_, reject) =>
 | 
					    } catch (error) {
 | 
				
			||||||
          setTimeout(() => reject(new Error('导出超时')), 30_000)
 | 
					      handleExportError();
 | 
				
			||||||
      )
 | 
					    }
 | 
				
			||||||
    ]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 开始轮询进度
 | 
					    // 清理之前的定时器
 | 
				
			||||||
    await startProgressPolling(taskId);
 | 
					    if (timerId.value) {
 | 
				
			||||||
  } catch (error: any) {
 | 
					      clearInterval(timerId.value);
 | 
				
			||||||
    open.value = false;
 | 
					    }
 | 
				
			||||||
    message.error('导出失败: ' + error.message);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 异常时清除定时器
 | 
					    // 启动轮询
 | 
				
			||||||
    if (pollTimer.value) clearInterval(pollTimer.value);
 | 
					    timerId.value = setInterval(async () => {
 | 
				
			||||||
    resetProgress();
 | 
					      try {
 | 
				
			||||||
 | 
					        const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        progressPercent.value = progress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (progress >= 100) {
 | 
				
			||||||
 | 
					          handleExportSuccess();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        handleExportError();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    handleExportError();
 | 
				
			||||||
 | 
					  } finally {
 | 
				
			||||||
 | 
					    isExporting.value = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onClose = () => {
 | 
					//  处理导出成功
 | 
				
			||||||
  resetProgress();
 | 
					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);
 | 
					onMounted(queryData);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue