mirror of
https://github.com/JOYCEQL/magic-resume.git
synced 2026-06-02 07:43:34 +02:00
feat: add self-evaluation section
This commit is contained in:
@@ -9,6 +9,7 @@ import ProjectPanel from "./project/ProjectPanel";
|
|||||||
import ExperiencePanel from "./experience/ExperiencePanel";
|
import ExperiencePanel from "./experience/ExperiencePanel";
|
||||||
import CustomPanel from "./custom/CustomPanel";
|
import CustomPanel from "./custom/CustomPanel";
|
||||||
import SkillPanel from "./skills/SkillPanel";
|
import SkillPanel from "./skills/SkillPanel";
|
||||||
|
import SelfEvaluationPanel from "./self-evaluation/SelfEvaluationPanel";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -34,6 +35,8 @@ export function EditPanel() {
|
|||||||
return <ExperiencePanel />;
|
return <ExperiencePanel />;
|
||||||
case "skills":
|
case "skills":
|
||||||
return <SkillPanel />;
|
return <SkillPanel />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationPanel />;
|
||||||
default:
|
default:
|
||||||
if (activeSection?.startsWith("custom")) {
|
if (activeSection?.startsWith("custom")) {
|
||||||
return <CustomPanel sectionId={activeSection} />;
|
return <CustomPanel sectionId={activeSection} />;
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ export function SidePanel() {
|
|||||||
);
|
);
|
||||||
}, [currentTemplate]);
|
}, [currentTemplate]);
|
||||||
|
|
||||||
|
// 过滤掉 menuSections 中已存在的模块,避免重复添加和 key 冲突
|
||||||
|
const filteredModules = useMemo(() => {
|
||||||
|
const existingIds = new Set(menuSections.map((s: MenuSection) => s.id));
|
||||||
|
return availableModules.filter((m) => !existingIds.has(m.id));
|
||||||
|
}, [availableModules, menuSections]);
|
||||||
|
|
||||||
const fontOptions = [
|
const fontOptions = [
|
||||||
{ value: "sans", label: t("typography.font.sans") },
|
{ value: "sans", label: t("typography.font.sans") },
|
||||||
{ value: "serif", label: t("typography.font.serif") },
|
{ value: "serif", label: t("typography.font.serif") },
|
||||||
@@ -180,7 +186,7 @@ export function SidePanel() {
|
|||||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-1" align="center">
|
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-1" align="center">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{/* Standard Sections Library */}
|
{/* Standard Sections Library */}
|
||||||
{availableModules.map((section) => (
|
{filteredModules.map((section) => (
|
||||||
<button
|
<button
|
||||||
key={section.id}
|
key={section.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -201,7 +207,7 @@ export function SidePanel() {
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Divider for Custom Section */}
|
{/* Divider for Custom Section */}
|
||||||
{availableModules.length > 0 && (
|
{filteredModules.length > 0 && (
|
||||||
<div className="h-px bg-border my-1" />
|
<div className="h-px bg-border my-1" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { useResumeStore } from "@/store/useResumeStore";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import Field from "../Field";
|
||||||
|
|
||||||
|
const SelfEvaluationPanel = () => {
|
||||||
|
const { activeResume, updateSelfEvaluationContent } = useResumeStore();
|
||||||
|
const selfEvaluationContent = activeResume?.selfEvaluationContent ?? "";
|
||||||
|
const handleChange = (value: string) => {
|
||||||
|
updateSelfEvaluationContent(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"rounded-lg border p-4",
|
||||||
|
"bg-card",
|
||||||
|
"border-border"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
value={selfEvaluationContent}
|
||||||
|
onChange={handleChange}
|
||||||
|
type="editor"
|
||||||
|
placeholder="描述你的自我评价..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationPanel;
|
||||||
@@ -20,5 +20,5 @@ export const classicConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "left",
|
layout: "left",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface ClassicTemplateProps {
|
interface ClassicTemplateProps {
|
||||||
@@ -29,6 +30,8 @@ const ClassicTemplate: React.FC<ClassicTemplateProps> = ({ data, template }) =>
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const sectionTitle = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const sectionTitle = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const creativeConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "left",
|
layout: "left",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface CreativeTemplateProps {
|
interface CreativeTemplateProps {
|
||||||
@@ -31,6 +32,8 @@ const CreativeTemplate: React.FC<CreativeTemplateProps> = ({ data, template }) =
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const elegantConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "center",
|
layout: "center",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface ElegantTemplateProps {
|
interface ElegantTemplateProps {
|
||||||
@@ -29,6 +30,8 @@ const ElegantTemplate: React.FC<ElegantTemplateProps> = ({ data, template }) =>
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const leftRightConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "left",
|
layout: "left",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface LeftRightTemplateProps {
|
interface LeftRightTemplateProps {
|
||||||
@@ -29,6 +30,8 @@ const LeftRightTemplate: React.FC<LeftRightTemplateProps> = ({ data, template })
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const minimalistConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "center",
|
layout: "center",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface MinimalistTemplateProps {
|
interface MinimalistTemplateProps {
|
||||||
@@ -29,6 +30,8 @@ const MinimalistTemplate: React.FC<MinimalistTemplateProps> = ({ data, template
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const modernConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "center",
|
layout: "center",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface ModernTemplateProps {
|
interface ModernTemplateProps {
|
||||||
@@ -29,6 +30,8 @@ const ModernTemplate: React.FC<ModernTemplateProps> = ({ data, template }) => {
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -20,5 +20,5 @@ export const timelineConfig: ResumeTemplate = {
|
|||||||
basic: {
|
basic: {
|
||||||
layout: "right",
|
layout: "right",
|
||||||
},
|
},
|
||||||
availableSections: ["skills", "experience", "projects", "education"],
|
availableSections: ["skills", "experience", "projects", "education", "selfEvaluation"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ExperienceSection from "./sections/ExperienceSection";
|
|||||||
import EducationSection from "./sections/EducationSection";
|
import EducationSection from "./sections/EducationSection";
|
||||||
import ProjectSection from "./sections/ProjectSection";
|
import ProjectSection from "./sections/ProjectSection";
|
||||||
import SkillSection from "./sections/SkillSection";
|
import SkillSection from "./sections/SkillSection";
|
||||||
|
import SelfEvaluationSection from "./sections/SelfEvaluationSection";
|
||||||
import CustomSection from "./sections/CustomSection";
|
import CustomSection from "./sections/CustomSection";
|
||||||
|
|
||||||
interface TimelineTemplateProps {
|
interface TimelineTemplateProps {
|
||||||
@@ -40,6 +41,8 @@ const TimelineTemplate: React.FC<TimelineTemplateProps> = ({ data, template }) =
|
|||||||
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} showTitle={false} />;
|
return <SkillSection skill={data.skillContent} globalSettings={data.globalSettings} showTitle={false} />;
|
||||||
case "projects":
|
case "projects":
|
||||||
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} showTitle={false} />;
|
return <ProjectSection projects={data.projects} globalSettings={data.globalSettings} showTitle={false} />;
|
||||||
|
case "selfEvaluation":
|
||||||
|
return <SelfEvaluationSection content={data.selfEvaluationContent} globalSettings={data.globalSettings} showTitle={false} />;
|
||||||
default:
|
default:
|
||||||
if (sectionId in data.customData) {
|
if (sectionId in data.customData) {
|
||||||
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
const title = data.menuSections.find((s) => s.id === sectionId)?.title || sectionId;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
import SectionTitle from "./SectionTitle";
|
||||||
|
import SectionWrapper from "../../shared/SectionWrapper";
|
||||||
|
import { GlobalSettings } from "@/types/resume";
|
||||||
|
import { normalizeRichTextContent } from "@/lib/richText";
|
||||||
|
|
||||||
|
interface SelfEvaluationSectionProps {
|
||||||
|
content?: string;
|
||||||
|
globalSettings?: GlobalSettings;
|
||||||
|
showTitle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelfEvaluationSection = ({ content, globalSettings, showTitle = true }: SelfEvaluationSectionProps) => {
|
||||||
|
return (
|
||||||
|
<SectionWrapper sectionId="selfEvaluation" style={{ marginTop: `${globalSettings?.sectionSpacing || 24}px` }}>
|
||||||
|
<SectionTitle type="selfEvaluation" globalSettings={globalSettings} showTitle={showTitle} />
|
||||||
|
<motion.div style={{ marginTop: `${globalSettings?.paragraphSpacing}px` }}>
|
||||||
|
<motion.div className="text-baseFont" layout="position"
|
||||||
|
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, lineHeight: globalSettings?.lineHeight || 1.6 }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: normalizeRichTextContent(content) }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</SectionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelfEvaluationSection;
|
||||||
@@ -76,6 +76,7 @@ export const initialResumeState = {
|
|||||||
<li>技术管理:具备团队管理经验,主导过多个大型项目的技术选型和架构设计</li>
|
<li>技术管理:具备团队管理经验,主导过多个大型项目的技术选型和架构设计</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>`,
|
</div>`,
|
||||||
|
selfEvaluationContent: "",
|
||||||
experience: [
|
experience: [
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
@@ -224,6 +225,7 @@ export const initialResumeStateEn = {
|
|||||||
<li>Technical Leadership: Team management experience, led technology selection and architecture design for large projects</li>
|
<li>Technical Leadership: Team management experience, led technology selection and architecture design for large projects</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>`,
|
</div>`,
|
||||||
|
selfEvaluationContent: "",
|
||||||
experience: [
|
experience: [
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
@@ -344,6 +346,7 @@ export const blankResumeState = {
|
|||||||
},
|
},
|
||||||
education: [],
|
education: [],
|
||||||
skillContent: "",
|
skillContent: "",
|
||||||
|
selfEvaluationContent: "",
|
||||||
experience: [],
|
experience: [],
|
||||||
projects: [],
|
projects: [],
|
||||||
menuSections: [initialResumeState.menuSections[0]],
|
menuSections: [initialResumeState.menuSections[0]],
|
||||||
@@ -366,6 +369,7 @@ export const blankResumeStateEn = {
|
|||||||
},
|
},
|
||||||
education: [],
|
education: [],
|
||||||
skillContent: "",
|
skillContent: "",
|
||||||
|
selfEvaluationContent: "",
|
||||||
experience: [],
|
experience: [],
|
||||||
projects: [],
|
projects: [],
|
||||||
menuSections: [initialResumeStateEn.menuSections[0]],
|
menuSections: [initialResumeStateEn.menuSections[0]],
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ export const STANDARD_MODULES: Record<string, ResumeModule> = {
|
|||||||
experience: { id: "experience", titleKey: "experience", icon: "💼" },
|
experience: { id: "experience", titleKey: "experience", icon: "💼" },
|
||||||
projects: { id: "projects", titleKey: "projects", icon: "🚀" },
|
projects: { id: "projects", titleKey: "projects", icon: "🚀" },
|
||||||
education: { id: "education", titleKey: "education", icon: "🎓" },
|
education: { id: "education", titleKey: "education", icon: "🎓" },
|
||||||
|
selfEvaluation: { id: "selfEvaluation", titleKey: "selfEvaluation", icon: "💬" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -316,7 +316,8 @@
|
|||||||
"skills": "Skills",
|
"skills": "Skills",
|
||||||
"experience": "Experience",
|
"experience": "Experience",
|
||||||
"projects": "Projects",
|
"projects": "Projects",
|
||||||
"education": "Education"
|
"education": "Education",
|
||||||
|
"selfEvaluation": "Self Evaluation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
|
|||||||
@@ -276,7 +276,8 @@
|
|||||||
"skills": "专业技能",
|
"skills": "专业技能",
|
||||||
"experience": "工作经验",
|
"experience": "工作经验",
|
||||||
"projects": "项目经历",
|
"projects": "项目经历",
|
||||||
"education": "教育经历"
|
"education": "教育经历",
|
||||||
|
"selfEvaluation": "自我评价"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ interface ResumeStore {
|
|||||||
deleteProject: (id: string) => void;
|
deleteProject: (id: string) => void;
|
||||||
setDraggingProjectId: (id: string | null) => void;
|
setDraggingProjectId: (id: string | null) => void;
|
||||||
updateSkillContent: (skillContent: string) => void;
|
updateSkillContent: (skillContent: string) => void;
|
||||||
|
updateSelfEvaluationContent: (content: string) => void;
|
||||||
reorderSections: (newOrder: ResumeData["menuSections"]) => void;
|
reorderSections: (newOrder: ResumeData["menuSections"]) => void;
|
||||||
toggleSectionVisibility: (sectionId: string) => void;
|
toggleSectionVisibility: (sectionId: string) => void;
|
||||||
setActiveSection: (sectionId: string) => void;
|
setActiveSection: (sectionId: string) => void;
|
||||||
@@ -417,6 +418,13 @@ export const useResumeStore = create(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateSelfEvaluationContent: (selfEvaluationContent) => {
|
||||||
|
const { activeResumeId } = get();
|
||||||
|
if (activeResumeId) {
|
||||||
|
get().updateResume(activeResumeId, { selfEvaluationContent });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
reorderSections: (newOrder) => {
|
reorderSections: (newOrder) => {
|
||||||
const { activeResumeId, resumes } = get();
|
const { activeResumeId, resumes } = get();
|
||||||
if (activeResumeId) {
|
if (activeResumeId) {
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ export interface ResumeData {
|
|||||||
projects: Project[];
|
projects: Project[];
|
||||||
customData: Record<string, CustomItem[]>;
|
customData: Record<string, CustomItem[]>;
|
||||||
skillContent: string;
|
skillContent: string;
|
||||||
|
selfEvaluationContent: string;
|
||||||
activeSection: string;
|
activeSection: string;
|
||||||
draggingProjectId: string | null;
|
draggingProjectId: string | null;
|
||||||
menuSections: MenuSection[];
|
menuSections: MenuSection[];
|
||||||
|
|||||||
Reference in New Issue
Block a user