feat: 初始化项目

- 初始化 Vue 3 + Vite 项目结构
- 添加核心组件 (Hero, About, Projects, Friends 等)
- 配置页面路由和基础样式
- 添加服务端 API 接口 (邮件发送, 好友请求)
- 添加项目配置文件 (vercel.json, vite.config.js)
This commit is contained in:
2025-12-06 23:39:15 +08:00
commit f80e5c0b5a
33 changed files with 1964 additions and 0 deletions

65
src/views/About.vue Normal file
View File

@@ -0,0 +1,65 @@
<template>
<main class="page">
<HeroSection :profile="profile" />
<SkillsSection :skills="skills" />
<GithubSection :github="github" />
</main>
</template>
<script setup>
import { onMounted, reactive } from "vue";
import HeroSection from "../components/HeroSection.vue";
import SkillsSection from "../components/SkillsSection.vue";
import GithubSection from "../components/GithubSection.vue";
import siteConfig from "../config/siteConfig";
const profile = siteConfig.profile;
const siteMeta = siteConfig.siteMeta;
const skills = siteConfig.skills;
const github = reactive({
...siteConfig.github,
heatmapUrl: `https://ghchart.rshah.org/${siteConfig.github.username}`,
});
// 修改此处:使用 VITE_ 前缀
const githubToken = import.meta.env.VITE_GITHUB_TOKEN ?? "";
console.log(githubToken);
onMounted(() => {
document.title = siteMeta.title;
const link = document.querySelector("link[rel~='icon']") || document.createElement("link");
link.rel = "icon";
link.href = siteMeta.icon;
document.head.appendChild(link);
fetchGithubMeta();
});
async function fetchGithubMeta() {
try {
const headers = githubToken ? { Authorization: `Bearer ${githubToken}` } : {};
const resp = await fetch(`https://api.github.com/users/${github.username}/repos?per_page=100&sort=updated`, {
headers,
});
const repos = await resp.json();
if (!Array.isArray(repos)) return;
const counts = repos.reduce((acc, repo) => {
if (!repo.language) return acc;
acc[repo.language] = (acc[repo.language] || 0) + 1;
return acc;
}, {});
const total = Object.values(counts).reduce((a, b) => a + b, 0) || 1;
const parsed = Object.entries(counts)
.map(([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 4);
github.languages = parsed.map((item) => ({
name: item.name,
percent: Math.round((item.count / total) * 100),
}));
} catch (error) {
console.error("Failed to fetch GitHub metadata:", error);
}
}
</script>

12
src/views/Friends.vue Normal file
View File

@@ -0,0 +1,12 @@
<template>
<main class="page">
<FriendsSection :friends="friends" />
</main>
</template>
<script setup>
import FriendsSection from "../components/FriendsSection.vue";
import siteConfig from "../config/siteConfig";
const friends = siteConfig.friends;
</script>

36
src/views/Home.vue Normal file
View File

@@ -0,0 +1,36 @@
<template>
<main class="page">
<HeroSection :profile="profile" />
<SocialLinks :links="socialLinks" />
<AboutSection :items="about" />
</main>
</template>
<script setup>
import { onMounted, reactive } from "vue";
import HeroSection from "../components/HeroSection.vue";
import SocialLinks from "../components/SocialLinks.vue";
import AboutSection from "../components/AboutSection.vue";
import siteConfig from "../config/siteConfig";
const profile = siteConfig.profile;
const socialLinks = siteConfig.socialLinks;
const siteMeta = siteConfig.siteMeta;
const about = siteConfig.about;
onMounted(() => {
document.title = siteMeta.title;
const link = document.querySelector("link[rel~='icon']") || document.createElement("link");
link.rel = "icon";
link.href = siteMeta.icon;
document.head.appendChild(link);
});
</script>
<style scoped>
.page {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
</style>

12
src/views/Projects.vue Normal file
View File

@@ -0,0 +1,12 @@
<template>
<main class="page">
<ProjectsSection :projects="projects" />
</main>
</template>
<script setup>
import ProjectsSection from "../components/ProjectsSection.vue";
import siteConfig from "../config/siteConfig";
const projects = siteConfig.projects;
</script>

12
src/views/Sites.vue Normal file
View File

@@ -0,0 +1,12 @@
<template>
<main class="page">
<SitesSection :sites="sites" />
</main>
</template>
<script setup>
import SitesSection from "../components/SitesSection.vue";
import siteConfig from "../config/siteConfig";
const sites = siteConfig.sites;
</script>