mirror of
https://github.com/LiuYuYang01/ThriveX-Blog.git
synced 2026-05-06 22:03:08 +08:00
重构:用默认值和安全的数据结构增强设备、my和resume页面中的数据处理
更新了设备页面,使用安全的数据结构,并为设备项和组提供默认值。 —改进了我的页面,实现了用户信息的默认值,保证了数据的安全处理。 -重构简历页面,包括默认的个人信息、链接、教育和项目详细信息,确保在数据丢失时使用回退值。
This commit is contained in:
@@ -9,7 +9,32 @@ interface Equipment {
|
||||
|
||||
export default async () => {
|
||||
const { data } = (await getPageConfigDataByNameAPI('equipment')) || { data: {} as Config };
|
||||
const { list } = data.value as { list: Equipment[] };
|
||||
const value = (data?.value as { list: Equipment[] }) || { list: [] };
|
||||
|
||||
const defaultItem = {
|
||||
name: '未命名设备',
|
||||
image: '',
|
||||
price: '0',
|
||||
description: '暂无描述',
|
||||
color: '#f5f5f5',
|
||||
};
|
||||
|
||||
const defaultGroup = {
|
||||
category: '未分类',
|
||||
description: '暂无描述',
|
||||
items: [] as Equipment['items'],
|
||||
};
|
||||
|
||||
const safeList: Equipment[] = (value.list || []).map((group) => ({
|
||||
...defaultGroup,
|
||||
...group,
|
||||
items: (group?.items || []).map((item) => ({
|
||||
...defaultItem,
|
||||
...item,
|
||||
price: `${item.price ?? defaultItem.price}`,
|
||||
color: item.color || defaultItem.color,
|
||||
})),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -18,7 +43,7 @@ export default async () => {
|
||||
|
||||
<div className="pt-20 pb-10">
|
||||
<div className="w-[90%] lg:w-[1200px] mx-auto mt-10 space-y-20 md:space-y-24">
|
||||
{list.map((group, index) => (
|
||||
{safeList.map((group, index) => (
|
||||
<div key={index}>
|
||||
<h2 className="text-xl">{group.category}</h2>
|
||||
<p className="text-gray-600 mb-6">{group.description}</p>
|
||||
|
||||
@@ -14,7 +14,42 @@ import InfoOne from './component/InfoOne';
|
||||
|
||||
export default async () => {
|
||||
const { data } = (await getPageConfigDataByNameAPI('my')) || { data: {} as Config };
|
||||
const { info_style, info_one, info_two, character, goals, project, technology_stack, hometown } = data.value as MyData;
|
||||
const value = (data?.value as MyData) || ({} as MyData);
|
||||
|
||||
const defaultInfoOne = {
|
||||
name: '未命名',
|
||||
notes: '',
|
||||
avatar: '',
|
||||
profession: '',
|
||||
introduction: '',
|
||||
};
|
||||
|
||||
const defaultInfoTwo = {
|
||||
author: '未提供作者',
|
||||
know_me: '#',
|
||||
left_tags: [] as string[],
|
||||
right_tags: [] as string[],
|
||||
avatar_url: '',
|
||||
};
|
||||
|
||||
const defaultCharacter = [] as MyData['character'];
|
||||
const defaultGoals = [] as MyData['goals'];
|
||||
const defaultProject = [] as MyData['project'];
|
||||
const defaultTechStack = [] as MyData['technology_stack'];
|
||||
const defaultHometown = [0, 0] as MyData['hometown'];
|
||||
|
||||
const safeData: MyData = {
|
||||
info_style: value.info_style || 'info_one',
|
||||
info_one: { ...defaultInfoOne, ...(value.info_one || {}) },
|
||||
info_two: { ...defaultInfoTwo, ...(value.info_two || {}) },
|
||||
character: value.character ?? defaultCharacter,
|
||||
goals: value.goals ?? defaultGoals,
|
||||
project: value.project ?? defaultProject,
|
||||
technology_stack: value.technology_stack ?? defaultTechStack,
|
||||
hometown: value.hometown ?? defaultHometown,
|
||||
};
|
||||
|
||||
const { info_style, info_one, info_two, character, goals, project, technology_stack, hometown } = safeData;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -34,7 +69,7 @@ export default async () => {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row w-[90%] sm:w-9/12 mt-52 mx-auto">
|
||||
<Map position={hometown}/>
|
||||
<Map position={hometown} />
|
||||
<Technology list={technology_stack} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,58 @@ import { Resume } from '@/types/app/resume';
|
||||
export default ({ data }: { data: Resume }) => {
|
||||
const { personalInfo, advantages, links, skills, workExperience, projects, education } = data || {};
|
||||
|
||||
const defaultPersonalInfo = {
|
||||
name: '神秘人',
|
||||
title: '前端开发工程师',
|
||||
age: '22岁',
|
||||
location: '中国',
|
||||
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=3311118881&s=640',
|
||||
contact: {
|
||||
phone: '10000000000',
|
||||
email: 'example@example.com',
|
||||
github: 'https://github.com',
|
||||
},
|
||||
};
|
||||
|
||||
const safePersonalInfo = {
|
||||
...defaultPersonalInfo,
|
||||
...personalInfo,
|
||||
contact: {
|
||||
...defaultPersonalInfo.contact,
|
||||
...personalInfo?.contact,
|
||||
},
|
||||
};
|
||||
|
||||
const defaultLinks = {
|
||||
github: 'https://github.com',
|
||||
csdn: 'https://blog.csdn.net',
|
||||
blog: 'https://example.com',
|
||||
};
|
||||
|
||||
const safeLinks = {
|
||||
...defaultLinks,
|
||||
...links,
|
||||
};
|
||||
|
||||
const defaultEducation = {
|
||||
school: '未提供学校',
|
||||
major: '未提供专业',
|
||||
degree: '未提供学历',
|
||||
period: '未提供时间',
|
||||
achievements: ['暂无成就'],
|
||||
};
|
||||
|
||||
const safeEducation = {
|
||||
...defaultEducation,
|
||||
...education,
|
||||
achievements: education?.achievements ?? defaultEducation.achievements,
|
||||
};
|
||||
|
||||
const safeSkills = skills ?? ['未填写技能'];
|
||||
const safeAdvantages = advantages ?? ['暂无优势'];
|
||||
const safeWorkExperience = workExperience ?? [];
|
||||
const safeProjects = projects ?? [];
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.style.scrollBehavior = 'smooth';
|
||||
}, []);
|
||||
@@ -36,8 +88,8 @@ export default ({ data }: { data: Resume }) => {
|
||||
|
||||
// 技能标签云数据处理
|
||||
const getSkillTags = () => {
|
||||
if (!skills || skills.length === 0) return [];
|
||||
return skills.map((skill) => {
|
||||
if (!safeSkills || safeSkills.length === 0) return [];
|
||||
return safeSkills.map((skill) => {
|
||||
// 提取技能名称和熟练度(如果有)
|
||||
const parts = skill.split('(');
|
||||
const name = parts[0].trim();
|
||||
@@ -56,8 +108,8 @@ export default ({ data }: { data: Resume }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<title>{`${personalInfo?.name ?? ''} - ${personalInfo?.title ?? ''}`}</title>
|
||||
<meta name="description" content={`${personalInfo?.name ?? ''} - ${personalInfo?.title ?? ''} 的个人简历`} />
|
||||
<title>{`${safePersonalInfo.name || '匿名用户'} - ${safePersonalInfo.title || '前端开发工程师'}`}</title>
|
||||
<meta name="description" content={`${safePersonalInfo.name || '匿名用户'} - ${safePersonalInfo.title || '前端开发工程师'} 的个人简历`} />
|
||||
|
||||
<div className="min-h-screen py-8 mt-[60px] px-4 sm:px-6 lg:px-8 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
|
||||
<motion.div initial="hidden" animate="visible" variants={containerVariants} className="max-w-5xl mx-auto">
|
||||
@@ -67,37 +119,37 @@ export default ({ data }: { data: Resume }) => {
|
||||
<div className="flex flex-col md:flex-row items-center gap-6">
|
||||
<motion.div initial={{ scale: 0.8 }} animate={{ scale: 1 }} transition={{ duration: 0.5 }} className="relative flex justify-center items-center w-32 h-32 rounded-full overflow-hidden bg-white shadow-xl border-transparent border-4 dark:border-gray-700">
|
||||
<div className="flex justify-center items-center w-[98%] h-[98%] rounded-full overflow-hidden">
|
||||
<img src={personalInfo?.avatar} alt={personalInfo?.name} className="object-cover w-full h-full" />
|
||||
<img src={safePersonalInfo.avatar || defaultPersonalInfo.avatar} alt={safePersonalInfo.name || '匿名用户'} className="object-cover w-full h-full" />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="flex-1 text-center md:text-left">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-1">{personalInfo?.name}</h1>
|
||||
<h2 className="font-semibold text-blue-600 dark:text-blue-400 mb-3">{personalInfo?.title}</h2>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-1">{safePersonalInfo.name || '匿名用户'}</h1>
|
||||
<h2 className="font-semibold text-blue-600 dark:text-blue-400 mb-3">{safePersonalInfo.title || '前端开发工程师'}</h2>
|
||||
|
||||
<div className="flex flex-wrap justify-center md:justify-start gap-4 text-gray-600 dark:text-gray-300 mb-4">
|
||||
<span className="flex items-center text-sm">
|
||||
<span className="font-medium mr-1">年龄:</span>
|
||||
{personalInfo?.age}
|
||||
{safePersonalInfo.age || '22岁'}
|
||||
</span>
|
||||
<span className="flex items-center text-sm">
|
||||
<span className="font-medium mr-1">地点:</span>
|
||||
{personalInfo?.location}
|
||||
{safePersonalInfo.location || '中国'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap justify-center md:justify-start gap-4 text-gray-700 dark:text-gray-300">
|
||||
<a href={`tel:${personalInfo?.contact?.phone}`} className="flex items-center hover:text-blue-600 text-sm">
|
||||
<a href={`tel:${safePersonalInfo.contact?.phone || defaultPersonalInfo.contact.phone}`} className="flex items-center hover:text-blue-600 text-sm">
|
||||
<FaPhone className="mr-1 text-blue-500" size={16} />
|
||||
<span>{personalInfo?.contact?.phone}</span>
|
||||
<span>{safePersonalInfo.contact?.phone || defaultPersonalInfo.contact.phone}</span>
|
||||
</a>
|
||||
|
||||
<a href={`mailto:${personalInfo?.contact?.email}`} className="flex items-center hover:text-blue-600 text-sm">
|
||||
<a href={`mailto:${safePersonalInfo.contact?.email || defaultPersonalInfo.contact.email}`} className="flex items-center hover:text-blue-600 text-sm">
|
||||
<FaEnvelope className="mr-1 text-blue-500" size={16} />
|
||||
<span>{personalInfo?.contact?.email}</span>
|
||||
<span>{safePersonalInfo.contact?.email || defaultPersonalInfo.contact.email}</span>
|
||||
</a>
|
||||
|
||||
<a href={personalInfo?.contact?.github} target="_blank" rel="noopener noreferrer" className="flex items-center hover:text-blue-600 text-sm">
|
||||
<a href={safePersonalInfo.contact?.github || defaultPersonalInfo.contact.github} target="_blank" rel="noopener noreferrer" className="flex items-center hover:text-blue-600 text-sm">
|
||||
<FaGithub className="mr-1 text-blue-500" size={16} />
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
@@ -118,7 +170,7 @@ export default ({ data }: { data: Resume }) => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 text-gray-600 dark:text-gray-300 text-sm">
|
||||
{advantages?.map((advantage, index) => (
|
||||
{safeAdvantages.map((advantage, index) => (
|
||||
<p key={index} className="flex items-center hover:text-blue-600 cursor-pointer">
|
||||
{advantage}
|
||||
</p>
|
||||
@@ -151,9 +203,9 @@ export default ({ data }: { data: Resume }) => {
|
||||
|
||||
<div className="">
|
||||
<div className="flex justify-between mb-3 pb-1.5 border-b border-gray-100 dark:border-gray-700">
|
||||
<h4 className="text-md font-bold text-gray-800 dark:text-white mb-1">{education?.school}</h4>
|
||||
<h4 className="text-md font-bold text-gray-800 dark:text-white mb-1">{safeEducation.school || '未提供学校'}</h4>
|
||||
<p className="text-gray-600 dark:text-gray-300 text-sm mb-1">
|
||||
{education?.major} | {education?.degree}
|
||||
{safeEducation.major || '未提供专业'} | {safeEducation.degree || '未提供学历'}
|
||||
</p>
|
||||
{/* <p className="text-gray-500 dark:text-gray-400 text-xs">
|
||||
{education?.period}
|
||||
@@ -161,7 +213,7 @@ export default ({ data }: { data: Resume }) => {
|
||||
</div>
|
||||
|
||||
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-1 text-sm">
|
||||
{education?.achievements?.map((achievement, index) => (
|
||||
{safeEducation.achievements?.map((achievement, index) => (
|
||||
<li key={index} className="flex items-center">
|
||||
<span className="flex items-center justify-center bg-blue-500 min-w-1.5 min-h-1.5 mr-2 rounded-full"></span>
|
||||
<span>{achievement}</span>
|
||||
@@ -179,20 +231,20 @@ export default ({ data }: { data: Resume }) => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{links?.github && (
|
||||
<a href={links.github} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
{safeLinks?.github && (
|
||||
<a href={safeLinks.github || defaultLinks.github} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
<FaGithub className="mr-2" size={16} /> GitHub
|
||||
</a>
|
||||
)}
|
||||
|
||||
{links?.csdn && (
|
||||
<a href={links.csdn} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
{safeLinks?.csdn && (
|
||||
<a href={safeLinks.csdn || defaultLinks.csdn} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
<FaGlobe className="mr-2" size={16} /> CSDN 技术博客
|
||||
</a>
|
||||
)}
|
||||
|
||||
{links?.blog && (
|
||||
<a href={links.blog} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
{safeLinks?.blog && (
|
||||
<a href={safeLinks.blog || defaultLinks.blog} target="_blank" rel="noopener noreferrer" className="flex items-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm font-medium transition-colors">
|
||||
<FaProjectDiagram className="mr-2" size={16} /> 开源项目作品
|
||||
</a>
|
||||
)}
|
||||
@@ -210,16 +262,16 @@ export default ({ data }: { data: Resume }) => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{workExperience?.map((job, index) => (
|
||||
{safeWorkExperience.map((job, index) => (
|
||||
<div key={index} className="relative pl-6 pb-6 border-l-2 border-blue-200 dark:border-blue-900">
|
||||
<div className="absolute left-[-7px] top-0 w-3 h-3 rounded-full bg-blue-500 ring-4 ring-blue-100 dark:ring-blue-900"></div>
|
||||
<div className="flex flex-wrap justify-between items-start mb-1">
|
||||
<h4 className="text-md font-bold text-gray-900 dark:text-white">{job.company}</h4>
|
||||
<span className="text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded mt-1 md:mt-0">{job.period}</span>
|
||||
<h4 className="text-md font-bold text-gray-900 dark:text-white">{job.company || '未提供公司'}</h4>
|
||||
<span className="text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded mt-1 md:mt-0">{job.period || '未提供时间'}</span>
|
||||
</div>
|
||||
<p className="text-gray-700 dark:text-gray-300 font-medium text-sm mb-2">{job.position}</p>
|
||||
<p className="text-gray-700 dark:text-gray-300 font-medium text-sm mb-2">{job.position || '未提供职位'}</p>
|
||||
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-1 text-sm">
|
||||
{job.responsibilities?.map((responsibility, index) => (
|
||||
{(job.responsibilities ?? ['暂无职责']).map((responsibility, index) => (
|
||||
<li key={index} className="flex items-center">
|
||||
<span className="flex items-center justify-center bg-blue-500 min-w-1.5 min-h-1.5 mr-2 rounded-full"></span>
|
||||
<span>{responsibility}</span>
|
||||
@@ -239,14 +291,14 @@ export default ({ data }: { data: Resume }) => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{projects?.map((project, index) => (
|
||||
{safeProjects.map((project, index) => (
|
||||
<div key={index} className="border border-gray-100 dark:border-gray-700 rounded-lg overflow-hidden">
|
||||
<div className="bg-gray-50 dark:bg-gray-700/50 p-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<div className="flex flex-wrap justify-between items-start mb-1">
|
||||
<h4 className="text-md font-bold text-gray-900 dark:text-white">{project.name}</h4>
|
||||
<span className="text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded mt-1 md:mt-0">{project.period}</span>
|
||||
<h4 className="text-md font-bold text-gray-900 dark:text-white">{project.name || '未命名项目'}</h4>
|
||||
<span className="text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded mt-1 md:mt-0">{project.period || '未提供时间'}</span>
|
||||
</div>
|
||||
<p className="text-gray-700 dark:text-gray-300 font-medium text-sm">{project.role}</p>
|
||||
<p className="text-gray-700 dark:text-gray-300 font-medium text-sm">{project.role || '未提供角色'}</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-3 text-sm">
|
||||
@@ -268,12 +320,12 @@ export default ({ data }: { data: Resume }) => {
|
||||
<h5 className="font-bold text-gray-800 dark:text-white mb-1 text-sm">技术栈</h5>
|
||||
<div className="text-gray-600 dark:text-gray-300 text-sm flex flex-wrap gap-2">
|
||||
{typeof project.techStack === 'string' ? (
|
||||
project.techStack
|
||||
project.techStack || '未提供技术栈'
|
||||
) : (
|
||||
<>
|
||||
<span className="px-2 py-0.5 rounded text-xs">前端: {project.techStack.frontend}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs">后端: {project.techStack.backend}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs">部署: {project.techStack.deployment}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs">前端: {project.techStack?.frontend || '未提供'}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs">后端: {project.techStack?.backend || '未提供'}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs">部署: {project.techStack?.deployment || '未提供'}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -314,9 +366,9 @@ export default ({ data }: { data: Resume }) => {
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{project.repositories?.map((item, index) => (
|
||||
<div key={index} className="flex items-center">
|
||||
<span>{item.name}:</span>
|
||||
<a href={item.url as string} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-xs font-medium bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded transition-colors">
|
||||
{item.url}
|
||||
<span>{item.name || '未命名链接'}:</span>
|
||||
<a href={(item.url as string) || '#'} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-xs font-medium bg-blue-50 dark:bg-blue-900/30 px-2 py-1 rounded transition-colors">
|
||||
{item.url || '未提供链接'}
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user