|
1 | 1 | import React, { useState } from "react"; |
2 | 2 | import { useTranslations } from "@/i18n/compat/client"; |
3 | | -import { Download, Loader2, ChevronDown } from "lucide-react"; |
| 3 | +import { Download, Loader2, ChevronDown, ShieldCheck } from "lucide-react"; |
4 | 4 | import { useResumeStore } from "@/store/useResumeStore"; |
5 | 5 | import { Button } from "@/components/ui/button"; |
6 | 6 | import { exportResumeAsJson, exportResumeAsMarkdown, exportToPdf } from "@/utils/export"; |
@@ -46,28 +46,32 @@ const ExportCard = ({ |
46 | 46 | if (!isLoading) onClick(); |
47 | 47 | }} |
48 | 48 | className={cn( |
49 | | - "group relative flex flex-col justify-center overflow-hidden p-6 pl-[130px] min-h-[140px] rounded-3xl border bg-card text-card-foreground shadow-sm transition-all duration-500", |
50 | | - isLoading ? "opacity-70 cursor-not-allowed" : cn("cursor-pointer hover:shadow-xl active:scale-[0.98] hover:-translate-y-1", hoverBorderClass) |
| 49 | + "group relative flex flex-col justify-center overflow-hidden p-6 pl-[136px] min-h-[130px] rounded-3xl border border-border/50 bg-card text-card-foreground shadow-sm transition-all duration-500", |
| 50 | + isLoading ? "opacity-70 cursor-not-allowed" : cn("cursor-pointer hover:shadow-2xl active:scale-[0.98] hover:-translate-y-1 hover:bg-muted/10", hoverBorderClass) |
51 | 51 | )} |
52 | 52 | > |
| 53 | + {/* 顶部内发光高光,增加卡片立体感 */} |
| 54 | + <div className="absolute inset-0 rounded-3xl ring-1 ring-inset ring-white/10 pointer-events-none mix-blend-overlay opacity-0 group-hover:opacity-100 transition-opacity duration-500" /> |
| 55 | + |
53 | 56 | {/* 底部环境光晕,让图标色渗入背景 */} |
54 | 57 | <div className={cn( |
55 | 58 | "absolute inset-0 pointer-events-none transition-opacity duration-500 bg-gradient-to-r to-transparent", |
56 | 59 | isLoading ? "opacity-50" : "opacity-0 group-hover:opacity-100", |
57 | 60 | bgGradientClass |
58 | 61 | )} /> |
59 | 62 |
|
| 63 | + {/* 调整后的图标层:完全收入卡片内部,尺寸克制 */} |
60 | 64 | <div className={cn( |
61 | | - "absolute -left-6 top-1/2 -translate-y-1/2 w-40 h-40 transition-transform duration-500 will-change-transform drop-shadow-xl pointer-events-none", |
62 | | - isLoading ? "-rotate-12 scale-[1.08] opacity-80" : "-rotate-12 group-hover:scale-[1.08]" |
| 65 | + "absolute left-6 top-1/2 -translate-y-1/2 w-24 h-24 transition-transform duration-500 will-change-transform drop-shadow-xl pointer-events-none", |
| 66 | + isLoading ? "-rotate-6 scale-[1.05] opacity-80" : "-rotate-6 group-hover:scale-[1.1] group-hover:-rotate-3" |
63 | 67 | )}> |
64 | 68 | <Icon className="w-full h-full object-contain" isLoading={isLoading} /> |
65 | 69 | </div> |
66 | 70 |
|
67 | | - {/* 文本内容说明 (Z-indexed above icon) */} |
| 71 | + {/* 文本内容说明 */} |
68 | 72 | <div className="relative z-10 flex flex-col pointer-events-none"> |
69 | | - <h3 className="font-bold text-[17px] mb-2 tracking-tight text-foreground/90 transition-colors">{title}</h3> |
70 | | - <p className="text-[13px] text-muted-foreground/80 leading-relaxed line-clamp-3 group-hover:text-muted-foreground transition-colors"> |
| 73 | + <h3 className="font-semibold text-[17px] mb-1.5 tracking-tight text-foreground/90 transition-colors">{title}</h3> |
| 74 | + <p className="text-[13px] text-muted-foreground/75 leading-relaxed line-clamp-3 group-hover:text-muted-foreground/90 transition-colors"> |
71 | 75 | {description} |
72 | 76 | </p> |
73 | 77 | </div> |
@@ -190,53 +194,70 @@ const PdfExport = ({ children }: { children?: React.ReactNode }) => { |
190 | 194 | )} |
191 | 195 | </DialogTrigger> |
192 | 196 |
|
193 | | - <DialogContent className="max-w-4xl gap-8 p-8 sm:rounded-2xl border-none shadow-2xl bg-gradient-to-b from-background to-muted/20"> |
194 | | - <DialogHeader className="gap-2"> |
195 | | - <DialogTitle className="text-2xl font-bold flex items-center gap-3"> |
196 | | - {t("modal.title")} |
197 | | - </DialogTitle> |
198 | | - <DialogDescription className="text-sm text-muted-foreground/80 mt-1"> |
199 | | - {t("modal.subtitle")} |
200 | | - </DialogDescription> |
201 | | - </DialogHeader> |
202 | | - |
203 | | - <div className="grid grid-cols-1 md:grid-cols-2 gap-5"> |
204 | | - <ExportCard |
205 | | - icon={PdfGlassIcon} |
206 | | - title={t("button.exportPdf")} |
207 | | - description={t("modal.pdfDesc")} |
208 | | - isLoading={isExporting} |
209 | | - onClick={handleExport} |
210 | | - bgGradientClass="from-rose-500/10 dark:from-rose-500/20" |
211 | | - hoverBorderClass="hover:border-rose-500/40 hover:ring-1 hover:ring-rose-500/20" |
212 | | - /> |
213 | | - <ExportCard |
214 | | - icon={PrintGlassIcon} |
215 | | - title={t("button.print")} |
216 | | - description={t("modal.printDesc")} |
217 | | - isLoading={isPrinting} |
218 | | - onClick={handlePrint} |
219 | | - bgGradientClass="from-sky-500/10 dark:from-sky-500/20" |
220 | | - hoverBorderClass="hover:border-sky-500/40 hover:ring-1 hover:ring-sky-500/20" |
221 | | - /> |
222 | | - <ExportCard |
223 | | - icon={JsonGlassIcon} |
224 | | - title={t("button.exportJson")} |
225 | | - description={t("modal.jsonDesc")} |
226 | | - isLoading={isExportingJson} |
227 | | - onClick={handleJsonExport} |
228 | | - bgGradientClass="from-amber-500/10 dark:from-amber-500/20" |
229 | | - hoverBorderClass="hover:border-amber-500/40 hover:ring-1 hover:ring-amber-500/20" |
230 | | - /> |
231 | | - <ExportCard |
232 | | - icon={MarkdownGlassIcon} |
233 | | - title={t("button.exportMarkdown")} |
234 | | - description={t("modal.markdownDesc")} |
235 | | - isLoading={isExportingMarkdown} |
236 | | - onClick={handleMarkdownExport} |
237 | | - bgGradientClass="from-indigo-500/10 dark:from-indigo-500/20" |
238 | | - hoverBorderClass="hover:border-indigo-500/40 hover:ring-1 hover:ring-indigo-500/20" |
239 | | - /> |
| 197 | + <DialogContent className="max-w-4xl gap-0 p-0 sm:rounded-[2rem] border border-border/60 shadow-[0_20px_60px_-15px_rgba(0,0,0,0.1)] bg-background overflow-hidden"> |
| 198 | + {/* 头部区域:带精美网格和高光 */} |
| 199 | + <div className="relative p-8 pb-6 border-b border-border/40 bg-muted/10"> |
| 200 | + {/* SaaS 网格底纹背景 */} |
| 201 | + <div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)]" /> |
| 202 | + |
| 203 | + <DialogHeader className="relative gap-2"> |
| 204 | + <DialogTitle className="text-2xl font-bold tracking-tight"> |
| 205 | + {t("modal.title")} |
| 206 | + </DialogTitle> |
| 207 | + <DialogDescription className="text-[15px] text-muted-foreground/80 mt-1"> |
| 208 | + {t("modal.subtitle")} |
| 209 | + </DialogDescription> |
| 210 | + </DialogHeader> |
| 211 | + </div> |
| 212 | + |
| 213 | + {/* 卡片展示区域 */} |
| 214 | + <div className="p-8 bg-background"> |
| 215 | + <div className="grid grid-cols-1 md:grid-cols-2 gap-5 relative"> |
| 216 | + <ExportCard |
| 217 | + icon={PdfGlassIcon} |
| 218 | + title={t("button.exportPdf")} |
| 219 | + description={t("modal.pdfDesc")} |
| 220 | + isLoading={isExporting} |
| 221 | + onClick={handleExport} |
| 222 | + bgGradientClass="from-rose-500/10 dark:from-rose-500/20" |
| 223 | + hoverBorderClass="hover:border-rose-500/40 hover:ring-1 hover:ring-rose-500/20" |
| 224 | + /> |
| 225 | + <ExportCard |
| 226 | + icon={PrintGlassIcon} |
| 227 | + title={t("button.print")} |
| 228 | + description={t("modal.printDesc")} |
| 229 | + isLoading={isPrinting} |
| 230 | + onClick={handlePrint} |
| 231 | + bgGradientClass="from-sky-500/10 dark:from-sky-500/20" |
| 232 | + hoverBorderClass="hover:border-sky-500/40 hover:ring-1 hover:ring-sky-500/20" |
| 233 | + /> |
| 234 | + <ExportCard |
| 235 | + icon={JsonGlassIcon} |
| 236 | + title={t("button.exportJson")} |
| 237 | + description={t("modal.jsonDesc")} |
| 238 | + isLoading={isExportingJson} |
| 239 | + onClick={handleJsonExport} |
| 240 | + bgGradientClass="from-amber-500/10 dark:from-amber-500/20" |
| 241 | + hoverBorderClass="hover:border-amber-500/40 hover:ring-1 hover:ring-amber-500/20" |
| 242 | + /> |
| 243 | + <ExportCard |
| 244 | + icon={MarkdownGlassIcon} |
| 245 | + title={t("button.exportMarkdown")} |
| 246 | + description={t("modal.markdownDesc")} |
| 247 | + isLoading={isExportingMarkdown} |
| 248 | + onClick={handleMarkdownExport} |
| 249 | + bgGradientClass="from-indigo-500/10 dark:from-indigo-500/20" |
| 250 | + hoverBorderClass="hover:border-indigo-500/40 hover:ring-1 hover:ring-indigo-500/20" |
| 251 | + /> |
| 252 | + </div> |
| 253 | + |
| 254 | + {/* 隐私保护横幅 */} |
| 255 | + <div className="mt-6 flex items-center gap-2 p-3 sm:px-4 rounded-xl bg-emerald-50 dark:bg-emerald-500/10 border border-emerald-100 dark:border-emerald-500/20 text-emerald-600 dark:text-emerald-400"> |
| 256 | + <ShieldCheck className="w-4 h-4 shrink-0" /> |
| 257 | + <p className="text-[13px] font-medium"> |
| 258 | + {t("modal.privacyNotice")} |
| 259 | + </p> |
| 260 | + </div> |
240 | 261 | </div> |
241 | 262 | </DialogContent> |
242 | 263 | </Dialog> |
|
0 commit comments