style(css): 优化 about.css 样式结构与可读性

- 将所有 CSS 规则展开为多行格式以提高可读性
- 统一缩进和空格风格,增强代码一致性
- 重新组织媒体查询和组件样式块顺序
- 补充遗漏的闭合大括号和分号
- 优化注释排版,使其更清晰易懂
- 确保渐变文本样式在不同浏览器中的兼容性
- 调整移动端技术标签显示方式,隐藏桌面端3D容器
- 完善夜间模式下的文本阴影效果
- 增强悬停状态过渡动画流畅度
- 修复部分元素在小屏幕上的布局问题
This commit is contained in:
hehh
2025-11-23 18:13:58 +08:00
parent 958bf037c4
commit c5ca56356d
4 changed files with 982 additions and 450 deletions

View File

@@ -10,7 +10,7 @@
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/about.css"> <link rel="stylesheet" href="css/about.css">
<!-- Artalk 评论样式 --> <!-- Artalk 评论样式 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/artalk/2.9.1/Artalk.css"> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.css">
<link rel="icon" href="favicon.ico"> <link rel="icon" href="favicon.ico">
</head> </head>
<body> <body>
@@ -263,7 +263,7 @@
<!-- 脚本BootCDN jQuery --> <!-- 脚本BootCDN jQuery -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="js/config.js"></script> <script src="js/config.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/artalk/2.9.1/Artalk.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/artalk/2.9.1/Artalk.js"></script>
<script src="js/about.js"></script> <script src="js/about.js"></script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@@ -117,20 +117,6 @@ class ThemeManager {
=========================== */ =========================== */
class DataManager { class DataManager {
constructor() { constructor() {
// Fallback Data if APIs fail
this.defaults = {
repos: [
{name: "yunxiao-LLM-reviewer", desc: "AI Code Reviewer based on LLM", stars: 9, url: "#"},
{name: "hexo-theme-stellar", desc: "Comprehensive Hexo theme", stars: 5, url: "#"},
{name: "Universal-IoT-Java", desc: "IoT Platform Demo", stars: 2, url: "#"}
],
posts: [
{title: "Vector Database Guide", date: "2025-01-02", cat: "Tech", url: "#"},
{title: "Spring Boot 3.0 Features", date: "2024-12-30", cat: "Java", url: "#"},
{title: "Microservices Patterns", date: "2024-12-28", cat: "Arch", url: "#"}
],
user: { repos: 165, followers: 6, created: "2018-05-14" }
};
this.init(); this.init();
} }
@@ -142,7 +128,7 @@ class DataManager {
// 优先缓存 -> API -> 默认值 // 优先缓存 -> API -> 默认值
async fetchGithub() { async fetchGithub() {
const user = (window.SiteConfig?.github?.username) || 'listener-He'; const user = (window.SiteConfig?.github?.username) || 'listener-He';
const cacheKey = (window.SiteConfig?.github?.cache?.cacheKey) || 'gh_data_v2'; const cacheKey = (window.SiteConfig?.cacheKeys?.github?.key) || 'gh_data_v2';
const cached = JSON.parse(localStorage.getItem(cacheKey)); const cached = JSON.parse(localStorage.getItem(cacheKey));
const now = Date.now(); const now = Date.now();
@@ -160,8 +146,8 @@ class DataManager {
fetch(`https://api.github.com/users/${user}/repos?sort=stars&per_page=100`) 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 userData = uRes.status === 'fulfilled' ? await uRes.value.json() : (window.SiteConfig?.defaults?.user);
let repoData = rRes.status === 'fulfilled' ? await rRes.value.json() : this.defaults.repos; let repoData = rRes.status === 'fulfilled' ? await rRes.value.json() : (window.SiteConfig?.defaults?.repos);
// 过滤掉fork项目并按星数排序 // 过滤掉fork项目并按星数排序
if (Array.isArray(repoData)) { if (Array.isArray(repoData)) {
@@ -180,38 +166,39 @@ class DataManager {
} catch (e) { } catch (e) {
console.warn("GH API Fail", e); console.warn("GH API Fail", e);
this.renderUser(this.defaults.user); this.renderUser(window.SiteConfig?.defaults?.user);
this.renderRepos(this.defaults.repos); this.renderRepos(window.SiteConfig?.defaults?.repos);
} }
} }
renderUser(data) { renderUser(data) {
const years = new Date().getFullYear() - new Date(data.created_at || this.defaults.user.created).getFullYear(); const years = new Date().getFullYear() - new Date(data.created_at || (window.SiteConfig?.defaults?.user?.created)).getFullYear();
$('#coding-years').text(years + "+"); $('#coding-years').text(years + "+");
$('#github-repos').text(data.public_repos || this.defaults.user.repos); $('#github-repos').text(data.public_repos || (window.SiteConfig?.defaults?.user?.repos));
$('#github-followers').text(data.followers || this.defaults.user.followers); $('#github-followers').text(data.followers || (window.SiteConfig?.defaults?.user?.followers));
} }
renderRepos(list) { renderRepos(list) {
if(!Array.isArray(list)) list = this.defaults.repos; if(!Array.isArray(list)) list = window.SiteConfig?.defaults?.repos;
let html = ''; let html = '';
list.slice(0, 5).forEach(repo => { list.slice(0, 12).forEach(repo => {
// Fix: API field compatibility // Fix: API field compatibility
const stars = repo.stargazers_count !== undefined ? repo.stargazers_count : (repo.stars || 0); 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 forks = repo.forks_count !== undefined ? repo.forks_count : (repo.forks || 0);
const desc = repo.description || repo.desc || 'No description.'; const desc = repo.description || repo.desc || 'No description.';
const url = repo.html_url || repo.url || '#'; const url = repo.html_url || repo.url || '#';
const dShort = (desc || '').length > 120 ? (desc.slice(0, 117) + '...') : desc;
html += ` html += `
<div class="repo-card" onclick="window.open('${url}')"> <div class="repo-card" onclick="window.open('${url}')">
<div class="repo-head"> <div class="repo-head">
<span>${repo.name}</span> <span class="gradient-text">${repo.name}</span>
<span> <span>
<i class="ri-star-fill"></i> ${stars} <i class="ri-star-fill"></i> ${stars}
<i class="ri-git-branch-fill"></i> ${forks} <i class="ri-git-branch-fill"></i> ${forks}
</span> </span>
</div> </div>
<div class="repo-desc">${desc}</div> <div class="repo-desc">${dShort}</div>
</div>`; </div>`;
}); });
$('#projects-container').html(html); $('#projects-container').html(html);
@@ -220,7 +207,7 @@ class DataManager {
// 从RSS获取博客文章 // 从RSS获取博客文章
async fetchBlog() { async fetchBlog() {
const rssUrl = window.SiteConfig?.blog?.rssUrl || 'https://blog.hehouhui.cn/api/rss'; const rssUrl = window.SiteConfig?.blog?.rssUrl || 'https://blog.hehouhui.cn/api/rss';
const cacheKey = window.SiteConfig?.blog?.cache?.key || 'blog_data_v2'; const cacheKey = (window.SiteConfig?.cacheKeys?.blog?.key) || 'blog_data_v2';
const cached = JSON.parse(localStorage.getItem(cacheKey)); const cached = JSON.parse(localStorage.getItem(cacheKey));
const now = Date.now(); const now = Date.now();
@@ -279,7 +266,7 @@ class DataManager {
this.renderBlog(data); this.renderBlog(data);
} catch (e2) { } catch (e2) {
console.warn("Local JSON Fail", e2); console.warn("Local JSON Fail", e2);
this.renderBlog(this.defaults.posts); this.renderBlog(window.SiteConfig?.defaults?.posts);
} }
} }
} }
@@ -371,10 +358,7 @@ class UIManager {
const container = document.getElementById('tech-container'); const container = document.getElementById('tech-container');
if(!container) return; if(!container) return;
const techStackRaw = window.SiteConfig?.techStack || [ const techStackRaw = window.SiteConfig?.techStack || [];
{name:'Java'},{name:'Spring'},{name:'Docker'},{name:'K8s'},{name:'Python'},{name:'Redis'},
{name:'React'},{name:'Vue'},{name:'MySQL'},{name:'MongoDB'},{name:'Linux'},{name:'Git'}
];
const techStack = techStackRaw.map((item, idx) => { const techStack = techStackRaw.map((item, idx) => {
const name = item.name || ''; const name = item.name || '';
const hash = Array.from(name).reduce((a,c)=>a+c.charCodeAt(0),0); const hash = Array.from(name).reduce((a,c)=>a+c.charCodeAt(0),0);
@@ -395,14 +379,10 @@ class UIManager {
extendedTechStack.forEach((item, index) => { extendedTechStack.forEach((item, index) => {
const el = document.createElement('span'); const el = document.createElement('span');
el.className = 'tech-tag-mobile'; el.className = 'tech-tag-mobile';
// 添加不同颜色的渐变类 const colorClass = `tag-color-${item.gradientId || ((index % 10) + 1)}`;
const colorClass = `tag-color-${(index % 5) + 1}`;
el.classList.add(colorClass); el.classList.add(colorClass);
el.innerText = item.name; el.innerText = item.name;
// 确保只显示文字,没有背景和边框
el.style.background = 'none';
el.style.border = 'none'; el.style.border = 'none';
el.style.color = 'transparent';
container.appendChild(el); container.appendChild(el);
}); });
} else { } else {
@@ -415,20 +395,16 @@ class UIManager {
techStack.forEach((item, index) => { techStack.forEach((item, index) => {
const el = document.createElement('a'); const el = document.createElement('a');
el.className = 'tech-tag-3d'; el.className = 'tech-tag-3d';
// 添加不同颜色的渐变类 const colorClass = `tag-color-${item.gradientId || ((index % 10) + 1)}`;
const colorClass = `tag-color-${(index % 5) + 1}`;
el.classList.add(colorClass); el.classList.add(colorClass);
el.innerText = item.name; el.innerText = item.name;
// 移除背景和边框样式,只保留文字
el.style.background = 'none';
el.style.border = 'none'; el.style.border = 'none';
el.style.color = 'transparent';
container.appendChild(el); container.appendChild(el);
tags.push({ el, x:0, y:0, z:0 }); tags.push({ el, x:0, y:0, z:0 });
}); });
// 增大球体半径避免标签重叠 // 动态半径避免容器溢出
let radius = 300; let radius = Math.max(160, Math.min(container.offsetWidth, container.offsetHeight) / 2 - 24);
const dtr = Math.PI/180; const dtr = Math.PI/180;
let lasta=1, lastb=1; let lasta=1, lastb=1;
let active=false, mouseX=0, mouseY=0; let active=false, mouseX=0, mouseY=0;
@@ -471,7 +447,8 @@ class UIManager {
let rx2=rx1*cb + rz1*sb, ry2=ry1, rz2=rx1*-sb + rz1*cb; let rx2=rx1*cb + rz1*sb, ry2=ry1, rz2=rx1*-sb + rz1*cb;
tag.x=rx2; tag.y=ry2; tag.z=rz2; tag.x=rx2; tag.y=ry2; tag.z=rz2;
let scale = (tag.z + radius)/(2*radius) + 0.5; let scale = (tag.z + radius)/(2*radius) + 0.45;
scale = Math.min(Math.max(scale, 0.7), 1.15);
let opacity = (tag.z + radius)/(2*radius) + 0.2; let opacity = (tag.z + radius)/(2*radius) + 0.2;
tag.el.style.opacity = Math.min(Math.max(opacity, 0.1), 1); tag.el.style.opacity = Math.min(Math.max(opacity, 0.1), 1);

View File

@@ -63,6 +63,12 @@ const SiteConfig = {
} }
}, },
// 通用缓存键与TTL毫秒
cacheKeys: {
github: { key: 'gh_data_v2', ttlMs: 3600000 },
blog: { key: 'blog_data_v2', ttlMs: 3600000 }
},
techStack: [ techStack: [
{ name: 'Java', category: 'core', weight: 5 }, { name: 'Java', category: 'core', weight: 5 },
{ name: 'Spring Boot', category: 'backend', weight: 5 }, { name: 'Spring Boot', category: 'backend', weight: 5 },
@@ -104,6 +110,21 @@ const SiteConfig = {
{ name: 'Postgresql', category: 'data', weight: 1 } { name: 'Postgresql', category: 'data', weight: 1 }
], ],
// 默认数据当API或RSS不可用时使用
defaults: {
repos: [
{name: "yunxiao-LLM-reviewer", desc: "AI Code Reviewer based on LLM", stars: 9, url: "#"},
{name: "hexo-theme-stellar", desc: "Comprehensive Hexo theme", stars: 5, url: "#"},
{name: "Universal-IoT-Java", desc: "IoT Platform Demo", stars: 2, url: "#"}
],
posts: [
{title: "Vector Database Guide", date: "2025-01-02", cat: "Tech", url: "#"},
{title: "Spring Boot 3.0 Features", date: "2024-12-30", cat: "Java", url: "#"},
{title: "Microservices Patterns", date: "2024-12-28", cat: "Arch", url: "#"}
],
user: { repos: 165, followers: 6, created: "2018-05-14" }
},
socialCards: { socialCards: {
rings: [130, 180, 230], rings: [130, 180, 230],
goldenAngle: 137.5, goldenAngle: 137.5,
@@ -136,13 +157,26 @@ const SiteConfig = {
}; };
if (Array.isArray(SiteConfig.techStack)) { if (Array.isArray(SiteConfig.techStack)) {
SiteConfig.techStack = SiteConfig.techStack.map((item, idx) => { const categoryGradientMap = {
core: 7,
backend: 4,
data: 9,
ops: 10,
ai: 3
};
const vividSet = [1, 4, 7, 8];
SiteConfig.techStack = SiteConfig.techStack.map((item) => {
const name = item.name || ''; const name = item.name || '';
const hash = Array.from(name).reduce((a, c) => a + c.charCodeAt(0), 0); const hash = Array.from(name).reduce((a, c) => a + c.charCodeAt(0), 0);
const gid = (item.gradientId && Number.isFinite(Number(item.gradientId))) if (item.gradientId && Number.isFinite(Number(item.gradientId))) {
? Math.max(1, Math.min(10, Number(item.gradientId))) return { ...item, gradientId: Math.max(1, Math.min(10, Number(item.gradientId))) };
: (hash % 10) + 1; }
return { ...item, gradientId: gid }; let base = categoryGradientMap[item.category] || ((hash % 10) + 1);
if (Number(item.weight) >= 5) {
base = vividSet[hash % vividSet.length];
}
return { ...item, gradientId: base };
}); });
} }