From 5c6dc7fee25b1688e8795b96f60953a8e31603d0 Mon Sep 17 00:00:00 2001 From: JOYCEQL <1449239013@qq.com> Date: Sun, 1 Mar 2026 15:08:19 +0800 Subject: [PATCH] feat: Implement blank resume creation and enhance section management with standard modules and a new section addition UI --- .../dashboard/resumes/CreateResumeModal.tsx | 2 +- src/app/app/dashboard/resumes/page.tsx | 3 +- src/components/editor/SidePanel.tsx | 82 ++++++++++++++++--- src/components/templates/classic/config.ts | 1 + src/components/templates/creative/config.ts | 1 + src/components/templates/elegant/config.ts | 1 + src/components/templates/left-right/config.ts | 1 + src/components/templates/minimalist/config.ts | 1 + src/components/templates/modern/config.ts | 1 + src/components/templates/timeline/config.ts | 1 + src/config/initialResumeData.ts | 46 ++++++++++- src/config/modules.ts | 12 +++ src/i18n/locales/en.json | 9 +- src/i18n/locales/zh.json | 9 +- src/store/useResumeStore.ts | 16 +++- src/types/template.ts | 1 + 16 files changed, 168 insertions(+), 19 deletions(-) create mode 100644 src/config/modules.ts diff --git a/src/app/app/dashboard/resumes/CreateResumeModal.tsx b/src/app/app/dashboard/resumes/CreateResumeModal.tsx index b74f5fd..9262256 100644 --- a/src/app/app/dashboard/resumes/CreateResumeModal.tsx +++ b/src/app/app/dashboard/resumes/CreateResumeModal.tsx @@ -114,7 +114,7 @@ const TemplateThumbnail = ({ }, basic: { ...initialResumeState.basic, - layout: template.basic?.layout || "classic", + layout: (template.basic?.layout as any) || "left", }, // Feed richer mock content in large preview. experience: sampleExperience, diff --git a/src/app/app/dashboard/resumes/page.tsx b/src/app/app/dashboard/resumes/page.tsx index dbe6ece..3bb7f61 100644 --- a/src/app/app/dashboard/resumes/page.tsx +++ b/src/app/app/dashboard/resumes/page.tsx @@ -253,7 +253,8 @@ const ResumeWorkbench = () => { }, []); const handleCreateFromModal = (templateId: string | null) => { - const newId = createResume(templateId); + const isBlank = !templateId; + const newId = createResume(templateId, isBlank); if (templateId) { const template = DEFAULT_TEMPLATES.find((t) => t.id === templateId); diff --git a/src/components/editor/SidePanel.tsx b/src/components/editor/SidePanel.tsx index 19505b3..ac6d773 100644 --- a/src/components/editor/SidePanel.tsx +++ b/src/components/editor/SidePanel.tsx @@ -18,8 +18,16 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import LayoutSetting from "./layout/LayoutSetting"; import { useResumeStore } from "@/store/useResumeStore"; import { cn } from "@/lib/utils"; -import { THEME_COLORS } from "@/types/resume"; +import { THEME_COLORS, MenuSection } from "@/types/resume"; import { ColorPicker } from "@/components/ui/color-picker"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Plus } from "lucide-react"; +import { STANDARD_MODULES } from "@/config/modules"; +import { DEFAULT_TEMPLATES } from "@/config"; const fontOptions = [ { value: "sans", label: "无衬线体" }, @@ -84,6 +92,18 @@ export function SidePanel() { const { themeColor = THEME_COLORS[0] } = globalSettings; const t = useTranslations("workbench.sidePanel"); + const currentTemplate = DEFAULT_TEMPLATES.find( + (t) => t.id === activeResume?.templateId + ); + + const availableModules = useMemo(() => { + return ( + currentTemplate?.availableSections + ?.map((id) => STANDARD_MODULES[id]) + .filter(Boolean) || [] + ); + }, [currentTemplate]); + const fontOptions = [ { value: "sans", label: t("typography.font.sans") }, { value: "serif", label: t("typography.font.serif") }, @@ -145,15 +165,57 @@ export function SidePanel() { reorderSections={reorderSections} /> -
- - {t("layout.addCustomSection")} - +
+ + + + + {t("layout.addCustomSection")} + + + +
+ {/* Standard Sections Library */} + {availableModules.map((section) => ( + + ))} + + {/* Divider for Custom Section */} + {availableModules.length > 0 && ( +
+ )} + + {/* Add Custom Section */} + +
+ +
diff --git a/src/components/templates/classic/config.ts b/src/components/templates/classic/config.ts index 0c903df..1622f04 100644 --- a/src/components/templates/classic/config.ts +++ b/src/components/templates/classic/config.ts @@ -20,4 +20,5 @@ export const classicConfig: ResumeTemplate = { basic: { layout: "left", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/creative/config.ts b/src/components/templates/creative/config.ts index bbeffa4..d67e95b 100644 --- a/src/components/templates/creative/config.ts +++ b/src/components/templates/creative/config.ts @@ -20,4 +20,5 @@ export const creativeConfig: ResumeTemplate = { basic: { layout: "left", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/elegant/config.ts b/src/components/templates/elegant/config.ts index 7a8fe4d..f7f818b 100644 --- a/src/components/templates/elegant/config.ts +++ b/src/components/templates/elegant/config.ts @@ -20,4 +20,5 @@ export const elegantConfig: ResumeTemplate = { basic: { layout: "center", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/left-right/config.ts b/src/components/templates/left-right/config.ts index 1a451f5..99706ea 100644 --- a/src/components/templates/left-right/config.ts +++ b/src/components/templates/left-right/config.ts @@ -20,4 +20,5 @@ export const leftRightConfig: ResumeTemplate = { basic: { layout: "left", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/minimalist/config.ts b/src/components/templates/minimalist/config.ts index c025eee..9e71255 100644 --- a/src/components/templates/minimalist/config.ts +++ b/src/components/templates/minimalist/config.ts @@ -20,4 +20,5 @@ export const minimalistConfig: ResumeTemplate = { basic: { layout: "center", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/modern/config.ts b/src/components/templates/modern/config.ts index 42f349c..17ff037 100644 --- a/src/components/templates/modern/config.ts +++ b/src/components/templates/modern/config.ts @@ -20,4 +20,5 @@ export const modernConfig: ResumeTemplate = { basic: { layout: "center", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/components/templates/timeline/config.ts b/src/components/templates/timeline/config.ts index 364c9f2..5e46cca 100644 --- a/src/components/templates/timeline/config.ts +++ b/src/components/templates/timeline/config.ts @@ -20,4 +20,5 @@ export const timelineConfig: ResumeTemplate = { basic: { layout: "right", }, + availableSections: ["skills", "experience", "projects", "education"], }; diff --git a/src/config/initialResumeData.ts b/src/config/initialResumeData.ts index bcc3e60..4fd98db 100644 --- a/src/config/initialResumeData.ts +++ b/src/config/initialResumeData.ts @@ -50,7 +50,7 @@ export const initialResumeState = { id: "1", school: "北京大学", major: "计算机科学与技术", - degree: "本科", + degree: "", startDate: "2013-09", endDate: "2017-06", visible: true, @@ -326,3 +326,47 @@ export const initialResumeStateEn = { activeSection: "basic", globalSettings: initialGlobalSettings, }; + +export const blankResumeState = { + ...initialResumeState, + title: "新建简历", + basic: { + ...initialResumeState.basic, + name: "", + title: "", + email: "", + phone: "", + location: "", + birthDate: "", + employementStatus: "", + photo: "", + customFields: [], + }, + education: [], + skillContent: "", + experience: [], + projects: [], + menuSections: [initialResumeState.menuSections[0]], +}; + +export const blankResumeStateEn = { + ...initialResumeStateEn, + title: "New Resume", + basic: { + ...initialResumeStateEn.basic, + name: "", + title: "", + email: "", + phone: "", + location: "", + birthDate: "", + employementStatus: "", + photo: "", + customFields: [], + }, + education: [], + skillContent: "", + experience: [], + projects: [], + menuSections: [initialResumeStateEn.menuSections[0]], +}; diff --git a/src/config/modules.ts b/src/config/modules.ts new file mode 100644 index 0000000..4309544 --- /dev/null +++ b/src/config/modules.ts @@ -0,0 +1,12 @@ +export interface ResumeModule { + id: string; + titleKey: string; + icon: string; +} + +export const STANDARD_MODULES: Record = { + skills: { id: "skills", titleKey: "skills", icon: "⚡" }, + experience: { id: "experience", titleKey: "experience", icon: "💼" }, + projects: { id: "projects", titleKey: "projects", icon: "🚀" }, + education: { id: "education", titleKey: "education", icon: "🎓" }, +}; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index d9daee1..7409851 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -291,7 +291,14 @@ "sidePanel": { "layout": { "title": "Layout", - "addCustomSection": "Add Custom Section" + "addCustomSection": "Add Module", + "addCustomSectionOption": "Add Custom Section", + "standardSections": { + "skills": "Skills", + "experience": "Experience", + "projects": "Projects", + "education": "Education" + } }, "theme": { "title": "Theme Color", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 4e2da60..d4f17a1 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -251,7 +251,14 @@ "sidePanel": { "layout": { "title": "布局", - "addCustomSection": "添加自定义模块" + "addCustomSection": "添加模块", + "addCustomSectionOption": "添加自定义模块", + "standardSections": { + "skills": "专业技能", + "experience": "工作经验", + "projects": "项目经历", + "education": "教育经历" + } }, "theme": { "title": "主题色", diff --git a/src/store/useResumeStore.ts b/src/store/useResumeStore.ts index 1d94360..1c1f4eb 100644 --- a/src/store/useResumeStore.ts +++ b/src/store/useResumeStore.ts @@ -15,6 +15,8 @@ import { DEFAULT_TEMPLATES } from "@/config"; import { initialResumeState, initialResumeStateEn, + blankResumeState, + blankResumeStateEn, } from "@/config/initialResumeData"; import { generateUUID } from "@/utils/uuid"; interface ResumeStore { @@ -22,7 +24,7 @@ interface ResumeStore { activeResumeId: string | null; activeResume: ResumeData | null; - createResume: (templateId: string | null) => string; + createResume: (templateId: string | null, isBlank?: boolean) => string; deleteResume: (resume: ResumeData) => void; duplicateResume: (resumeId: string) => string; updateResume: (resumeId: string, data: Partial) => void; @@ -113,7 +115,7 @@ export const useResumeStore = create( activeResumeId: null, activeResume: null, - createResume: (templateId = null) => { + createResume: (templateId = null, isBlank = false) => { const locale = typeof document !== "undefined" ? document.cookie @@ -122,8 +124,14 @@ export const useResumeStore = create( ?.split("=")[1] || "zh" : "zh"; - const initialResumeData = - locale === "en" ? initialResumeStateEn : initialResumeState; + let initialResumeData: any; + if (isBlank) { + initialResumeData = + locale === "en" ? blankResumeStateEn : blankResumeState; + } else { + initialResumeData = + locale === "en" ? initialResumeStateEn : initialResumeState; + } const id = generateUUID(); const template = templateId diff --git a/src/types/template.ts b/src/types/template.ts index a735bec..7b41705 100644 --- a/src/types/template.ts +++ b/src/types/template.ts @@ -20,6 +20,7 @@ export interface ResumeTemplate { basic: { layout?: "left" | "center" | "right"; }; + availableSections?: string[]; } export interface TemplateConfig {