Files
reimburse/reimburse/ExpensePreviewSheet.swift
T
2026-09-18 10:35:38 +08:00

63 lines
3.4 KiB
Swift

import SwiftUI
struct ExpensePreviewSheet: View {
@EnvironmentObject var store: WorkspaceStore
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .top) {
VStack(alignment: .leading, spacing: 5) {
Text("报销单预览").font(.title2.bold())
Text("已选 \(store.expenseMatchIDs.count) 组 · 合并为 \(store.expenseRows.count) 条费用 · \(store.expensePreview?.templateName ?? store.expenseTemplateName)")
.font(.caption).foregroundStyle(.secondary).lineLimit(2)
}
Spacer()
Text("合计 \(currency(store.expenseRows.reduce(0) { $0 + $1.amountValue }))")
.font(.title3.bold()).monospacedDigit()
}.padding(20)
Divider()
if store.busy {
VStack(spacing: 14) {
ProgressView()
Text("正在填入所选费用、收款资料和签字岗位…")
Button("取消生成", action: store.cancel)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
} else if let preview = store.expensePreview, let layout = preview.layout {
ExpenseDocumentPreview(layout: layout).id(preview.file)
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
ContentUnavailableView("暂时无法生成预览", systemImage: "doc.badge.ellipsis",
description: Text("请检查模板及收款信息,或重试。"))
}
if let error = store.expensePreviewError {
Label(error, systemImage: "exclamationmark.triangle")
.font(.callout).foregroundStyle(.red).textSelection(.enabled)
.frame(maxWidth: .infinity, alignment: .leading).padding(.horizontal, 20).padding(.vertical, 10)
}
if let preview = store.expensePreview, !preview.uncalculatedCells.isEmpty {
Text("部分自定义公式尚未计算:\(preview.uncalculatedCells.prefix(5).joined(separator: "、"))。这些位置在预览中留空,Excel / WPS 打开后会重算。")
.font(.caption).foregroundStyle(.orange)
.frame(maxWidth: .infinity, alignment: .leading).padding(.horizontal, 20).padding(.bottom, 10)
}
Divider()
HStack {
Text("确认后保存此报销单")
.font(.caption).foregroundStyle(.secondary)
Spacer()
Button("返回修改") { dismiss() }.keyboardShortcut(.cancelAction)
if store.expensePreviewError != nil || store.expensePreview == nil {
Button("重新预览", action: store.prepareExpensePreview)
}
Button("确认并保存…", action: store.saveExpensePreview)
.buttonStyle(.borderedProminent)
.disabled(store.expensePreview == nil || store.expensePreviewError != nil)
}.padding(20).disabled(store.busy)
}
.frame(width: min(1200, (NSScreen.main?.visibleFrame.width ?? 1280) - 80),
height: min(820, (NSScreen.main?.visibleFrame.height ?? 900) - 80))
.interactiveDismissDisabled(store.busy)
.onDisappear { store.discardExpensePreview() }
}
}