实现 下拉滚动加载分页数据、导出动态加载进度条显示

main
HUOJIN\92525 2025-05-08 17:02:34 +08:00
parent 9b61dab6c6
commit ffdf0aaf71
3 changed files with 139 additions and 19 deletions

View File

@ -59,11 +59,20 @@ export const addressApi = {
return postRequest('/address/importAddress', file); return postRequest('/address/importAddress', file);
}, },
createExportTask: () => {
return postRequest('/address/createExportTask', {});
},
getExportProgress: (taskId: string) => {
return getRequest(`/address/progress/${taskId}`, {});
},
/** /**
* @author hj * @author hj
*/ */
exportAddress: () => { exportAddress: (taskId: string) => {
return getDownload('/address/exportAddress', {}); return getDownload(`/address/exportAddress/${taskId}`, {});
} }
}; };

View File

@ -56,6 +56,12 @@
</template> </template>
导入 导入
</a-button> </a-button>
<a-modal v-model:open="open" :title="progressTitle" :maskClosable="false" :footer="null">
<a-progress
:percent="progressPercent"
:status="progressStatus"
/>
</a-modal>
<a-button @click="onExportAddress" type="primary" v-privilege="'address:exportAddress'"> <a-button @click="onExportAddress" type="primary" v-privilege="'address:exportAddress'">
<template #icon> <template #icon>
@ -79,6 +85,7 @@
bordered bordered
:loading="tableLoading" :loading="tableLoading"
:pagination="false" :pagination="false"
:scroll="{ x: 1500, y: 350 }"
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }" :row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
> >
<template #bodyCell="{ text, record, column }"> <template #bodyCell="{ text, record, column }">
@ -180,7 +187,6 @@ import AddressForm from '/@/views/business/wms/base/address/address-form.vue';
import {TABLE_ID_CONST} from "/@/constants/support/table-id-const"; import {TABLE_ID_CONST} from "/@/constants/support/table-id-const";
import AddressSelect from "/@/views/business/wms/base/address/address-select.vue"; import AddressSelect from "/@/views/business/wms/base/address/address-select.vue";
import {fileApi} from "/@/api/support/file-api"; import {fileApi} from "/@/api/support/file-api";
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
// loading // loading
const tableLoading = ref(false); const tableLoading = ref(false);
@ -420,9 +426,56 @@ async function onImportAddress() {
} }
// //
function onExportAddress() { const progressTitle = ref('文件导出中,请稍等...');
addressApi.exportAddress(); const progressPercent = ref(0);//
} const progressStatus = ref('active');//
const currentTaskId = ref('');//ID
const open = ref<boolean>(false);//
const onExportAddress = async () => {
try {
open.value = true;
//ID
const {data: taskId} = await addressApi.createExportTask();
currentTaskId.value = taskId;
progressStatus.value = 'active';
//
addressApi.exportAddress(currentTaskId.value);
//
const timer = setInterval(async () => {
try {
//
const {data: progress} = await addressApi.getExportProgress(currentTaskId.value);
if (progress === -1) {
clearInterval(timer);
progressStatus.value = 'exception';
return;
}
progressPercent.value = progress;
if (progress >= 100) {
clearInterval(timer);
progressStatus.value = 'success';
progressTitle.value = '文件导出完成';
// 1
setTimeout(() => {
open.value = false;
progressPercent.value = 0;
}, 1000);
}
} catch (error) {
clearInterval(timer);
progressStatus.value = 'exception';
progressPercent.value = 0;
}
}, 1000); //
} catch (error) {
message.error('导出失败');
}
};
onMounted(queryData); onMounted(queryData);

View File

@ -7,14 +7,19 @@
<a-select <a-select
v-model:value="selectValue" v-model:value="selectValue"
:style="`width: ${width}`" :style="`width: ${width}`"
:placeholder="placeholder" :placeholder="props.placeholder"
:showSearch="true" :showSearch="true"
:allowClear="true" :allowClear="true"
:size="size" :size="size"
@change="handleChange" @change="handleChange"
@search="handleSearch"
@popupScroll="handleScroll"
@blur="handleBlur"
:disabled="disabled" :disabled="disabled"
:mode="multiple ? 'multiple' : undefined" :mode="multiple ? 'multiple' : ''"
optionFilterProp="label" optionFilterProp="label"
:loading="loading"
>
> >
<a-select-option v-for="address in addressList" :key="address.addressId" :label="address.name"> <a-select-option v-for="address in addressList" :key="address.addressId" :label="address.name">
{{ address.name }} {{ address.name }}
@ -25,7 +30,18 @@
<script setup lang="ts"> <script setup lang="ts">
import {onMounted, ref, watch} from 'vue'; import {onMounted, ref, watch} from 'vue';
import {addressApi} from "/@/api/business/wms/base/address/address-api"; import {addressApi} from "/@/api/business/wms/base/address/address-api";
import {smartSentry} from '/@/lib/smart-sentry'; import {debounce} from "lodash";
import {smartSentry} from "/@/lib/smart-sentry";
const loading = ref(false);
const page = ref({
pageNum: 1,
pageSize: 10,
total: 0,
name: ''
});
interface Address { interface Address {
addressId: number; addressId: number;
@ -33,10 +49,7 @@ interface Address {
} }
const props = defineProps({ const props = defineProps({
modelValue: { value: [Number, String, Object, Array],
type: [Number, String, Object, Array],
default: undefined
},
width: { width: {
type: String, type: String,
default: '200px', default: '200px',
@ -57,18 +70,39 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
status: {
type: String,
default: null,
},
disabledFlag: {
type: Boolean,
default: null,
},
}); });
const addressList = ref<Address[]>([]); const addressList = ref<Address[]>([]);
const selectValue = ref(props.modelValue); const selectValue = ref(props.value);
async function queryData() { async function queryData(append = false) {
try { try {
const res = await addressApi.queryAddress({}); loading.value = true;
addressList.value = res.data; let res = await addressApi.queryAddress({
pageNum: page.value.pageNum,
pageSize: page.value.pageSize,
name: page.value.name
});
if (append) {
addressList.value = [...addressList.value, ...res.data.list];
} else {
addressList.value = res.data.list;
}
page.value.total = res.data.total;
} catch (e) { } catch (e) {
smartSentry.captureError(e); smartSentry.captureError(e);
} finally {
loading.value = false;
} }
} }
function querySelectedAddress(value: any) { function querySelectedAddress(value: any) {
@ -89,9 +123,33 @@ function handleChange(value: any) {
emit('change', selectedAddress); emit('change', selectedAddress);
} }
// modelValue //
const handleSearch = debounce(async (value: string) => {
page.value.name = value;
page.value.pageNum = 1;
await queryData();
}, 500);
//
const handleScroll = async (e: Event) => {
const {scrollTop, clientHeight, scrollHeight} = e.target as HTMLElement;
if (scrollHeight - (scrollTop + clientHeight) < 10 && !loading.value) {
if (addressList.value.length < page.value.total) {
page.value.pageNum++;
await queryData(true);
}
}
};
//
const handleBlur = async () => {
page.value.pageNum = 1;
await queryData();
};
// value
watch( watch(
() => props.modelValue, () => props.value,
(newValue) => { (newValue) => {
selectValue.value = newValue; selectValue.value = newValue;
} }