From 34d36a15f339d331d668d4063cfdff50cffa5800 Mon Sep 17 00:00:00 2001
From: zhou zhou <zozhouo3o@gmail.com>
Date: 星期五, 17 四月 2026 15:11:32 +0800
Subject: [PATCH] #导出服务
---
rsf-design/src/views/basic-info/loc/index.vue | 192 ++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 176 insertions(+), 16 deletions(-)
diff --git a/rsf-design/src/views/basic-info/loc/index.vue b/rsf-design/src/views/basic-info/loc/index.vue
index 8e69d59..d106336 100644
--- a/rsf-design/src/views/basic-info/loc/index.vue
+++ b/rsf-design/src/views/basic-info/loc/index.vue
@@ -33,8 +33,10 @@
:preview-rows="previewRows"
:preview-meta="resolvedPreviewMeta"
:total="pagination.total"
- :disabled="loading"
- @export="handleExport"
+ :disabled="loading || exportTaskLoading"
+ export-auth="manager:loc:export"
+ print-auth="list"
+ @export="handleExportRequest"
@print="handlePrint"
/>
</ElSpace>
@@ -71,7 +73,7 @@
</template>
<script setup>
- import { computed, onMounted, ref } from 'vue'
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/store/modules/user'
import { useAuth } from '@/hooks/core/useAuth'
@@ -84,9 +86,12 @@
import { fetchWarehouseAreasList, fetchWarehouseList } from '@/api/warehouse-areas'
import {
fetchDeleteLoc,
+ fetchCreateLocExportTask,
+ fetchDownloadLocExportTask,
fetchExportLocReport,
fetchGetLocDetail,
fetchGetLocMany,
+ fetchLocExportTask,
fetchLocPage,
fetchLocTypeList,
fetchSaveLoc,
@@ -117,6 +122,9 @@
defineOptions({ name: 'Loc' })
+ const EXPORT_SYNC_MAX_ROWS = 5000
+ const EXPORT_TASK_POLL_INTERVAL = 3000
+
const { hasAuth } = useAuth()
const userStore = useUserStore()
@@ -127,7 +135,9 @@
const warehouseOptions = ref([])
const areaOptions = ref([])
const locTypeOptions = ref([])
+ const exportTaskLoading = ref(false)
let handleDeleteAction = null
+ let exportTaskTimer = null
const reportTitle = LOC_REPORT_TITLE
const reportQueryParams = computed(() => buildLocSearchParams(searchForm.value))
@@ -189,9 +199,13 @@
])
async function fetchLocDetailById(id) {
- return guardRequestWithMessage(fetchGetLocDetail(id), {}, {
- timeoutMessage: '搴撲綅璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
- })
+ return guardRequestWithMessage(
+ fetchGetLocDetail(id),
+ {},
+ {
+ timeoutMessage: '搴撲綅璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+ }
+ )
}
async function openDetail(row) {
@@ -292,13 +306,17 @@
}
const resolvePrintRecords = async (payload) => {
- const response = Array.isArray(payload?.ids) && payload.ids.length > 0
- ? await fetchGetLocMany(payload.ids)
- : await fetchLocPage({
- ...reportQueryParams.value,
- current: 1,
- pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20
- })
+ const response =
+ Array.isArray(payload?.ids) && payload.ids.length > 0
+ ? await fetchGetLocMany(payload.ids)
+ : await fetchLocPage({
+ ...reportQueryParams.value,
+ current: 1,
+ pageSize:
+ Number(pagination.total) > 0
+ ? Number(pagination.total)
+ : Number(payload?.pageSize) || 20
+ })
return defaultResponseAdapter(response).records
}
@@ -321,6 +339,129 @@
buildPreviewRows: (records) => buildLocPrintRows(records),
buildPreviewMeta: (rows) => buildPreviewDialogMeta(rows)
})
+
+ function clearExportTaskTimer() {
+ if (exportTaskTimer) {
+ clearTimeout(exportTaskTimer)
+ exportTaskTimer = null
+ }
+ }
+
+ function getExportRowCount(payload) {
+ const selectedIds = Array.isArray(payload?.ids) ? payload.ids.filter(Boolean) : []
+ if (selectedIds.length > 0) {
+ return selectedIds.length
+ }
+ return Number(pagination.total) || 0
+ }
+
+ function needsAsyncExport(payload) {
+ return getExportRowCount(payload) > EXPORT_SYNC_MAX_ROWS
+ }
+
+ function scheduleExportTaskPoll(taskId) {
+ clearExportTaskTimer()
+ exportTaskTimer = setTimeout(() => {
+ pollExportTask(taskId)
+ }, EXPORT_TASK_POLL_INTERVAL)
+ }
+
+ function resolveDownloadFileName(task = {}, response) {
+ const contentDisposition = response?.headers?.get('Content-Disposition') || ''
+ const matchedPart = contentDisposition
+ .split(';')
+ .map((part) => part.trim())
+ .find((part) => /^filename\*?=/i.test(part))
+
+ if (matchedPart) {
+ let fileName = matchedPart.replace(/^filename\*?=/i, '').trim()
+ if (fileName.toLowerCase().startsWith("utf-8''")) {
+ fileName = fileName.slice(7)
+ }
+ if (fileName.startsWith('"') && fileName.endsWith('"')) {
+ fileName = fileName.slice(1, -1)
+ }
+ try {
+ return decodeURIComponent(fileName)
+ } catch {
+ return fileName
+ }
+ }
+ return task.fileName || 'loc.xlsx'
+ }
+
+ async function downloadExportTaskFile(task) {
+ const response = await fetchDownloadLocExportTask(task.id, {
+ headers: {
+ Authorization: userStore.accessToken || ''
+ }
+ })
+ if (!response.ok) {
+ throw new Error(`瀵煎嚭鏂囦欢涓嬭浇澶辫触锛�${response.status}锛塦)
+ }
+
+ const blob = await response.blob()
+ const downloadUrl = window.URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = downloadUrl
+ link.download = resolveDownloadFileName(task, response)
+ document.body.appendChild(link)
+ link.click()
+ link.remove()
+ window.URL.revokeObjectURL(downloadUrl)
+ }
+
+ async function pollExportTask(taskId) {
+ try {
+ const task = await fetchLocExportTask(taskId)
+ const status = Number(task?.status)
+ if (status === 2) {
+ clearExportTaskTimer()
+ await downloadExportTaskFile(task)
+ exportTaskLoading.value = false
+ ElMessage.success(
+ `瀵煎嚭浠诲姟宸插畬鎴愶紝宸插紑濮嬩笅杞�${task?.rowCount ? `锛�${task.rowCount}琛岋級` : ''}`
+ )
+ return
+ }
+ if (status === 3) {
+ clearExportTaskTimer()
+ exportTaskLoading.value = false
+ ElMessage.error(task?.errorMsg || '瀵煎嚭浠诲姟鎵ц澶辫触')
+ return
+ }
+ scheduleExportTaskPoll(taskId)
+ } catch (error) {
+ clearExportTaskTimer()
+ exportTaskLoading.value = false
+ ElMessage.error(error?.message || '鏌ヨ瀵煎嚭浠诲姟鐘舵�佸け璐�')
+ }
+ }
+
+ async function handleExportRequest(payload) {
+ if (!needsAsyncExport(payload)) {
+ await handleExport(payload)
+ return
+ }
+
+ const exportRowCount = getExportRowCount(payload)
+ exportTaskLoading.value = true
+ clearExportTaskTimer()
+ try {
+ const task = await fetchCreateLocExportTask(payload)
+ ElMessage.success(
+ `鏈瀵煎嚭鍏� ${exportRowCount} 琛岋紝宸茶秴杩� ${EXPORT_SYNC_MAX_ROWS} 琛岋紝绯荤粺宸茶嚜鍔ㄥ垏鎹负鍚庡彴瀵煎嚭浠诲姟${task?.taskCode ? `锛�${task.taskCode}锛塦 : ''}`
+ )
+ if (!task?.id) {
+ throw new Error('瀵煎嚭浠诲姟鍒涘缓鎴愬姛锛屼絾鏈繑鍥炰换鍔D')
+ }
+ scheduleExportTaskPoll(task.id)
+ } catch (error) {
+ exportTaskLoading.value = false
+ clearExportTaskTimer()
+ ElMessage.error(error?.message || '鍒涘缓瀵煎嚭浠诲姟澶辫触')
+ }
+ }
const resolvedPreviewMeta = computed(() =>
buildLocReportMeta({
@@ -348,9 +489,28 @@
onMounted(async () => {
await Promise.all([
- loadOptions(fetchWarehouseList, resolveLocWarehouseOptions, warehouseOptions, '浠撳簱閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'),
- loadOptions(fetchWarehouseAreasList, resolveLocAreaOptions, areaOptions, '搴撳尯閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'),
- loadOptions(fetchLocTypeList, resolveLocTypeOptions, locTypeOptions, '搴撲綅绫诲瀷閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟')
+ loadOptions(
+ fetchWarehouseList,
+ resolveLocWarehouseOptions,
+ warehouseOptions,
+ '浠撳簱閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+ ),
+ loadOptions(
+ fetchWarehouseAreasList,
+ resolveLocAreaOptions,
+ areaOptions,
+ '搴撳尯閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+ ),
+ loadOptions(
+ fetchLocTypeList,
+ resolveLocTypeOptions,
+ locTypeOptions,
+ '搴撲綅绫诲瀷閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+ )
])
})
+
+ onBeforeUnmount(() => {
+ clearExportTaskTimer()
+ })
</script>
--
Gitblit v1.9.1