Skip to content

Commit 9a356be

Browse files
committed
feat: enhance site
1 parent de88ecc commit 9a356be

File tree

11 files changed

+284
-31
lines changed

11 files changed

+284
-31
lines changed

.claude/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"enabledPlugins": {
3+
"ui-ux-pro-max@ui-ux-pro-max-skill": true
4+
}
5+
}
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
1-
# Simple workflow for deploying static content to GitHub Pages
2-
name: Deploy static content to Pages
1+
name: Deploy to GitHub Pages
32

43
on:
5-
# Runs on pushes targeting the default branch
64
push:
7-
branches: ["main"]
8-
9-
# Allows you to run this workflow manually from the Actions tab
5+
branches: [main]
106
workflow_dispatch:
117

12-
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
138
permissions:
149
contents: read
1510
pages: write
1611
id-token: write
1712

18-
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
19-
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
2013
concurrency:
21-
group: "pages"
22-
cancel-in-progress: false
14+
group: pages
15+
cancel-in-progress: true
2316

2417
jobs:
2518
build:
@@ -40,20 +33,17 @@ jobs:
4033
- name: Build
4134
run: npm run build
4235

43-
- name: Setup Pages
44-
uses: actions/configure-pages@v5
45-
4636
- name: Upload artifact
4737
uses: actions/upload-pages-artifact@v3
4838
with:
4939
path: dist
5040

5141
deploy:
42+
needs: build
43+
runs-on: ubuntu-latest
5244
environment:
5345
name: github-pages
5446
url: ${{ steps.deployment.outputs.page_url }}
55-
runs-on: ubuntu-latest
56-
needs: build
5747
steps:
5848
- name: Deploy to GitHub Pages
5949
id: deployment

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
.vscode
55
node_modules
66
dist
7+
github-actions-deploy*

index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,61 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>RushDB - 无限进步</title>
7+
<meta name="description" content="RushDB 实验室 - 专注于数据库系统研究与开发的技术团队,致力于推动数据库技术的创新与发展。" />
8+
<meta name="keywords" content="RushDB, 数据库, Database, 技术实验室, 开源项目, MiniOB" />
9+
<meta name="author" content="RushDB Lab" />
10+
<link rel="canonical" href="https://rushdb-lab.github.io/" />
11+
12+
<!-- Open Graph / Facebook -->
13+
<meta property="og:type" content="website" />
14+
<meta property="og:url" content="https://rushdb-lab.github.io/" />
15+
<meta property="og:title" content="RushDB - 无限进步" />
16+
<meta property="og:description" content="RushDB 实验室 - 专注于数据库系统研究与开发的技术团队,致力于推动数据库技术的创新与发展。" />
17+
<meta property="og:image" content="https://rushdb-lab.github.io/RushDB.png" />
18+
<meta property="og:locale" content="zh_CN" />
19+
<meta property="og:locale:alternate" content="en_US" />
20+
<meta property="og:locale:alternate" content="ja_JP" />
21+
<meta property="og:site_name" content="RushDB Lab" />
22+
23+
<!-- Twitter Card -->
24+
<meta name="twitter:card" content="summary_large_image" />
25+
<meta name="twitter:url" content="https://rushdb-lab.github.io/" />
26+
<meta name="twitter:title" content="RushDB - 无限进步" />
27+
<meta name="twitter:description" content="RushDB 实验室 - 专注于数据库系统研究与开发的技术团队,致力于推动数据库技术的创新与发展。" />
28+
<meta name="twitter:image" content="https://rushdb-lab.github.io/RushDB.png" />
29+
30+
<!-- Preconnect for performance -->
31+
<link rel="preconnect" href="https://fonts.googleapis.com" />
32+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
33+
734
<link rel="icon" type="image/png" href="/RushDB.png" />
835
<link rel="shortcut icon" type="image/png" href="/RushDB.png" />
36+
<link rel="apple-touch-icon" href="/RushDB.png" />
937
<link
1038
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
1139
rel="stylesheet"
1240
/>
41+
42+
<!-- Structured Data (JSON-LD) -->
43+
<script type="application/ld+json">
44+
{
45+
"@context": "https://schema.org",
46+
"@type": "Organization",
47+
"name": "RushDB Lab",
48+
"alternateName": "RushDB 实验室",
49+
"url": "https://rushdb-lab.github.io/",
50+
"logo": "https://rushdb-lab.github.io/RushDB.png",
51+
"description": "RushDB 实验室 - 专注于数据库系统研究与开发的技术团队,致力于推动数据库技术的创新与发展。",
52+
"sameAs": [
53+
"https://github.com/RushDB-Lab"
54+
],
55+
"contactPoint": {
56+
"@type": "ContactPoint",
57+
"contactType": "technical support",
58+
"availableLanguage": ["Chinese", "English", "Japanese"]
59+
}
60+
}
61+
</script>
1362
</head>
1463
<body>
1564
<div id="app"></div>

public/robots.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
User-agent: *
2+
Allow: /
3+
4+
Sitemap: https://rushdb-lab.github.io/sitemap.xml

public/sitemap.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3+
<url>
4+
<loc>https://rushdb-lab.github.io/</loc>
5+
<lastmod>2026-02-02</lastmod>
6+
<changefreq>weekly</changefreq>
7+
<priority>1.0</priority>
8+
</url>
9+
</urlset>

src/App.vue

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ const languageLabel = computed(() => {
3131
3232
const scrollProgress = ref(0);
3333
const navbarBg = ref("rgba(5, 7, 18, 0.72)");
34+
const showBackToTop = ref(false);
35+
const activeSection = ref("");
3436
3537
let scrollRaf: number | null = null;
3638
let pointerRaf: number | null = null;
3739
let sectionObserver: IntersectionObserver | null = null;
40+
let navHighlightObserver: IntersectionObserver | null = null;
3841
3942
function ensureBusuanziScriptLoaded() {
4043
const scriptId = "busuanzi-script";
@@ -106,6 +109,7 @@ function switchLanguage(lang: Lang) {
106109
107110
function updateScrollEffects() {
108111
navbarBg.value = window.scrollY > 100 ? "rgba(5, 7, 18, 0.82)" : "rgba(5, 7, 18, 0.72)";
112+
showBackToTop.value = window.scrollY > 300;
109113
110114
const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
111115
scrollProgress.value = maxScroll > 0 ? window.scrollY / maxScroll : 0;
@@ -195,6 +199,29 @@ function setupMemberCardHoverZIndex() {
195199
});
196200
}
197201
202+
function setupNavHighlightObserver() {
203+
const sections = ["about", "achievements", "projects", "members", "news", "contact"];
204+
205+
navHighlightObserver = new IntersectionObserver(
206+
(entries) => {
207+
entries.forEach((entry) => {
208+
if (entry.isIntersecting) {
209+
activeSection.value = entry.target.id;
210+
}
211+
});
212+
},
213+
{
214+
threshold: 0.3,
215+
rootMargin: "-80px 0px -50% 0px",
216+
}
217+
);
218+
219+
sections.forEach((id) => {
220+
const el = document.getElementById(id);
221+
if (el) navHighlightObserver?.observe(el);
222+
});
223+
}
224+
198225
onMounted(async () => {
199226
document.documentElement.classList.add("js");
200227
@@ -210,6 +237,7 @@ onMounted(async () => {
210237
211238
setupSectionObserver();
212239
setupMemberCardHoverZIndex();
240+
setupNavHighlightObserver();
213241
ensureBusuanziScriptLoaded();
214242
});
215243
@@ -228,12 +256,15 @@ onUnmounted(() => {
228256
document.removeEventListener("click", onDocumentClick);
229257
sectionObserver?.disconnect();
230258
sectionObserver = null;
259+
navHighlightObserver?.disconnect();
260+
navHighlightObserver = null;
231261
});
232262
</script>
233263

234264
<template>
235-
<div class="scroll-progress" :style="{ transform: `scaleX(${scrollProgress})` }"></div>
236-
<nav class="navbar" :class="{ 'menu-open': isMenuOpen }" :style="{ background: navbarBg }">
265+
<a href="#main-content" class="skip-link">{{ t('a11y.skipToContent') }}</a>
266+
<div class="scroll-progress" :style="{ transform: `scaleX(${scrollProgress})` }" role="progressbar" :aria-valuenow="Math.round(scrollProgress * 100)" aria-valuemin="0" aria-valuemax="100" :aria-label="t('a11y.scrollProgress')"></div>
267+
<nav class="navbar" :class="{ 'menu-open': isMenuOpen }" :style="{ background: navbarBg }" role="navigation" :aria-label="t('a11y.mainNavigation')">
237268
<div class="nav-content">
238269
<a href="#" class="nav-logo" @click.prevent="scrollToTop">
239270
<img src="/RushDB.png" alt="RushDB Logo" class="nav-logo-img" />
@@ -245,22 +276,22 @@ onUnmounted(() => {
245276
</button>
246277
<ul class="nav-links">
247278
<li>
248-
<a href="#about">{{ t("nav.about") }}</a>
279+
<a href="#about" :class="{ active: activeSection === 'about' }">{{ t("nav.about") }}</a>
249280
</li>
250281
<li>
251-
<a href="#achievements">{{ t("nav.achievements") }}</a>
282+
<a href="#achievements" :class="{ active: activeSection === 'achievements' }">{{ t("nav.achievements") }}</a>
252283
</li>
253284
<li>
254-
<a href="#projects">{{ t("nav.projects") }}</a>
285+
<a href="#projects" :class="{ active: activeSection === 'projects' }">{{ t("nav.projects") }}</a>
255286
</li>
256287
<li>
257-
<a href="#members">{{ t("nav.members") }}</a>
288+
<a href="#members" :class="{ active: activeSection === 'members' }">{{ t("nav.members") }}</a>
258289
</li>
259290
<li>
260-
<a href="#news">{{ t("nav.news") }}</a>
291+
<a href="#news" :class="{ active: activeSection === 'news' }">{{ t("nav.news") }}</a>
261292
</li>
262293
<li>
263-
<a href="#contact">{{ t("nav.contact") }}</a>
294+
<a href="#contact" :class="{ active: activeSection === 'contact' }">{{ t("nav.contact") }}</a>
264295
</li>
265296
</ul>
266297

@@ -288,7 +319,7 @@ onUnmounted(() => {
288319
<HeroSection />
289320

290321
<div class="container">
291-
<main class="main-content">
322+
<main id="main-content" class="main-content" role="main">
292323
<div class="content-inner">
293324
<AboutSection />
294325
<AchievementsSection />
@@ -349,4 +380,17 @@ onUnmounted(() => {
349380
</div>
350381
</footer>
351382
</div>
383+
384+
<!-- Back to Top Button -->
385+
<button
386+
v-show="showBackToTop"
387+
class="back-to-top"
388+
type="button"
389+
:aria-label="t('a11y.backToTop')"
390+
@click="scrollToTop"
391+
>
392+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
393+
<polyline points="18 15 12 9 6 15"></polyline>
394+
</svg>
395+
</button>
352396
</template>

src/i18n.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ const messages = {
151151
en: "🇬🇧 English",
152152
ja: "🇯🇵 日本語",
153153
},
154+
a11y: {
155+
skipToContent: "跳转到主要内容",
156+
scrollProgress: "页面滚动进度",
157+
mainNavigation: "主导航",
158+
backToTop: "返回顶部",
159+
},
154160
},
155161
en: {
156162
nav: {
@@ -300,6 +306,12 @@ const messages = {
300306
en: "🇬🇧 English",
301307
ja: "🇯🇵 日本語",
302308
},
309+
a11y: {
310+
skipToContent: "Skip to main content",
311+
scrollProgress: "Page scroll progress",
312+
mainNavigation: "Main navigation",
313+
backToTop: "Back to top",
314+
},
303315
},
304316
ja: {
305317
nav: {
@@ -449,6 +461,12 @@ const messages = {
449461
en: "🇬🇧 English",
450462
ja: "🇯🇵 日本語",
451463
},
464+
a11y: {
465+
skipToContent: "メインコンテンツへスキップ",
466+
scrollProgress: "ページスクロール進捗",
467+
mainNavigation: "メインナビゲーション",
468+
backToTop: "トップへ戻る",
469+
},
452470
},
453471
} as const;
454472

src/sections/AboutSection.vue

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,58 @@
11
<script setup lang="ts">
2+
import { onMounted, onUnmounted, ref } from "vue";
23
import { useI18n } from "vue-i18n";
34
45
const { t } = useI18n({ useScope: "global" });
6+
7+
const membersCount = ref(0);
8+
const awardsCount = ref(0);
9+
const projectsCount = ref(0);
10+
const hasAnimated = ref(false);
11+
12+
let observer: IntersectionObserver | null = null;
13+
14+
function animateCounter(target: number, setter: (val: number) => void, duration = 1500) {
15+
const startTime = performance.now();
16+
const animate = (currentTime: number) => {
17+
const elapsed = currentTime - startTime;
18+
const progress = Math.min(elapsed / duration, 1);
19+
const easeOut = 1 - Math.pow(1 - progress, 3);
20+
setter(Math.floor(easeOut * target));
21+
if (progress < 1) {
22+
requestAnimationFrame(animate);
23+
}
24+
};
25+
requestAnimationFrame(animate);
26+
}
27+
28+
function startCounterAnimation() {
29+
if (hasAnimated.value) return;
30+
hasAnimated.value = true;
31+
animateCounter(3, (val) => (membersCount.value = val));
32+
animateCounter(4, (val) => (awardsCount.value = val));
33+
animateCounter(1, (val) => (projectsCount.value = val));
34+
}
35+
36+
onMounted(() => {
37+
observer = new IntersectionObserver(
38+
(entries) => {
39+
entries.forEach((entry) => {
40+
if (entry.isIntersecting) {
41+
startCounterAnimation();
42+
}
43+
});
44+
},
45+
{ threshold: 0.5 }
46+
);
47+
48+
const statsEl = document.querySelector(".intro-stats");
49+
if (statsEl) observer.observe(statsEl);
50+
});
51+
52+
onUnmounted(() => {
53+
observer?.disconnect();
54+
observer = null;
55+
});
556
</script>
657

758
<template>
@@ -25,19 +76,19 @@ const { t } = useI18n({ useScope: "global" });
2576
</p>
2677
<div class="intro-stats">
2778
<div class="stat-item">
28-
<div class="stat-number">3</div>
79+
<div class="stat-number">{{ membersCount }}</div>
2980
<div class="stat-label">
3081
{{ t("about.stats.members") }}
3182
</div>
3283
</div>
3384
<div class="stat-item">
34-
<div class="stat-number">4</div>
85+
<div class="stat-number">{{ awardsCount }}</div>
3586
<div class="stat-label">
3687
{{ t("about.stats.awards") }}
3788
</div>
3889
</div>
3990
<div class="stat-item">
40-
<div class="stat-number">1</div>
91+
<div class="stat-number">{{ projectsCount }}</div>
4192
<div class="stat-label">
4293
{{ t("about.stats.projects") }}
4394
</div>

0 commit comments

Comments
 (0)