feat(about): 优化页面样式与功能增强

- 新增渐变色彩变量,用于文本和标签的视觉效果提升
- 增强导航项和操作按钮的悬停动效与交互反馈
- 重构技术标签和兴趣模块,支持动态渐变色彩展示
- 优化移动端布局,包括底部导航、技术标签滚动及兴趣模块适配
- 引入Artalk主题切换支持,提升评论区体验一致性
- 博客内容获取逻辑升级:优先使用RSS源并添加缓存机制
- GitHub仓库数据过滤非原创项目,并增加fork数量显示
- 技术标签3D球体增大半径以改善标签分布与可读性
- 页面结构调整,明确划分开源项目与博客文章区域
- 修复多处移动端显示异常问题,提高整体响应式表现
This commit is contained in:
hehh
2025-11-23 16:37:12 +08:00
parent 25c2321669
commit 6bb4ce7b5e
3 changed files with 660 additions and 98 deletions

View File

@@ -95,6 +95,11 @@ class ThemeManager {
if(next === 'night') this.root.setAttribute('data-theme', 'night');
else this.root.removeAttribute('data-theme');
localStorage.setItem('theme', next);
// 更新Artalk主题
if (typeof Artalk !== 'undefined') {
Artalk.reload();
}
});
}
}
@@ -144,11 +149,19 @@ class DataManager {
// Parallel Fetch
const [uRes, rRes] = await Promise.allSettled([
fetch(`https://api.github.com/users/${user}`),
fetch(`https://api.github.com/users/${user}/repos?sort=stars&per_page=6`)
fetch(`https://api.github.com/users/${user}/repos?sort=stars&per_page=100`)
]);
const userData = uRes.status === 'fulfilled' ? await uRes.value.json() : this.defaults.user;
const repoData = rRes.status === 'fulfilled' ? await rRes.value.json() : this.defaults.repos;
let repoData = rRes.status === 'fulfilled' ? await rRes.value.json() : this.defaults.repos;
// 过滤掉fork项目并按星数排序
if (Array.isArray(repoData)) {
repoData = repoData
.filter(repo => !repo.fork && (repo.stargazers_count > 0 || repo.forks_count > 0))
.sort((a, b) => (b.stargazers_count || 0) - (a.stargazers_count || 0))
.slice(0, 12); // 只取前12个
}
// Cache & Render
localStorage.setItem(cacheKey, JSON.stringify({
@@ -177,6 +190,7 @@ class DataManager {
list.slice(0, 5).forEach(repo => {
// Fix: API field compatibility
const stars = repo.stargazers_count !== undefined ? repo.stargazers_count : (repo.stars || 0);
const forks = repo.forks_count !== undefined ? repo.forks_count : (repo.forks || 0);
const desc = repo.description || repo.desc || 'No description.';
const url = repo.html_url || repo.url || '#';
@@ -184,7 +198,10 @@ class DataManager {
<div class="repo-card" onclick="window.open('${url}')">
<div class="repo-head">
<span>${repo.name}</span>
<span style="color:#f1c40f"><i class="ri-star-fill"></i> ${stars}</span>
<span>
<i class="ri-star-fill"></i> ${stars}
<i class="ri-git-branch-fill"></i> ${forks}
</span>
</div>
<div class="repo-desc">${desc}</div>
</div>`;
@@ -192,11 +209,70 @@ class DataManager {
$('#projects-container').html(html);
}
fetchBlog() {
fetch('data/articles.json')
.then(r => r.json())
.then(data => this.renderBlog(data))
.catch(() => this.renderBlog(this.defaults.posts));
// 从RSS获取博客文章
async fetchBlog() {
const rssUrl = window.SiteConfig?.blog?.rssUrl || 'https://blog.hehouhui.cn/api/rss';
const cacheKey = 'blog_data_v2';
const cached = JSON.parse(localStorage.getItem(cacheKey));
const now = Date.now();
// Check Cache (1 hour)
if(cached && (now - cached.time < 3600000)) {
this.renderBlog(cached.posts);
return;
}
try {
// 尝试从RSS获取
const response = await fetch(rssUrl);
if (!response.ok) throw new Error('RSS fetch failed');
const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "application/xml");
if (xmlDoc.documentElement.nodeName === "parsererror") {
throw new Error('RSS XML parse error');
}
// 解析RSS项目
const items = xmlDoc.getElementsByTagName("item");
const posts = [];
for (let i = 0; i < Math.min(items.length, 5); i++) {
const item = items[i];
const title = item.querySelector("title")?.textContent || "无标题";
const link = item.querySelector("link")?.textContent || "#";
const pubDate = item.querySelector("pubDate")?.textContent || "";
const category = item.querySelector("category")?.textContent || "Tech";
posts.push({
title,
link,
date: pubDate,
cat: category
});
}
// Cache & Render
localStorage.setItem(cacheKey, JSON.stringify({
posts,
time: now
}));
this.renderBlog(posts);
} catch (e) {
console.warn("RSS API Fail", e);
// 降级到本地JSON文件
try {
const response = await fetch('data/articles.json');
const data = await response.json();
this.renderBlog(data);
} catch (e2) {
console.warn("Local JSON Fail", e2);
this.renderBlog(this.defaults.posts);
}
}
}
renderBlog(list) {
@@ -272,9 +348,14 @@ class UIManager {
if(isMobile) {
// Mobile: Horizontal Scroll Snap (Compact 3 rows)
container.classList.add('mobile-scroll');
techStack.forEach(item => {
// 创建多个标签副本以实现无缝滚动效果
const extendedTechStack = [...techStack, ...techStack, ...techStack];
extendedTechStack.forEach((item, index) => {
const el = document.createElement('span');
el.className = 'tech-tag-mobile';
// 添加不同颜色的渐变类
const colorClass = `tag-color-${(index % 5) + 1}`;
el.classList.add(colorClass);
el.innerText = item.name;
container.appendChild(el);
});
@@ -283,15 +364,22 @@ class UIManager {
container.classList.remove('mobile-scroll');
const tags = [];
techStack.forEach(item => {
techStack.forEach((item, index) => {
const el = document.createElement('a');
el.className = 'tech-tag-3d';
// 添加不同颜色的渐变类
const colorClass = `tag-color-${(index % 5) + 1}`;
el.classList.add(colorClass);
el.innerText = item.name;
// 移除背景和边框样式
el.style.background = 'none';
el.style.border = 'none';
container.appendChild(el);
tags.push({ el, x:0, y:0, z:0 });
});
let radius = 120; // Smaller radius to fit
// 增大球体半径以避免标签重叠
let radius = 220;
const dtr = Math.PI/180;
let lasta=1, lastb=1;
let active=false, mouseX=0, mouseY=0;