diff --git a/web/src/components/minimal/InlineError.tsx b/web/src/components/minimal/InlineError.tsx
index 9d2d9bc..6c16217 100644
--- a/web/src/components/minimal/InlineError.tsx
+++ b/web/src/components/minimal/InlineError.tsx
@@ -1,11 +1,24 @@
type InlineErrorProps = {
message?: string | null;
+ actionLabel?: string;
+ onAction?: () => void;
};
-export function InlineError({ message }: InlineErrorProps) {
+export function InlineError({ message, actionLabel, onAction }: InlineErrorProps) {
return (
- {message ?
{message}
:
}
+ {message ? (
+
+ {message}
+ {actionLabel && onAction ? (
+
+ ) : null}
+
+ ) : (
+
+ )}
);
}
diff --git a/web/src/components/minimal/MinimalHome.tsx b/web/src/components/minimal/MinimalHome.tsx
index e9ade1b..f0560d7 100644
--- a/web/src/components/minimal/MinimalHome.tsx
+++ b/web/src/components/minimal/MinimalHome.tsx
@@ -9,13 +9,17 @@ type MinimalHomeProps = {
selectedFile: File | null;
phase: FlowPhase;
exportProgress: number;
+ exportMessage?: string;
error: InlineErrorState | null;
+ canRetryExport: boolean;
privacyAccepted: boolean;
turnstileToken: string;
onTextChange: (value: string) => void;
onUploadTrigger: () => boolean;
onFileSelect: (file: File | null) => void;
onSubmit: () => void;
+ onCancelExport: () => void;
+ onRetryExport: () => void;
onClear: () => void;
onPrivacyAcceptedChange: (value: boolean) => void;
onTurnstileTokenChange: (value: string) => void;
@@ -65,18 +69,24 @@ export function MinimalHome(props: MinimalHomeProps) {
selectedFile={props.selectedFile}
phase={props.phase}
exportProgress={props.exportProgress}
+ exportMessage={props.exportMessage}
privacyAccepted={props.privacyAccepted}
turnstileToken={props.turnstileToken}
onTextChange={props.onTextChange}
onUploadTrigger={props.onUploadTrigger}
onFileSelect={props.onFileSelect}
onSubmit={props.onSubmit}
+ onCancelExport={props.onCancelExport}
onClear={props.onClear}
onDragActiveChange={setIsDragActive}
onPrivacyAcceptedChange={props.onPrivacyAcceptedChange}
onTurnstileTokenChange={props.onTurnstileTokenChange}
/>
-
+
diff --git a/web/src/components/minimal/WaveExportProgress.tsx b/web/src/components/minimal/WaveExportProgress.tsx
index 54f2500..c0938b5 100644
--- a/web/src/components/minimal/WaveExportProgress.tsx
+++ b/web/src/components/minimal/WaveExportProgress.tsx
@@ -1,8 +1,10 @@
type WaveExportProgressProps = {
progress: number;
+ message?: string;
+ onCancel?: () => void;
};
-export function WaveExportProgress({ progress }: WaveExportProgressProps) {
+export function WaveExportProgress({ progress, message = "正在生成 Word 文件", onCancel }: WaveExportProgressProps) {
return (
@@ -11,9 +13,14 @@ export function WaveExportProgress({ progress }: WaveExportProgressProps) {
- 正在生成 Word 文件
+ {message}
{Math.round(progress)}%
+ {onCancel ? (
+
+ ) : null}
);
}
diff --git a/web/src/styles/components.css b/web/src/styles/components.css
index 71da175..2ce6b0e 100644
--- a/web/src/styles/components.css
+++ b/web/src/styles/components.css
@@ -204,6 +204,7 @@
.inline-error span {
display: inline-flex;
align-items: center;
+ gap: var(--space-2);
min-height: 2rem;
}
@@ -217,6 +218,21 @@
font-size: var(--text-sm);
}
+.inline-error button,
+.wave-progress-cancel {
+ border: 1px solid rgba(154, 95, 100, 0.18);
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.72);
+ color: inherit;
+ cursor: pointer;
+ font: inherit;
+ font-weight: 700;
+}
+
+.inline-error button {
+ padding: 0.18rem 0.55rem;
+}
+
.wave-progress {
display: grid;
gap: var(--space-3);
@@ -260,6 +276,12 @@
font-size: var(--text-sm);
}
+.wave-progress-cancel {
+ justify-self: end;
+ padding: 0.32rem 0.72rem;
+ color: var(--text-secondary);
+}
+
.precheck-modal-top,
.precheck-footer,
.preview-block-header {