Initial Green_Datura site
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
.next/
|
||||
.next-dev-logs/
|
||||
out/
|
||||
node_modules/
|
||||
tsconfig.tsbuildinfo
|
||||
*.log
|
||||
.env*
|
||||
*.zip
|
||||
deploy-single-file/
|
||||
5
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Green_Datura 官网
|
||||
|
||||
基于 Next.js App Router 与 Ant Design 的单页官网,包含福州大学至诚学院 Green_Datura 网络安全俱乐部简介、荣誉成果和微信公众号入口。
|
||||
|
||||
静态构建产物输出到 `out/`
|
||||
6
next-env.d.ts
vendored
Normal file
6
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
13
next.config.mjs
Normal file
13
next.config.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const projectRoot = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
outputFileTracingRoot: projectRoot
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
2069
package-lock.json
generated
Normal file
2069
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "cyber-school-team-site",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.6.1",
|
||||
"@ant-design/nextjs-registry": "^1.0.2",
|
||||
"antd": "^5.25.0",
|
||||
"next": "^15.3.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
BIN
public/assets/cyber-hero.png
Normal file
BIN
public/assets/cyber-hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/assets/green-datura-logo-mark.png
Normal file
BIN
public/assets/green-datura-logo-mark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
BIN
public/assets/green-datura-logo.png
Normal file
BIN
public/assets/green-datura-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
BIN
public/assets/green-datura-wechat-qrcode.png
Normal file
BIN
public/assets/green-datura-wechat-qrcode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
605
src/app/globals.css
Normal file
605
src/app/globals.css
Normal file
@@ -0,0 +1,605 @@
|
||||
:root {
|
||||
--primary: #16a34a;
|
||||
--accent: #52c41a;
|
||||
--cyan: #0f766e;
|
||||
--title: #12301f;
|
||||
--light-title: #12301f;
|
||||
--muted: #52715d;
|
||||
--light-muted: rgba(34, 69, 48, 0.82);
|
||||
--line: #d9e3f0;
|
||||
--glass-line: rgba(22, 101, 52, 0.16);
|
||||
--page-bg: #edfdf2;
|
||||
--surface: #ffffff;
|
||||
--glass: rgba(255, 255, 255, 0.72);
|
||||
--glass-strong: rgba(255, 255, 255, 0.86);
|
||||
--dark: #e4f9ec;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
scroll-padding-top: 78px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background:
|
||||
radial-gradient(circle at 12% 8%, rgba(34, 197, 94, 0.18), transparent 30%),
|
||||
radial-gradient(circle at 82% 24%, rgba(15, 118, 110, 0.1), transparent 28%),
|
||||
linear-gradient(145deg, #f4fff7 0%, #dff7e8 48%, #f8fff9 100%);
|
||||
color: var(--light-title);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.site-shell {
|
||||
min-height: 100vh;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
height: 64px;
|
||||
padding: 0 32px;
|
||||
background: rgba(244, 255, 247, 0.84);
|
||||
border-bottom: 1px solid var(--glass-line);
|
||||
box-shadow:
|
||||
0 18px 45px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.16);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex: 0 0 auto;
|
||||
min-width: 148px;
|
||||
color: var(--title);
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
display: block;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
object-fit: contain;
|
||||
flex: 0 0 auto;
|
||||
filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.24));
|
||||
}
|
||||
|
||||
.kicker-logo {
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.28));
|
||||
}
|
||||
|
||||
.site-anchor {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor-link {
|
||||
padding-inline: 12px 0;
|
||||
}
|
||||
|
||||
.site-header .site-anchor .ant-anchor-link-title {
|
||||
color: rgba(18, 48, 31, 0.72) !important;
|
||||
font-weight: 600;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.site-header .site-anchor .ant-anchor-link-title:hover,
|
||||
.site-header .site-anchor .ant-anchor-link-active > .ant-anchor-link-title {
|
||||
color: #166534 !important;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor-ink {
|
||||
background: rgba(82, 196, 26, 0.58);
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 640px;
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(244, 255, 247, 0.98) 0%, rgba(229, 250, 236, 0.9) 36%, rgba(220, 252, 231, 0.56) 74%, rgba(220, 252, 231, 0.24) 100%),
|
||||
linear-gradient(180deg, rgba(244, 255, 247, 0.18) 0%, rgba(237, 253, 242, 0.82) 100%),
|
||||
url("/assets/cyber-hero.png");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.hero-section::after {
|
||||
position: absolute;
|
||||
inset: auto 0 0;
|
||||
height: 130px;
|
||||
background: linear-gradient(180deg, rgba(245, 247, 251, 0), var(--page-bg));
|
||||
content: "";
|
||||
}
|
||||
|
||||
.hero-inner,
|
||||
.section-inner {
|
||||
width: min(1180px, calc(100% - 48px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 96px 0 118px;
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
max-width: 650px;
|
||||
padding: 30px;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.86), rgba(231, 250, 238, 0.68));
|
||||
border: 1px solid var(--glass-line);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 26px 70px rgba(0, 0, 0, 0.24),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.18),
|
||||
inset 0 0 24px rgba(255, 255, 255, 0.04);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.hero-copy.ant-card,
|
||||
.intro-copy.ant-card,
|
||||
.stats-panel.ant-card,
|
||||
.timeline-panel.ant-card {
|
||||
color: var(--light-muted);
|
||||
}
|
||||
|
||||
.hero-kicker,
|
||||
.section-kicker,
|
||||
.stats-kicker {
|
||||
color: var(--primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-kicker {
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.hero-title.ant-typography {
|
||||
margin: 14px 0 18px;
|
||||
color: var(--title);
|
||||
font-size: 64px;
|
||||
line-height: 1.06;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.hero-text.ant-typography {
|
||||
max-width: 590px;
|
||||
color: rgba(34, 69, 48, 0.8);
|
||||
font-size: 20px;
|
||||
line-height: 1.72;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.ant-btn-primary.ant-btn-color-primary {
|
||||
color: #ffffff;
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 8px 18px rgba(22, 163, 74, 0.22);
|
||||
}
|
||||
|
||||
.ant-btn-primary.ant-btn-color-primary:not(:disabled):not(.ant-btn-disabled):hover {
|
||||
color: #ffffff;
|
||||
background: #15803d;
|
||||
border-color: #15803d;
|
||||
}
|
||||
|
||||
.hero-secondary-button {
|
||||
color: #166534;
|
||||
border-color: rgba(22, 101, 52, 0.24);
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 92px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section::before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
linear-gradient(rgba(22, 101, 52, 0.06) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(22, 101, 52, 0.06) 1px, transparent 1px);
|
||||
background-size: 44px 44px;
|
||||
mask-image: linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent);
|
||||
content: "";
|
||||
}
|
||||
|
||||
.section-heading {
|
||||
position: relative;
|
||||
max-width: 720px;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.section-heading .ant-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-heading h2.ant-typography,
|
||||
.wechat-copy h2.ant-typography {
|
||||
margin-top: 12px;
|
||||
color: var(--light-title);
|
||||
font-size: 38px;
|
||||
line-height: 1.18;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.section-heading .ant-typography + .ant-typography,
|
||||
.wechat-copy .ant-typography + .ant-typography {
|
||||
margin-top: 14px;
|
||||
color: var(--light-muted);
|
||||
font-size: 16px;
|
||||
line-height: 1.78;
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
background: linear-gradient(180deg, rgba(237, 253, 242, 0.96), rgba(220, 248, 230, 0.92));
|
||||
}
|
||||
|
||||
.intro-copy {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
padding: 34px;
|
||||
color: var(--light-muted);
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--glass-line);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 22px 55px rgba(0, 0, 0, 0.18),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.14);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.intro-copy .ant-typography {
|
||||
color: var(--light-muted);
|
||||
}
|
||||
|
||||
.intro-copy h3.ant-typography {
|
||||
margin: 0 0 18px;
|
||||
color: var(--light-title);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.intro-copy p.ant-typography {
|
||||
color: var(--light-muted);
|
||||
line-height: 1.78;
|
||||
}
|
||||
|
||||
.tag-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.tag-grid .ant-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 32px;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
font-weight: 600;
|
||||
background: rgba(34, 197, 94, 0.14);
|
||||
border-color: rgba(82, 196, 26, 0.4);
|
||||
}
|
||||
|
||||
.culture-grid {
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
.culture-item {
|
||||
padding: 16px;
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid rgba(22, 101, 52, 0.12);
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
color: var(--light-muted);
|
||||
}
|
||||
|
||||
.culture-icon {
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
color: #166534;
|
||||
background: rgba(82, 196, 26, 0.16);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.culture-item strong {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: var(--light-title);
|
||||
}
|
||||
|
||||
.culture-item p {
|
||||
margin: 0;
|
||||
color: var(--light-muted);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.stats-panel {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
padding: 30px;
|
||||
color: #ffffff;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(22, 163, 74, 0.86), rgba(15, 118, 110, 0.52)),
|
||||
rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid var(--glass-line);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 24px 55px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.16);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.stats-kicker {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.stat-tile {
|
||||
min-height: 112px;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
border: 1px solid rgba(255, 255, 255, 0.26);
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.stat-tile .ant-card-body,
|
||||
.stat-tile-content {
|
||||
width: 100%;
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
.stat-tile .ant-statistic-title,
|
||||
.stat-tile .ant-statistic-content {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.stat-tile .ant-statistic-title {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.honors-section {
|
||||
background:
|
||||
linear-gradient(180deg, rgba(228, 249, 236, 0.94) 0%, rgba(239, 253, 244, 0.98) 100%);
|
||||
}
|
||||
|
||||
.timeline-panel {
|
||||
position: relative;
|
||||
padding: 34px 34px 10px;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--glass-line);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 22px 55px rgba(0, 0, 0, 0.18),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.14);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.timeline-year {
|
||||
color: var(--primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.timeline-item strong {
|
||||
color: var(--light-title);
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.timeline-item p {
|
||||
margin: 0;
|
||||
color: var(--light-muted);
|
||||
}
|
||||
|
||||
.wechat-section {
|
||||
background:
|
||||
linear-gradient(135deg, rgba(220, 252, 231, 0.86), rgba(209, 250, 229, 0.74)),
|
||||
var(--dark);
|
||||
}
|
||||
|
||||
.wechat-inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.wechat-copy {
|
||||
max-width: 660px;
|
||||
}
|
||||
|
||||
.wechat-copy .section-kicker,
|
||||
.wechat-copy h2.ant-typography {
|
||||
color: var(--title);
|
||||
}
|
||||
|
||||
.wechat-copy .ant-typography + .ant-typography {
|
||||
color: var(--light-muted);
|
||||
}
|
||||
|
||||
.wechat-card {
|
||||
flex: 0 0 320px;
|
||||
padding: 28px;
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 22px 55px rgba(0, 0, 0, 0.24),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.wechat-qrcode-image {
|
||||
display: block;
|
||||
width: min(238px, 100%);
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.wechat-card span {
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
color: rgba(18, 48, 31, 0.72);
|
||||
text-align: center;
|
||||
background: #dcfce7;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.site-header {
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.site-anchor {
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.site-anchor::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor {
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor-link {
|
||||
padding-inline: 10px 0;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
min-height: 620px;
|
||||
background-position: 60% center;
|
||||
}
|
||||
|
||||
.hero-title.ant-typography {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.hero-text.ant-typography {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 72px 0;
|
||||
}
|
||||
|
||||
.section-heading h2.ant-typography,
|
||||
.wechat-copy h2.ant-typography {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.wechat-inner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.wechat-card {
|
||||
flex-basis: auto;
|
||||
max-width: 340px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
html {
|
||||
scroll-padding-top: 68px;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.site-anchor .ant-anchor-link {
|
||||
padding-inline: 8px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.hero-inner,
|
||||
.section-inner {
|
||||
width: min(100% - 32px, 1180px);
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
min-height: 590px;
|
||||
}
|
||||
|
||||
.hero-inner {
|
||||
padding: 74px 0 96px;
|
||||
}
|
||||
|
||||
.hero-title.ant-typography {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.hero-text.ant-typography {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.intro-copy,
|
||||
.stats-panel,
|
||||
.timeline-panel {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.section-heading h2.ant-typography,
|
||||
.wechat-copy h2.ant-typography {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.culture-item-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wechat-card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
29
src/app/layout.tsx
Normal file
29
src/app/layout.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { Metadata } from "next";
|
||||
import { AntdRegistry } from "@ant-design/nextjs-registry";
|
||||
import "antd/dist/reset.css";
|
||||
import "./globals.css";
|
||||
import { Providers } from "./providers";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Green_Datura 官网",
|
||||
description: "展示福州大学至诚学院 Green_Datura 网络安全俱乐部简介、荣誉成果与微信公众号入口。",
|
||||
icons: {
|
||||
icon: "/assets/green-datura-logo-mark.png"
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="zh-CN">
|
||||
<body>
|
||||
<AntdRegistry>
|
||||
<Providers>{children}</Providers>
|
||||
</AntdRegistry>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
69
src/app/page.tsx
Normal file
69
src/app/page.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowRightOutlined } from "@ant-design/icons";
|
||||
import { Anchor, Button, Card, Flex, Layout, Space, Typography } from "antd";
|
||||
import type { AnchorProps } from "antd";
|
||||
import { Honors } from "@/components/Honors";
|
||||
import { TeamIntro } from "@/components/TeamIntro";
|
||||
import { WeChatSection } from "@/components/WeChatSection";
|
||||
|
||||
const { Content, Footer, Header } = Layout;
|
||||
const { Paragraph, Title } = Typography;
|
||||
|
||||
const navItems: AnchorProps["items"] = [
|
||||
{ key: "home", href: "#home", title: "首页" },
|
||||
{ key: "intro", href: "#intro", title: "俱乐部简介" },
|
||||
{ key: "honors", href: "#honors", title: "荣誉成果" },
|
||||
{ key: "wechat", href: "#wechat", title: "微信公众号" }
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<Layout className="site-shell">
|
||||
<Header className="site-header">
|
||||
<a className="brand" href="#home" aria-label="Green_Datura 首页">
|
||||
<img className="brand-logo" src="/assets/green-datura-logo-mark.png" alt="" />
|
||||
<span>Green_Datura</span>
|
||||
</a>
|
||||
<Anchor
|
||||
affix={false}
|
||||
direction="horizontal"
|
||||
targetOffset={76}
|
||||
items={navItems}
|
||||
className="site-anchor"
|
||||
/>
|
||||
</Header>
|
||||
|
||||
<Content>
|
||||
<section id="home" className="hero-section">
|
||||
<div className="hero-inner">
|
||||
<Card className="hero-copy" variant="borderless" styles={{ body: { padding: 0 } }}>
|
||||
<Space className="hero-kicker" size={8}>
|
||||
<img className="kicker-logo" src="/assets/green-datura-logo-mark.png" alt="" />
|
||||
福州大学至诚学院
|
||||
</Space>
|
||||
<Title className="hero-title">Green_Datura</Title>
|
||||
<Paragraph className="hero-text">
|
||||
成立于 2020 年 3 月的至诚学院网络安全俱乐部,寓意生生不息的希望,面向 CTF、漏洞挖掘与校园安全实践持续成长。
|
||||
</Paragraph>
|
||||
<Flex gap={12} wrap="wrap" className="hero-actions">
|
||||
<Button type="primary" size="large" href="#intro" icon={<ArrowRightOutlined />}>
|
||||
了解俱乐部
|
||||
</Button>
|
||||
<Button size="large" href="#honors" className="hero-secondary-button">
|
||||
查看荣誉
|
||||
</Button>
|
||||
</Flex>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<TeamIntro />
|
||||
<Honors />
|
||||
<WeChatSection />
|
||||
</Content>
|
||||
|
||||
<Footer className="site-footer">Green_Datura · 生生不息,代代传承</Footer>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
48
src/app/providers.tsx
Normal file
48
src/app/providers.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { ConfigProvider } from "antd";
|
||||
import zhCN from "antd/locale/zh_CN";
|
||||
|
||||
type ProvidersProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function Providers({ children }: ProvidersProps) {
|
||||
return (
|
||||
<ConfigProvider
|
||||
locale={zhCN}
|
||||
theme={{
|
||||
token: {
|
||||
colorPrimary: "#16a34a",
|
||||
colorSuccess: "#52c41a",
|
||||
colorText: "#12301f",
|
||||
colorBgLayout: "#edfdf2",
|
||||
colorBgContainer: "rgba(255, 255, 255, 0.82)",
|
||||
colorBorder: "rgba(22, 101, 52, 0.16)",
|
||||
borderRadius: 8,
|
||||
fontFamily:
|
||||
'Inter, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif'
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
borderRadius: 8,
|
||||
controlHeight: 42
|
||||
},
|
||||
Card: {
|
||||
borderRadiusLG: 8
|
||||
},
|
||||
Anchor: {
|
||||
linkPaddingBlock: 8,
|
||||
linkPaddingInlineStart: 12
|
||||
},
|
||||
Tag: {
|
||||
borderRadiusSM: 6
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
45
src/components/Honors.tsx
Normal file
45
src/components/Honors.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { Card, Space, Timeline, Typography } from "antd";
|
||||
import { TrophyOutlined } from "@ant-design/icons";
|
||||
import { timelineHonors, type HonorLevel } from "@/data/honors";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const levelColor: Record<HonorLevel, string> = {
|
||||
attackDefense: "#52c41a",
|
||||
ctf: "#1677ff",
|
||||
vulnerability: "#13c2c2",
|
||||
campus: "#faad14"
|
||||
};
|
||||
|
||||
export function Honors() {
|
||||
const timelineItems = timelineHonors.map((honor) => ({
|
||||
color: levelColor[honor.level],
|
||||
children: (
|
||||
<div className="timeline-item">
|
||||
<span className="timeline-year">{honor.year}</span>
|
||||
<strong>{honor.event}</strong>
|
||||
<p>{honor.award}</p>
|
||||
</div>
|
||||
)
|
||||
}));
|
||||
|
||||
return (
|
||||
<section id="honors" className="section honors-section">
|
||||
<div className="section-inner">
|
||||
<div className="section-heading">
|
||||
<Space className="section-kicker" size={8}>
|
||||
<TrophyOutlined />
|
||||
荣誉成果
|
||||
</Space>
|
||||
<Title level={2}>用成绩记录持续进步</Title>
|
||||
</div>
|
||||
|
||||
<Card className="timeline-panel" variant="borderless" styles={{ body: { padding: 0 } }}>
|
||||
<Timeline mode="left" items={timelineItems} />
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
116
src/components/TeamIntro.tsx
Normal file
116
src/components/TeamIntro.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import { Card, Col, Flex, Row, Space, Statistic, Tag, Typography } from "antd";
|
||||
import {
|
||||
AimOutlined,
|
||||
BugOutlined,
|
||||
CodeOutlined,
|
||||
ExperimentOutlined,
|
||||
TeamOutlined
|
||||
} from "@ant-design/icons";
|
||||
|
||||
const { Paragraph, Title } = Typography;
|
||||
|
||||
const directions = [
|
||||
"WEB安全研究",
|
||||
"渗透测试",
|
||||
"漏洞挖掘",
|
||||
"代码审计",
|
||||
"二进制安全",
|
||||
"移动安全",
|
||||
"数据安全",
|
||||
"密码安全"
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{ title: "成立时间", value: "2020年3月" },
|
||||
{ title: "研究方向", value: "8+" },
|
||||
{ title: "CNVD上报", value: "20+" }
|
||||
];
|
||||
|
||||
const cultureItems = [
|
||||
{ icon: <AimOutlined />, title: "生生不息", text: "Green_Datura 寓意生生不息的希望,俱乐部希望在至诚延续、代代传承。" },
|
||||
{ icon: <ExperimentOutlined />, title: "实战实践", text: "参与国家级、省级攻防演练、CTF 赛事与漏洞响应平台,把学习转化为真实安全能力。" },
|
||||
{ icon: <TeamOutlined />, title: "校园守护", text: "在学院网信办老师协作下,参与维护学校网络安全。" }
|
||||
];
|
||||
|
||||
export function TeamIntro() {
|
||||
return (
|
||||
<section id="intro" className="section intro-section">
|
||||
<div className="section-inner">
|
||||
<div className="section-heading">
|
||||
<Space className="section-kicker" size={8}>
|
||||
<BugOutlined />
|
||||
俱乐部简介
|
||||
</Space>
|
||||
<Title level={2}>富有活力的年轻安全团队</Title>
|
||||
<Paragraph>
|
||||
Green_Datura 团队于 2020 年成立,由福州大学至诚学院的网络安全爱好者组成,在计算机工程系支持下发展,并由黄巧云老师担任指导。
|
||||
团队长期参与攻防演练、CTF 赛事与校园网络安全维护。
|
||||
</Paragraph>
|
||||
</div>
|
||||
|
||||
<Row gutter={[24, 24]} align="stretch">
|
||||
<Col xs={24} lg={14}>
|
||||
<Card className="intro-copy" variant="borderless" styles={{ body: { padding: 0 } }}>
|
||||
<Title level={3}>研究方向</Title>
|
||||
<Flex wrap="wrap" gap={10} className="tag-grid">
|
||||
{directions.map((item) => (
|
||||
<Tag key={item} color="blue">
|
||||
{item}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
<Paragraph>
|
||||
目前俱乐部成员研究方向包括但不限于 WEB 安全、渗透测试、漏洞挖掘、代码审计、二进制安全、移动安全、数据安全和密码安全等。团队曾向腾讯、美团、华为、
|
||||
微博、BOSS直聘、京东、58同城、百卓网络、星网锐捷、嘀嗒出行和厦门航空等平台报告漏洞,并在国家信息安全漏洞库(CNVD)上报超过 20 项。
|
||||
</Paragraph>
|
||||
<Flex vertical gap={14} className="culture-grid">
|
||||
{cultureItems.map((item) => (
|
||||
<Card
|
||||
className="culture-item"
|
||||
key={item.title}
|
||||
size="small"
|
||||
variant="borderless"
|
||||
styles={{ body: { padding: 0 } }}
|
||||
>
|
||||
<Flex gap={14} align="flex-start" className="culture-item-content">
|
||||
<span className="culture-icon">{item.icon}</span>
|
||||
<div>
|
||||
<strong>{item.title}</strong>
|
||||
<p>{item.text}</p>
|
||||
</div>
|
||||
</Flex>
|
||||
</Card>
|
||||
))}
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} lg={10}>
|
||||
<Card className="stats-panel" variant="borderless" styles={{ body: { padding: 0 } }}>
|
||||
<Flex vertical gap={16}>
|
||||
<Space className="stats-kicker" size={8}>
|
||||
<CodeOutlined />
|
||||
Club Profile
|
||||
</Space>
|
||||
{stats.map((item) => (
|
||||
<Card
|
||||
className="stat-tile"
|
||||
key={item.title}
|
||||
size="small"
|
||||
variant="borderless"
|
||||
styles={{ body: { padding: 0 } }}
|
||||
>
|
||||
<Flex align="center" className="stat-tile-content">
|
||||
<Statistic title={item.title} value={item.value} />
|
||||
</Flex>
|
||||
</Card>
|
||||
))}
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
56
src/components/WeChatSection.tsx
Normal file
56
src/components/WeChatSection.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Card, Flex, Image, Space, Typography } from "antd";
|
||||
import { LinkOutlined, WechatOutlined } from "@ant-design/icons";
|
||||
|
||||
const { Paragraph, Title } = Typography;
|
||||
|
||||
const WECHAT_URL =
|
||||
"https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=Mzg4NDY5NjIyNw==#wechat_redirect";
|
||||
|
||||
export function WeChatSection() {
|
||||
return (
|
||||
<section id="wechat" className="section wechat-section">
|
||||
<Flex className="section-inner wechat-inner" align="center" gap={54}>
|
||||
<div className="wechat-copy">
|
||||
<Space className="section-kicker" size={8}>
|
||||
<WechatOutlined />
|
||||
微信公众号
|
||||
</Space>
|
||||
<Title level={2}>关注招新通知与技术分享</Title>
|
||||
<Paragraph>
|
||||
欢迎关注 Green_Datura 微信公众号,获取比赛资讯、技术分享、招新通知以及团队动态。俱乐部每年面向大一、大二招新,欢迎对网络安全感兴趣并愿意投入时间精力的同学加入。
|
||||
</Paragraph>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
href={WECHAT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon={<LinkOutlined />}
|
||||
>
|
||||
关注公众号
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card
|
||||
className="wechat-card"
|
||||
aria-label="微信公众号二维码"
|
||||
variant="borderless"
|
||||
styles={{ body: { padding: 0 } }}
|
||||
>
|
||||
<Flex vertical align="center" gap={16}>
|
||||
<Image
|
||||
src="/assets/green-datura-wechat-qrcode.png"
|
||||
alt="Green_Datura 微信公众号二维码"
|
||||
width={238}
|
||||
preview={false}
|
||||
className="wechat-qrcode-image"
|
||||
/>
|
||||
<span>扫码访问公众号入口</span>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Flex>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
101
src/data/honors.ts
Normal file
101
src/data/honors.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
export type HonorLevel = "attackDefense" | "ctf" | "vulnerability" | "campus";
|
||||
|
||||
export type Honor = {
|
||||
year: string;
|
||||
event: string;
|
||||
award: string;
|
||||
level: HonorLevel;
|
||||
};
|
||||
|
||||
export const timelineHonors: Honor[] = [
|
||||
{
|
||||
year: "2025年11月26日",
|
||||
event: "2025第九届一带一路暨金砖国家技能发展与技术创新大赛 - 第二届网络安全防护治理实战技能区域赛",
|
||||
award: "省级一等奖",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2024-2025年",
|
||||
event: "第五届福建省“闽盾杯”网络安全空间大赛",
|
||||
award: "二等奖 1 项,三等奖 1 项",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2024年12月15日",
|
||||
event: "2024深育杯大学生网络安全大赛网络攻防赛道",
|
||||
award: "国家级特等奖",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2024年",
|
||||
event: "“数字中国创新大赛”数字安全赛道",
|
||||
award: "铜奖 1 项,优秀奖 3 项",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2024年",
|
||||
event: "“闽盾-2024”网络安全攻防演练",
|
||||
award: "优秀攻击方",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2024年",
|
||||
event: "福建省文旅厅攻防演练",
|
||||
award: "第二名",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2024年",
|
||||
event: "眉山市攻防护网行动",
|
||||
award: "攻击队排名第六,得分超过 24,000 分",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2024年",
|
||||
event: "福州网信市护行动",
|
||||
award: "三等奖,得分超过 20,000 分",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2023-2024年",
|
||||
event: "第四届福建省“闽盾杯”网络安全空间大赛",
|
||||
award: "二等奖 1 项,三等奖 1 项",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2023-2024年",
|
||||
event: "第 20 届信息安全与对抗技术竞赛",
|
||||
award: "个人挑战赛一等奖 8 项,另获多项二等奖、三等奖",
|
||||
level: "ctf"
|
||||
},
|
||||
{
|
||||
year: "2023年",
|
||||
event: "交通运输部海事局攻防演练",
|
||||
award: "第一名",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2023年",
|
||||
event: "福建省生态环境护网行动(红队)",
|
||||
award: "总分超过 7,000 分,排名第二",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2023年",
|
||||
event: "福建省教育攻防护网行动(红队)",
|
||||
award: "高校组二等奖",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2023年",
|
||||
event: "“闽盾-2023”网络安全攻防演练",
|
||||
award: "优秀攻击方",
|
||||
level: "attackDefense"
|
||||
},
|
||||
{
|
||||
year: "2023年",
|
||||
event: "福州市攻防护网行动(红队)",
|
||||
award: "攻击队三等奖,得分超过 10,000 分",
|
||||
level: "attackDefense"
|
||||
}
|
||||
];
|
||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user