111
This commit is contained in:
55
src/data/anime.ts
Normal file
55
src/data/anime.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// 本地番剧数据配置
|
||||
export type AnimeItem = {
|
||||
title: string;
|
||||
status: "watching" | "completed" | "planned";
|
||||
rating: number;
|
||||
cover: string;
|
||||
description: string;
|
||||
episodes: string;
|
||||
year: string;
|
||||
genre: string[];
|
||||
studio: string;
|
||||
link: string;
|
||||
progress: number;
|
||||
totalEpisodes: number;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
|
||||
const localAnimeList: AnimeItem[] = [
|
||||
{
|
||||
title: "Lycoris Recoil",
|
||||
status: "completed",
|
||||
rating: 9.8,
|
||||
cover: "/assets/anime/lkls.webp",
|
||||
description: "Girl's gunfight",
|
||||
episodes: "12 episodes",
|
||||
year: "2022",
|
||||
genre: ["Action", "Slice of life"],
|
||||
studio: "A-1 Pictures",
|
||||
link: "https://www.bilibili.com/bangumi/media/md28338623",
|
||||
progress: 12,
|
||||
totalEpisodes: 12,
|
||||
startDate: "2022-07",
|
||||
endDate: "2022-09",
|
||||
},
|
||||
{
|
||||
title: "名侦探柯南",
|
||||
status: "watching",
|
||||
rating: 9.7,
|
||||
cover: "/assets/anime/mztkn.webp",
|
||||
description: "名侦探",
|
||||
episodes: "12 episodes",
|
||||
year: "1996",
|
||||
genre: ["推理", "悬疑"],
|
||||
studio: "TMS Entertainment",
|
||||
link: "https://www.bilibili.com/bangumi/media/md28228775",
|
||||
progress: 1194,
|
||||
totalEpisodes: 1241,
|
||||
startDate: "1996-01",
|
||||
endDate: "Unkown",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
export default localAnimeList;
|
||||
28
src/data/devices.ts
Normal file
28
src/data/devices.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// 设备数据配置文件
|
||||
|
||||
export interface Device {
|
||||
name: string;
|
||||
image: string;
|
||||
specs: string;
|
||||
description: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
// 设备类别类型,支持品牌和自定义类别
|
||||
export type DeviceCategory = {
|
||||
[categoryName: string]: Device[];
|
||||
} & {
|
||||
自定义?: Device[];
|
||||
};
|
||||
|
||||
export const devicesData: DeviceCategory = {
|
||||
Xiaomi: [
|
||||
{
|
||||
name: "Xiaomi 14Pro",
|
||||
image: "/images/device/mi14p.jpg",
|
||||
specs: "Black / 12G + 256TB",
|
||||
description: "Xiaomi 14 Pro,超越旗舰,超乎所想。",
|
||||
link: "https://www.mi.com/xiaomi-14-pro",
|
||||
},
|
||||
],
|
||||
};
|
||||
98
src/data/diary.ts
Normal file
98
src/data/diary.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
// 日记数据配置
|
||||
// 用于管理日记页面的数据
|
||||
|
||||
export interface DiaryItem {
|
||||
id: number;
|
||||
content: string;
|
||||
date: string;
|
||||
images?: string[];
|
||||
location?: string;
|
||||
mood?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// 示例日记数据
|
||||
const diaryData: DiaryItem[] = [
|
||||
// {
|
||||
// id: 1,
|
||||
// content:
|
||||
// "The falling speed of cherry blossoms is five centimeters per second!",
|
||||
// date: "2025-01-15T10:30:00Z",
|
||||
// images: ["/images/diary/sakura.jpg", "/images/diary/1.jpg"],
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// content:
|
||||
// "The falling speed of cherry blossoms is five centimeters per second!",
|
||||
// date: "2025-01-15T10:30:00Z",
|
||||
// images: ["/images/diary/sakura.jpg", "/images/diary/1.jpg"],
|
||||
// },
|
||||
];
|
||||
|
||||
// 获取日记统计数据
|
||||
export const getDiaryStats = () => {
|
||||
const total = diaryData.length;
|
||||
const hasImages = diaryData.filter(
|
||||
(item) => item.images && item.images.length > 0,
|
||||
).length;
|
||||
const hasLocation = diaryData.filter((item) => item.location).length;
|
||||
const hasMood = diaryData.filter((item) => item.mood).length;
|
||||
|
||||
return {
|
||||
total,
|
||||
hasImages,
|
||||
hasLocation,
|
||||
hasMood,
|
||||
imagePercentage: Math.round((hasImages / total) * 100),
|
||||
locationPercentage: Math.round((hasLocation / total) * 100),
|
||||
moodPercentage: Math.round((hasMood / total) * 100),
|
||||
};
|
||||
};
|
||||
|
||||
// 获取日记列表(按时间倒序)
|
||||
export const getDiaryList = (limit?: number) => {
|
||||
const sortedData = diaryData.sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
);
|
||||
|
||||
if (limit && limit > 0) {
|
||||
return sortedData.slice(0, limit);
|
||||
}
|
||||
|
||||
return sortedData;
|
||||
};
|
||||
|
||||
// 获取最新的日记
|
||||
export const getLatestDiary = () => {
|
||||
return getDiaryList(1)[0];
|
||||
};
|
||||
|
||||
// 根据ID获取日记
|
||||
export const getDiaryById = (id: number) => {
|
||||
return diaryData.find((item) => item.id === id);
|
||||
};
|
||||
|
||||
// 获取包含图片的日记
|
||||
export const getDiaryWithImages = () => {
|
||||
return diaryData.filter((item) => item.images && item.images.length > 0);
|
||||
};
|
||||
|
||||
// 根据标签筛选日记
|
||||
export const getDiaryByTag = (tag: string) => {
|
||||
return diaryData
|
||||
.filter((item) => item.tags?.includes(tag))
|
||||
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
};
|
||||
|
||||
// 获取所有标签
|
||||
export const getAllTags = () => {
|
||||
const tags = new Set<string>();
|
||||
diaryData.forEach((item) => {
|
||||
if (item.tags) {
|
||||
item.tags.forEach((tag) => tags.add(tag));
|
||||
}
|
||||
});
|
||||
return Array.from(tags).sort();
|
||||
};
|
||||
|
||||
export default diaryData;
|
||||
95
src/data/friends.ts
Normal file
95
src/data/friends.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// 友情链接数据配置
|
||||
// 用于管理友情链接页面的数据
|
||||
|
||||
export interface FriendItem {
|
||||
id: number;
|
||||
title: string;
|
||||
imgurl: string;
|
||||
desc: string;
|
||||
siteurl: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
// 友情链接数据
|
||||
export const friendsData: FriendItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Astro",
|
||||
imgurl: "https://avatars.githubusercontent.com/u/44914786?v=4&s=640",
|
||||
desc: "The web framework for content-driven websites",
|
||||
siteurl: "https://github.com/withastro/astro",
|
||||
tags: ["Framework"],
|
||||
},
|
||||
// {
|
||||
// id: 2,
|
||||
// title: "Mizuki Docs",
|
||||
// imgurl:
|
||||
// "http://q.qlogo.cn/headimg_dl?dst_uin=3231515355&spec=640&img_type=jpg",
|
||||
// desc: "Mizuki User Manual",
|
||||
// siteurl: "https://docs.mizuki.mysqil.com",
|
||||
// tags: ["Docs"],
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// title: "Vercel",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/14985020?v=4&s=640",
|
||||
// desc: "Develop. Preview. Ship.",
|
||||
// siteurl: "https://vercel.com",
|
||||
// tags: ["Hosting", "Cloud"],
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// title: "Tailwind CSS",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/67109815?v=4&s=640",
|
||||
// desc: "A utility-first CSS framework for rapidly building custom designs",
|
||||
// siteurl: "https://tailwindcss.com",
|
||||
// tags: ["CSS", "Framework"],
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// title: "TypeScript",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/6154722?v=4&s=640",
|
||||
// desc: "TypeScript is JavaScript with syntax for types",
|
||||
// siteurl: "https://www.typescriptlang.org",
|
||||
// tags: ["Language", "JavaScript"],
|
||||
// },
|
||||
// {
|
||||
// id: 6,
|
||||
// title: "React",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/6412038?v=4&s=640",
|
||||
// desc: "A JavaScript library for building user interfaces",
|
||||
// siteurl: "https://reactjs.org",
|
||||
// tags: ["Framework", "JavaScript"],
|
||||
// },
|
||||
// {
|
||||
// id: 7,
|
||||
// title: "GitHub",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/9919?v=4&s=640",
|
||||
// desc: "Where the world builds software",
|
||||
// siteurl: "https://github.com",
|
||||
// tags: ["Development", "Platform"],
|
||||
// },
|
||||
// {
|
||||
// id: 8,
|
||||
// title: "MDN Web Docs",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/7565578?v=4&s=640",
|
||||
// desc: "The web's most comprehensive resource for web developers",
|
||||
// siteurl: "https://developer.mozilla.org",
|
||||
// tags: ["Docs", "Reference"],
|
||||
// },
|
||||
];
|
||||
|
||||
// 获取所有友情链接数据
|
||||
export function getFriendsList(): FriendItem[] {
|
||||
return friendsData;
|
||||
}
|
||||
|
||||
// 获取随机排序的友情链接数据
|
||||
export function getShuffledFriendsList(): FriendItem[] {
|
||||
const shuffled = [...friendsData];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
return shuffled;
|
||||
}
|
||||
82
src/data/projects.ts
Normal file
82
src/data/projects.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// Project data configuration file
|
||||
// Used to manage data for the project display page
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
category: "web" | "mobile" | "desktop" | "other";
|
||||
techStack: string[];
|
||||
status: "completed" | "in-progress" | "planned";
|
||||
liveDemo?: string;
|
||||
sourceCode?: string;
|
||||
startDate: string;
|
||||
endDate?: string;
|
||||
featured?: boolean;
|
||||
tags?: string[];
|
||||
visitUrl?: string; // 添加前往项目链接字段
|
||||
}
|
||||
|
||||
export const projectsData: Project[] = [
|
||||
// {
|
||||
// id: "mizuki-blog",
|
||||
// title: "Mizuki Blog Theme",
|
||||
// description:
|
||||
// "Modern blog theme developed based on the Astro framework, supporting multilingual, dark mode, and responsive design features.",
|
||||
// image: "",
|
||||
// category: "web",
|
||||
// techStack: ["Astro", "TypeScript", "Tailwind CSS", "Svelte"],
|
||||
// status: "completed",
|
||||
// liveDemo: "https://blog.example.com",
|
||||
// sourceCode: "https://github.com/example/mizuki", // 更改为GitHub链接
|
||||
// visitUrl: "https://blog.example.com", // 添加前往项目链接
|
||||
// startDate: "2024-01-01",
|
||||
// endDate: "2024-06-01",
|
||||
// featured: true,
|
||||
// tags: ["Blog", "Theme", "Open Source"],
|
||||
// },
|
||||
];
|
||||
|
||||
// Get project statistics
|
||||
export const getProjectStats = () => {
|
||||
const total = projectsData.length;
|
||||
const completed = projectsData.filter((p) => p.status === "completed").length;
|
||||
const inProgress = projectsData.filter(
|
||||
(p) => p.status === "in-progress",
|
||||
).length;
|
||||
const planned = projectsData.filter((p) => p.status === "planned").length;
|
||||
|
||||
return {
|
||||
total,
|
||||
byStatus: {
|
||||
completed,
|
||||
inProgress,
|
||||
planned,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// Get projects by category
|
||||
export const getProjectsByCategory = (category?: string) => {
|
||||
if (!category || category === "all") {
|
||||
return projectsData;
|
||||
}
|
||||
return projectsData.filter((p) => p.category === category);
|
||||
};
|
||||
|
||||
// Get featured projects
|
||||
export const getFeaturedProjects = () => {
|
||||
return projectsData.filter((p) => p.featured);
|
||||
};
|
||||
|
||||
// Get all tech stacks
|
||||
export const getAllTechStack = () => {
|
||||
const techSet = new Set<string>();
|
||||
projectsData.forEach((project) => {
|
||||
project.techStack.forEach((tech) => {
|
||||
techSet.add(tech);
|
||||
});
|
||||
});
|
||||
return Array.from(techSet).sort();
|
||||
};
|
||||
310
src/data/skills.ts
Normal file
310
src/data/skills.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
// Skill data configuration file
|
||||
// Used to manage data for the skill display page
|
||||
|
||||
export interface Skill {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string; // Iconify icon name
|
||||
category: "frontend" | "backend" | "database" | "tools" | "other";
|
||||
level: "beginner" | "intermediate" | "advanced" | "expert";
|
||||
experience: {
|
||||
years: number;
|
||||
months: number;
|
||||
};
|
||||
projects?: string[]; // Related project IDs
|
||||
certifications?: string[];
|
||||
color?: string; // Skill card theme color
|
||||
}
|
||||
|
||||
export const skillsData: Skill[] = [
|
||||
// Frontend Skills
|
||||
{
|
||||
id: "javascript",
|
||||
name: "JavaScript",
|
||||
description:
|
||||
"Modern JavaScript development, including ES6+ syntax, asynchronous programming, and modular development.",
|
||||
icon: "logos:javascript",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 6 },
|
||||
projects: ["mizuki-blog", "portfolio-website", "data-visualization-tool"],
|
||||
color: "#F7DF1E",
|
||||
},
|
||||
{
|
||||
id: "typescript",
|
||||
name: "TypeScript",
|
||||
description:
|
||||
"A type-safe superset of JavaScript that enhances code quality and development efficiency.",
|
||||
icon: "logos:typescript-icon",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 8 },
|
||||
projects: ["mizuki-blog", "portfolio-website", "task-manager-app"],
|
||||
color: "#3178C6",
|
||||
},
|
||||
{
|
||||
id: "react",
|
||||
name: "React",
|
||||
description:
|
||||
"A JavaScript library for building user interfaces, including Hooks, Context, and state management.",
|
||||
icon: "logos:react",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 10 },
|
||||
projects: ["portfolio-website", "task-manager-app"],
|
||||
color: "#61DAFB",
|
||||
},
|
||||
{
|
||||
id: "vue",
|
||||
name: "Vue.js",
|
||||
description:
|
||||
"A progressive JavaScript framework that is easy to learn and use, suitable for rapid development.",
|
||||
icon: "logos:vue",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 8 },
|
||||
projects: ["data-visualization-tool"],
|
||||
color: "#4FC08D",
|
||||
},
|
||||
{
|
||||
id: "nextjs",
|
||||
name: "Next.js",
|
||||
description:
|
||||
"A production-level React framework supporting SSR, SSG, and full-stack development.",
|
||||
icon: "logos:nextjs-icon",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 4 },
|
||||
projects: ["e-commerce-frontend", "blog-platform"],
|
||||
color: "#616161", // 更改为深灰色,避免纯黑色
|
||||
},
|
||||
{
|
||||
id: "astro",
|
||||
name: "Astro",
|
||||
description:
|
||||
"A modern static site generator supporting multi-framework integration and excellent performance.",
|
||||
icon: "logos:astro-icon",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 2 },
|
||||
projects: ["mizuki-blog"],
|
||||
color: "#FF5D01",
|
||||
},
|
||||
{
|
||||
id: "vite",
|
||||
name: "Vite",
|
||||
description:
|
||||
"Next-generation frontend build tool with fast cold starts and hot updates.",
|
||||
icon: "logos:vitejs",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 2 },
|
||||
projects: ["vue-project", "react-project"],
|
||||
color: "#646CFF",
|
||||
},
|
||||
|
||||
// Backend Skills
|
||||
{
|
||||
id: "python",
|
||||
name: "Python",
|
||||
description:
|
||||
"A general-purpose programming language suitable for web development, data analysis, machine learning, and more.",
|
||||
icon: "logos:python",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 10 },
|
||||
color: "#3776AB",
|
||||
},
|
||||
{
|
||||
id: "cpp",
|
||||
name: "C++",
|
||||
description:
|
||||
"A high-performance systems programming language widely used in game development, system software, and embedded development.",
|
||||
icon: "logos:c-plusplus",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 4 },
|
||||
projects: ["game-engine", "system-optimization"],
|
||||
color: "#00599C",
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "C",
|
||||
description:
|
||||
"A low-level systems programming language, the foundation for operating systems and embedded systems development.",
|
||||
icon: "logos:c",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 3 },
|
||||
projects: ["embedded-system", "kernel-module"],
|
||||
color: "#A8B9CC",
|
||||
},
|
||||
{
|
||||
id: "django",
|
||||
name: "Django",
|
||||
description:
|
||||
"A high-level Python web framework with rapid development and clean, pragmatic design.",
|
||||
icon: "logos:django-icon",
|
||||
category: "backend",
|
||||
level: "beginner",
|
||||
experience: { years: 0, months: 6 },
|
||||
projects: ["blog-backend"],
|
||||
color: "#092E20",
|
||||
},
|
||||
|
||||
// Database Skills
|
||||
{
|
||||
id: "mysql",
|
||||
name: "MySQL",
|
||||
description:
|
||||
"The world's most popular open-source relational database management system, widely used in web applications.",
|
||||
icon: "logos:mysql-icon",
|
||||
category: "database",
|
||||
level: "advanced",
|
||||
experience: { years: 2, months: 6 },
|
||||
projects: ["e-commerce-platform", "blog-system"],
|
||||
color: "#4479A1",
|
||||
},
|
||||
|
||||
// Tools
|
||||
{
|
||||
id: "git",
|
||||
name: "Git",
|
||||
description:
|
||||
"A distributed version control system, an essential tool for code management and team collaboration.",
|
||||
icon: "logos:git-icon",
|
||||
category: "tools",
|
||||
level: "advanced",
|
||||
experience: { years: 3, months: 0 },
|
||||
color: "#F05032",
|
||||
},
|
||||
{
|
||||
id: "vscode",
|
||||
name: "VS Code",
|
||||
description:
|
||||
"A lightweight but powerful code editor with a rich plugin ecosystem.",
|
||||
icon: "logos:visual-studio-code",
|
||||
category: "tools",
|
||||
level: "expert",
|
||||
experience: { years: 3, months: 6 },
|
||||
color: "#007ACC",
|
||||
},
|
||||
{
|
||||
id: "pycharm",
|
||||
name: "PyCharm",
|
||||
description:
|
||||
"A professional Python IDE by JetBrains providing intelligent code analysis and debugging features.",
|
||||
icon: "logos:pycharm",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 4 },
|
||||
projects: ["python-web-app", "data-analysis"],
|
||||
color: "#21D789",
|
||||
},
|
||||
{
|
||||
id: "docker",
|
||||
name: "Docker",
|
||||
description:
|
||||
"A containerization platform that simplifies application deployment and environment management.",
|
||||
icon: "logos:docker-icon",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 0 },
|
||||
color: "#2496ED",
|
||||
},
|
||||
{
|
||||
id: "nginx",
|
||||
name: "Nginx",
|
||||
description: "A high-performance web server and reverse proxy server.",
|
||||
icon: "logos:nginx",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 2 },
|
||||
projects: ["web-server-config", "load-balancer"],
|
||||
color: "#009639",
|
||||
},
|
||||
{
|
||||
id: "linux",
|
||||
name: "Linux",
|
||||
description:
|
||||
"An open-source operating system, the preferred choice for server deployment and development environments.",
|
||||
icon: "logos:linux-tux",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 0 },
|
||||
projects: ["server-management", "shell-scripting"],
|
||||
color: "#FCC624",
|
||||
},
|
||||
{
|
||||
id: "photoshop",
|
||||
name: "Photoshop",
|
||||
description: "Professional image editing and design software.",
|
||||
icon: "logos:adobe-photoshop",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 6 },
|
||||
projects: ["ui-design", "image-processing"],
|
||||
color: "#31A8FF",
|
||||
},
|
||||
|
||||
// Other Skills
|
||||
{
|
||||
id: "photography",
|
||||
name: "photography",
|
||||
description:
|
||||
"二次元风光摄影.",
|
||||
icon: "material-symbols:photo-camera-outline-rounded",
|
||||
category: "other",
|
||||
level: "advanced",
|
||||
experience: { years: 2, months: 4 },
|
||||
projects: ["modern-api"],
|
||||
color: "#E10098",
|
||||
},
|
||||
];
|
||||
|
||||
// Get skill statistics
|
||||
export const getSkillStats = () => {
|
||||
const total = skillsData.length;
|
||||
const byLevel = {
|
||||
beginner: skillsData.filter((s) => s.level === "beginner").length,
|
||||
intermediate: skillsData.filter((s) => s.level === "intermediate").length,
|
||||
advanced: skillsData.filter((s) => s.level === "advanced").length,
|
||||
expert: skillsData.filter((s) => s.level === "expert").length,
|
||||
};
|
||||
const byCategory = {
|
||||
frontend: skillsData.filter((s) => s.category === "frontend").length,
|
||||
backend: skillsData.filter((s) => s.category === "backend").length,
|
||||
database: skillsData.filter((s) => s.category === "database").length,
|
||||
tools: skillsData.filter((s) => s.category === "tools").length,
|
||||
other: skillsData.filter((s) => s.category === "other").length,
|
||||
};
|
||||
|
||||
return { total, byLevel, byCategory };
|
||||
};
|
||||
|
||||
// Get skills by category
|
||||
export const getSkillsByCategory = (category?: string) => {
|
||||
if (!category || category === "all") {
|
||||
return skillsData;
|
||||
}
|
||||
return skillsData.filter((s) => s.category === category);
|
||||
};
|
||||
|
||||
// Get advanced skills
|
||||
export const getAdvancedSkills = () => {
|
||||
return skillsData.filter(
|
||||
(s) => s.level === "advanced" || s.level === "expert",
|
||||
);
|
||||
};
|
||||
|
||||
// Calculate total years of experience
|
||||
export const getTotalExperience = () => {
|
||||
const totalMonths = skillsData.reduce((total, skill) => {
|
||||
return total + skill.experience.years * 12 + skill.experience.months;
|
||||
}, 0);
|
||||
return {
|
||||
years: Math.floor(totalMonths / 12),
|
||||
months: totalMonths % 12,
|
||||
};
|
||||
};
|
||||
110
src/data/timeline.ts
Normal file
110
src/data/timeline.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
// Timeline data configuration file
|
||||
// Used to manage data for the timeline page
|
||||
|
||||
export interface TimelineItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: "education" | "work" | "project" | "achievement";
|
||||
startDate: string;
|
||||
endDate?: string; // If empty, it means current
|
||||
location?: string;
|
||||
organization?: string;
|
||||
position?: string;
|
||||
skills?: string[];
|
||||
achievements?: string[];
|
||||
links?: {
|
||||
name: string;
|
||||
url: string;
|
||||
type: "website" | "certificate" | "project" | "other";
|
||||
}[];
|
||||
icon?: string; // Iconify icon name
|
||||
color?: string;
|
||||
featured?: boolean;
|
||||
}
|
||||
|
||||
export const timelineData: TimelineItem[] = [
|
||||
{
|
||||
id: "current-study",
|
||||
title: "Studying Chemical Engineering",
|
||||
description:
|
||||
"Currently studying Chemical Engineering.",
|
||||
type: "education",
|
||||
startDate: "2022-09-01",
|
||||
location: "Dalian",
|
||||
organization: "Dalian University of Technology",
|
||||
// skills: ["Java", "Python", "JavaScript", "HTML/CSS", "MySQL"],
|
||||
// achievements: [
|
||||
// "Current GPA: 3.6/4.0",
|
||||
// "Completed data structures and algorithms course project",
|
||||
// "Participated in multiple course project developments",
|
||||
// ],
|
||||
icon: "material-symbols:school",
|
||||
color: "#059669",
|
||||
featured: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Get timeline statistics
|
||||
export const getTimelineStats = () => {
|
||||
const total = timelineData.length;
|
||||
const byType = {
|
||||
education: timelineData.filter((item) => item.type === "education").length,
|
||||
work: timelineData.filter((item) => item.type === "work").length,
|
||||
project: timelineData.filter((item) => item.type === "project").length,
|
||||
achievement: timelineData.filter((item) => item.type === "achievement")
|
||||
.length,
|
||||
};
|
||||
|
||||
return { total, byType };
|
||||
};
|
||||
|
||||
// Get timeline items by type
|
||||
export const getTimelineByType = (type?: string) => {
|
||||
if (!type || type === "all") {
|
||||
return timelineData.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
}
|
||||
return timelineData
|
||||
.filter((item) => item.type === type)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
};
|
||||
|
||||
// Get featured timeline items
|
||||
export const getFeaturedTimeline = () => {
|
||||
return timelineData
|
||||
.filter((item) => item.featured)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
};
|
||||
|
||||
// Get current ongoing items
|
||||
export const getCurrentItems = () => {
|
||||
return timelineData.filter((item) => !item.endDate);
|
||||
};
|
||||
|
||||
// Calculate total work experience
|
||||
export const getTotalWorkExperience = () => {
|
||||
const workItems = timelineData.filter((item) => item.type === "work");
|
||||
let totalMonths = 0;
|
||||
|
||||
workItems.forEach((item) => {
|
||||
const startDate = new Date(item.startDate);
|
||||
const endDate = item.endDate ? new Date(item.endDate) : new Date();
|
||||
const diffTime = Math.abs(endDate.getTime() - startDate.getTime());
|
||||
const diffMonths = Math.ceil(diffTime / (1000 * 60 * 60 * 24 * 30));
|
||||
totalMonths += diffMonths;
|
||||
});
|
||||
|
||||
return {
|
||||
years: Math.floor(totalMonths / 12),
|
||||
months: totalMonths % 12,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user