This commit is contained in:
zwgczx committed 2026-03-12 12:02:05 +08:00
1 parent 398fca9265
commit f4f6e5c618
6 files changed
+207 -71

No files matched your search

+76 -4
View File
@@ -20,6 +20,11 @@ import {
AlertDialogRoot,
AlertDialogTitle,
AlertDialogTrigger,
ToastDescription,
ToastProvider,
ToastRoot,
ToastTitle,
ToastViewport,
} from 'reka-ui'
import { decodeZwArchive, encodeZwArchive, ZW_FILE_EXTENSION } from '@/lib/zwArchive'
import { addNumbers, roundTo } from '@/lib/decimal'
@@ -62,6 +67,7 @@ interface ScaleRowLike {
id: string
amount: number | null
landArea: number | null
isGroupRow?: boolean
}
interface XmInfoStorageLike extends XmInfoLike {
@@ -457,6 +463,10 @@ const pendingImportPayload = shallowRef<DataPackage | null>(null)
const pendingImportFileName = ref('')
const userGuideOpen = ref(false)
const userGuideStepIndex = ref(0)
const reportExportToastOpen = ref(false)
const reportExportProgress = ref(0)
const reportExportStatus = ref<'running' | 'success' | 'error'>('running')
const reportExportText = ref('')
const tabItemElMap = new Map<string, HTMLElement>()
const tabTitleElMap = new Map<string, HTMLElement>()
const tabPanelElMap = new Map<string, HTMLElement>()
@@ -468,6 +478,7 @@ const isTabDragging = ref(false)
const tabTitleOverflowMap = ref<Record<string, boolean>>({})
let tabStripViewportEl: HTMLElement | null = null
let tabTitleOverflowRafId: number | null = null
let reportExportToastTimer: ReturnType<typeof setTimeout> | null = null
const tabsModel = computed({
get: () => tabStore.tabs,
@@ -502,6 +513,31 @@ const closeMenus = () => {
dataMenuOpen.value = false
}
const clearReportExportToastTimer = () => {
if (!reportExportToastTimer) return
clearTimeout(reportExportToastTimer)
reportExportToastTimer = null
}
const showReportExportProgress = (progress: number, text: string) => {
clearReportExportToastTimer()
reportExportStatus.value = 'running'
reportExportProgress.value = Math.max(0, Math.min(100, progress))
reportExportText.value = text
reportExportToastOpen.value = true
}
const finishReportExportProgress = (success: boolean, text: string) => {
clearReportExportToastTimer()
reportExportStatus.value = success ? 'success' : 'error'
reportExportProgress.value = 100
reportExportText.value = text
reportExportToastOpen.value = true
reportExportToastTimer = setTimeout(() => {
reportExportToastOpen.value = false
}, success ? 1200 : 1800)
}
const markGuideCompleted = () => {
try {
localStorage.setItem(USER_GUIDE_COMPLETED_KEY, '1')
@@ -1016,6 +1052,16 @@ const toExportScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] =
.filter((item): item is ExportScaleRow => Boolean(item))
}
const sumLeafScaleCost = (rows: ScaleRowLike[] | undefined) => {
if (!Array.isArray(rows)) return 0
return sumNumbers(
rows.map(row => {
if (row?.isGroupRow === true) return null
return toFiniteNumber(row?.amount)
})
)
}
const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => {
@@ -1365,9 +1411,8 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const projectInfo = projectInfoRaw || {}
const projectScaleSource = projectScaleRaw || {}
const projectScale = projectScaleSource.roughCalcEnabled ? [] : toExportScaleRows(projectScaleSource.detailRows)
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumNumbers(projectScale.map(item => item.cost))
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumLeafScaleCost(projectScaleSource.detailRows)
projectScale.push({
major: -1, cost: projectScaleCost,
area: null
@@ -1572,14 +1617,17 @@ const exportData = async () => {
const exportReport = async () => {
try {
showReportExportProgress(10, '正在准备报表导出...')
const now = new Date()
showReportExportProgress(40, '正在汇总报表数据...')
const payload = await buildExportReportPayload()
showReportExportProgress(80, '正在生成并写出报表文件...')
const fileName = `${sanitizeFileNamePart(payload.name)}-报表-${formatExportTimestamp(now)}`
console.log(payload)
await exportFile(fileName, payload)
finishReportExportProgress(true, '报表导出完成')
} catch (error) {
console.error('export report failed:', error)
// window.alert('导出报表失败,请重试。')
finishReportExportProgress(false, '报表导出失败,请重试')
} finally {
dataMenuOpen.value = false
}
@@ -1710,6 +1758,7 @@ onMounted(() => {
})
onBeforeUnmount(() => {
clearReportExportToastTimer()
window.removeEventListener('mousedown', handleGlobalMouseDown)
window.removeEventListener('keydown', handleGlobalKeyDown)
window.removeEventListener('resize', scheduleUpdateTabTitleOverflow)
@@ -1761,6 +1810,7 @@ watch(
</script>
<template>
<ToastProvider>
<TooltipProvider>
<div class="flex flex-col w-full h-screen bg-background overflow-hidden">
<div class="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-2 border-b bg-muted/30 px-2 pt-1 h-15 flex-none">
@@ -1966,8 +2016,30 @@ watch(
</div>
</div>
</div>
<ToastRoot
:open="reportExportToastOpen"
:duration="0"
class="pointer-events-auto rounded-xl border border-border bg-card px-4 py-3 text-foreground shadow-lg"
>
<ToastTitle class="text-sm font-semibold text-foreground">
{{ reportExportStatus === 'running' ? '导出报表' : (reportExportStatus === 'success' ? '导出成功' : '导出失败') }}
</ToastTitle>
<ToastDescription class="mt-1 text-xs text-muted-foreground">{{ reportExportText }}</ToastDescription>
<div class="mt-2 h-1.5 w-full overflow-hidden rounded-full bg-muted">
<div
class="h-full transition-all duration-300"
:class="reportExportStatus === 'error'
? 'bg-red-500'
: (reportExportStatus === 'success' ? 'bg-foreground/70' : 'bg-foreground')"
:style="{ width: `${reportExportProgress}%` }"
/>
</div>
</ToastRoot>
<ToastViewport class="fixed bottom-5 right-5 z-[85] flex w-[380px] max-w-[92vw] flex-col gap-2 outline-none" />
</div>
</TooltipProvider>
</ToastProvider>
</template>
<style scoped>