mirror of
https://github.com/KivotosRailway/website.git
synced 2026-11-03 11:06:32 +09:00
简略新建 成员一览 页面
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 411 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import { MembersPage } from "@/components/members/members-page";
|
||||||
|
import { getLocaleMessages, isLocale } from "@/lib/i18n";
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
|
||||||
|
const { locale } = await params;
|
||||||
|
if (!isLocale(locale)) notFound();
|
||||||
|
return { title: getLocaleMessages(locale).footer.columns[1].links[1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function MembersRoute({ params }: { params: Promise<{ locale: string }> }) {
|
||||||
|
const { locale } = await params;
|
||||||
|
if (!isLocale(locale)) notFound();
|
||||||
|
return <MembersPage locale={locale} />;
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ a { color: inherit; text-decoration: none; }
|
|||||||
.friendly-links-content { width: min(1214px, 100%); margin: 0 auto; }
|
.friendly-links-content { width: min(1214px, 100%); margin: 0 auto; }
|
||||||
.friendly-links-section + .friendly-links-section { margin-top: 50px; }
|
.friendly-links-section + .friendly-links-section { margin-top: 50px; }
|
||||||
.friendly-links-section h2 { margin: 0 0 20px; color: #181c20; font-size: 40px; font-weight: 700; line-height: 48px; }
|
.friendly-links-section h2 { margin: 0 0 20px; color: #181c20; font-size: 40px; font-weight: 700; line-height: 48px; }
|
||||||
|
.friendly-links-section h2::after { display: block; width: 28.875px; height: 5.25px; margin-top: 17.5px; background: #8f56a2; content: ""; }
|
||||||
.friendly-links-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
|
.friendly-links-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
|
||||||
.friendly-link-card { position: relative; display: flex; align-items: stretch; min-width: 0; min-height: 186px; overflow: hidden; border: 1.1px solid #e3e6e8; border-radius: 16.42px; background: #fff; color: #181c20; transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease; }
|
.friendly-link-card { position: relative; display: flex; align-items: stretch; min-width: 0; min-height: 186px; overflow: hidden; border: 1.1px solid #e3e6e8; border-radius: 16.42px; background: #fff; color: #181c20; transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease; }
|
||||||
a.friendly-link-card:hover, a.friendly-link-card:focus-visible { border-color: #8f56a2; box-shadow: 0 8px 20px rgba(40, 29, 47, .1); transform: translateY(-2px); }
|
a.friendly-link-card:hover, a.friendly-link-card:focus-visible { border-color: #8f56a2; box-shadow: 0 8px 20px rgba(40, 29, 47, .1); transform: translateY(-2px); }
|
||||||
@@ -73,6 +74,24 @@ a.friendly-link-card:hover, a.friendly-link-card:focus-visible { border-color: #
|
|||||||
.friendly-link-card-description { color: #8d949a; font-size: 16.42px; line-height: 21.89px; text-align: center; }
|
.friendly-link-card-description { color: #8d949a; font-size: 16.42px; line-height: 21.89px; text-align: center; }
|
||||||
.friendly-link-card-archive { position: absolute; top: 13px; right: 5px; padding: 4px 6px; border-radius: 4px; background: #e72329; color: #fff; font-size: 10px; line-height: 12px; }
|
.friendly-link-card-archive { position: absolute; top: 13px; right: 5px; padding: 4px 6px; border-radius: 4px; background: #e72329; color: #fff; font-size: 10px; line-height: 12px; }
|
||||||
.friendly-links-page .page-bottom-nav { margin-top: 176px; }
|
.friendly-links-page .page-bottom-nav { margin-top: 176px; }
|
||||||
|
.members-page-inner { gap: 50px; }
|
||||||
|
.members-hero { background: var(--policy-hero-image) center 47% / 100% auto no-repeat; }
|
||||||
|
.members-sections { width: min(1200px, 100%); }
|
||||||
|
.member-section + .member-section { margin-top: 46px; }
|
||||||
|
.member-section-heading { margin: 0 0 20px; }
|
||||||
|
.member-section-heading h2 { margin: 0; color: #202326; font-size: 30px; font-weight: 700; line-height: 1.35; letter-spacing: -.03em; }
|
||||||
|
.member-section-heading span { display: block; width: 28px; height: 5px; margin-top: 12px; background: #8f56a2; }
|
||||||
|
.member-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 28px 20px; }
|
||||||
|
.member-card { display: block; min-width: 0; color: #202326; }
|
||||||
|
a.member-card { transition: opacity .2s ease, transform .2s ease; }
|
||||||
|
a.member-card:hover, a.member-card:focus-visible { opacity: .82; transform: translateY(-2px); }
|
||||||
|
.member-card-avatar { display: flex; aspect-ratio: 1; align-items: center; justify-content: center; overflow: hidden; border-radius: 4px; background: #d9d9d9; color: #8f56a2; font-size: 72px; font-weight: 700; }
|
||||||
|
.member-card-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
||||||
|
.member-card-copy { padding-top: 12px; }
|
||||||
|
.member-card-copy h3 { margin: 0; font-size: 20px; font-weight: 700; line-height: 1.4; letter-spacing: -.03em; }
|
||||||
|
.member-card-copy p { margin: 4px 0 0; color: #5f6464; font-size: 14px; line-height: 1.45; }
|
||||||
|
.member-empty { margin: 0; padding: 40px 0; color: #697684; font-size: 16px; }
|
||||||
|
.members-page .page-bottom-nav { margin-top: 80px; }
|
||||||
.news-page-inner { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
|
.news-page-inner { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
|
||||||
.news-page-header { display: flex; justify-content: center; align-items: center; margin: 0 0 30px; }
|
.news-page-header { display: flex; justify-content: center; align-items: center; margin: 0 0 30px; }
|
||||||
.news-page-header h1 { margin: 0; font-size: clamp(52px, 5vw, 88px); letter-spacing: -0.07em; line-height: 1; font-weight: 800; }
|
.news-page-header h1 { margin: 0; font-size: clamp(52px, 5vw, 88px); letter-spacing: -0.07em; line-height: 1; font-weight: 800; }
|
||||||
@@ -224,6 +243,9 @@ html[data-theme="dark"] .site-footer { color: #afbbc6; background: #0b1016; }
|
|||||||
html[data-theme="dark"] .site-footer-top, html[data-theme="dark"] .site-footer-meta { border-color: rgba(255, 255, 255, 0.1); }
|
html[data-theme="dark"] .site-footer-top, html[data-theme="dark"] .site-footer-meta { border-color: rgba(255, 255, 255, 0.1); }
|
||||||
html[data-theme="dark"] .site-footer-title, html[data-theme="dark"] .site-footer-column h3, html[data-theme="dark"] .site-footer-column a, html[data-theme="dark"] .site-footer-icon, html[data-theme="dark"] .site-footer-legal-row, html[data-theme="dark"] .site-footer-copyright-row, html[data-theme="dark"] .site-footer-meta p { color: #afbbc6; }
|
html[data-theme="dark"] .site-footer-title, html[data-theme="dark"] .site-footer-column h3, html[data-theme="dark"] .site-footer-column a, html[data-theme="dark"] .site-footer-icon, html[data-theme="dark"] .site-footer-legal-row, html[data-theme="dark"] .site-footer-copyright-row, html[data-theme="dark"] .site-footer-meta p { color: #afbbc6; }
|
||||||
html[data-theme="dark"] .site-footer-icon img { filter: brightness(0) invert(1); opacity: .78; }
|
html[data-theme="dark"] .site-footer-icon img { filter: brightness(0) invert(1); opacity: .78; }
|
||||||
|
html[data-theme="dark"] .member-section-heading h2, html[data-theme="dark"] .member-card { color: var(--ink); }
|
||||||
|
html[data-theme="dark"] .member-card-avatar { background: #263240; }
|
||||||
|
html[data-theme="dark"] .member-card-copy p, html[data-theme="dark"] .member-empty { color: #afbbc6; }
|
||||||
|
|
||||||
/* The static HTML has no theme attribute. Apply the system theme before React hydrates. */
|
/* The static HTML has no theme attribute. Apply the system theme before React hydrates. */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@@ -264,6 +286,9 @@ html[data-theme="dark"] .site-footer-icon img { filter: brightness(0) invert(1);
|
|||||||
html:not([data-theme]) .site-footer-top, html:not([data-theme]) .site-footer-meta { border-color: rgba(255, 255, 255, 0.1); }
|
html:not([data-theme]) .site-footer-top, html:not([data-theme]) .site-footer-meta { border-color: rgba(255, 255, 255, 0.1); }
|
||||||
html:not([data-theme]) .site-footer-title, html:not([data-theme]) .site-footer-column h3, html:not([data-theme]) .site-footer-column a, html:not([data-theme]) .site-footer-icon, html:not([data-theme]) .site-footer-legal-row, html:not([data-theme]) .site-footer-copyright-row, html:not([data-theme]) .site-footer-meta p { color: #afbbc6; }
|
html:not([data-theme]) .site-footer-title, html:not([data-theme]) .site-footer-column h3, html:not([data-theme]) .site-footer-column a, html:not([data-theme]) .site-footer-icon, html:not([data-theme]) .site-footer-legal-row, html:not([data-theme]) .site-footer-copyright-row, html:not([data-theme]) .site-footer-meta p { color: #afbbc6; }
|
||||||
html:not([data-theme]) .site-footer-icon img { filter: brightness(0) invert(1); opacity: .78; }
|
html:not([data-theme]) .site-footer-icon img { filter: brightness(0) invert(1); opacity: .78; }
|
||||||
|
html:not([data-theme]) .member-section-heading h2, html:not([data-theme]) .member-card { color: var(--ink); }
|
||||||
|
html:not([data-theme]) .member-card-avatar { background: #263240; }
|
||||||
|
html:not([data-theme]) .member-card-copy p, html:not([data-theme]) .member-empty { color: #afbbc6; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
@@ -332,6 +357,7 @@ html[data-theme="dark"] .site-footer-icon img { filter: brightness(0) invert(1);
|
|||||||
.site-footer-brand-wrap { display: none; }
|
.site-footer-brand-wrap { display: none; }
|
||||||
.site-footer-socials { flex-direction: row; align-items: center; }
|
.site-footer-socials { flex-direction: row; align-items: center; }
|
||||||
.site-footer-top { align-items: center; }
|
.site-footer-top { align-items: center; }
|
||||||
|
.member-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 16px; }
|
||||||
.site-footer-legal-row { gap: 8px 12px; }
|
.site-footer-legal-row { gap: 8px 12px; }
|
||||||
.site-footer { height: auto; padding: 28px 0 22px; }
|
.site-footer { height: auto; padding: 28px 0 22px; }
|
||||||
.site-footer-meta p { font-size: 12px; }
|
.site-footer-meta p { font-size: 12px; }
|
||||||
@@ -423,6 +449,8 @@ html[data-theme="dark"] .site-footer-icon img { filter: brightness(0) invert(1);
|
|||||||
.site-footer-socials { gap: 18px; }
|
.site-footer-socials { gap: 18px; }
|
||||||
.site-footer-title { width: auto; font-size: 18px; }
|
.site-footer-title { width: auto; font-size: 18px; }
|
||||||
.site-footer-icons { gap: 14px; }
|
.site-footer-icons { gap: 14px; }
|
||||||
|
.member-section-heading h2 { font-size: 26px; }
|
||||||
|
.member-grid { grid-template-columns: 1fr; gap: 24px; }
|
||||||
.site-footer-main { padding-top: 22px; }
|
.site-footer-main { padding-top: 22px; }
|
||||||
.site-footer-columns { gap: 18px; }
|
.site-footer-columns { gap: 18px; }
|
||||||
.site-footer-column h3 { font-size: 18px; }
|
.site-footer-column h3 { font-size: 18px; }
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import members from "@/data/members.json";
|
||||||
|
import type { CSSProperties } from "react";
|
||||||
|
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
||||||
|
import { getLocaleMessages, type Locale } from "@/lib/i18n";
|
||||||
|
|
||||||
|
type LocalizedText = Record<Locale, string>;
|
||||||
|
type MemberEntry = {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
avatar?: string;
|
||||||
|
url?: string;
|
||||||
|
i18n?: { name?: Partial<LocalizedText>; role?: Partial<LocalizedText> };
|
||||||
|
};
|
||||||
|
type MemberSection = { id: string; title: LocalizedText; members: MemberEntry[] };
|
||||||
|
type MembersData = { title: LocalizedText; empty: LocalizedText; sections: MemberSection[] };
|
||||||
|
|
||||||
|
const memberData = members as MembersData;
|
||||||
|
|
||||||
|
function localized(value: string, translations: Partial<LocalizedText> | undefined, locale: Locale) {
|
||||||
|
return translations?.[locale] ?? value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MemberCard({ member, locale }: { member: MemberEntry; locale: Locale }) {
|
||||||
|
const name = localized(member.name, member.i18n?.name, locale);
|
||||||
|
const role = localized(member.role, member.i18n?.role, locale);
|
||||||
|
const content = (
|
||||||
|
<>
|
||||||
|
<div className="member-card-avatar">
|
||||||
|
{member.avatar ? (
|
||||||
|
// eslint-disable-next-line @next/next/no-img-element -- Member data may use static or remote avatar URLs in a static export.
|
||||||
|
<img src={member.avatar} alt="" width={285} height={285} />
|
||||||
|
) : <span aria-hidden="true">{name.slice(0, 1)}</span>}
|
||||||
|
</div>
|
||||||
|
<div className="member-card-copy"><h3>{name}</h3>{role && <p>{role}</p>}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return member.url ? (
|
||||||
|
<a className="member-card" href={member.url} target="_blank" rel="noreferrer" aria-label={name}>{content}</a>
|
||||||
|
) : <article className="member-card">{content}</article>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MembersPage({ locale }: { locale: Locale }) {
|
||||||
|
const messages = getLocaleMessages(locale);
|
||||||
|
const pageTitle = memberData.title[locale];
|
||||||
|
const heroStyle = { "--policy-hero-image": 'url("/members/members-hero.png")' } as CSSProperties;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="policy-page members-page">
|
||||||
|
<div className="policy-page-inner members-page-inner">
|
||||||
|
<section className="policy-hero members-hero" aria-labelledby="members-page-title" style={heroStyle}>
|
||||||
|
<div className="policy-hero-inner"><h1 id="members-page-title">{pageTitle}</h1></div>
|
||||||
|
</section>
|
||||||
|
<div className="members-sections">
|
||||||
|
{memberData.sections.map((section) => (
|
||||||
|
<section className="member-section" key={section.id} aria-labelledby={`members-${section.id}`}>
|
||||||
|
<header className="member-section-heading"><h2 id={`members-${section.id}`}>{section.title[locale]}</h2><span aria-hidden="true" /></header>
|
||||||
|
{section.members.length ? (
|
||||||
|
<div className="member-grid">
|
||||||
|
{section.members.map((member, index) => <MemberCard key={`${section.id}-${member.name}-${index}`} member={member} locale={locale} />)}
|
||||||
|
</div>
|
||||||
|
) : <p className="member-empty">{memberData.empty[locale]}</p>}
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="page-bottom-nav"><div className="page-bottom-nav-inner"><PageBreadcrumb items={[{ label: messages.common.home, href: "/" }, { label: messages.footer.columns[1].title }, { label: pageTitle }]} /></div></div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"title": {
|
||||||
|
"zh-Hans": "成员一览",
|
||||||
|
"zh-Hant": "成員一覽",
|
||||||
|
"en": "Members",
|
||||||
|
"jp": "メンバー"
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"zh-Hans": "成员资料正在整理中。",
|
||||||
|
"zh-Hant": "成員資料正在整理中。",
|
||||||
|
"en": "Member profiles are being prepared.",
|
||||||
|
"jp": "メンバー情報は現在準備中です。"
|
||||||
|
},
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"id": "leadership",
|
||||||
|
"title": {
|
||||||
|
"zh-Hans": "领导团队",
|
||||||
|
"zh-Hant": "領導團隊",
|
||||||
|
"en": "Leadership",
|
||||||
|
"jp": "運営チーム"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "IF-Chan",
|
||||||
|
"role": "服主&主要出资者",
|
||||||
|
"avatar": "/icon/avatar/if-chan.png",
|
||||||
|
"url": "https://if-chan.mika.vin/",
|
||||||
|
"i18n": {
|
||||||
|
"name": {
|
||||||
|
"en": "IF-Chan",
|
||||||
|
"jp": "IF-Chan"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"zh-Hant": "服主&主要出資者",
|
||||||
|
"en": "Server Owner & Main Investor",
|
||||||
|
"jp": "サーバーオーナー&主要投資家"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MisakaAkio",
|
||||||
|
"role": "主要网络维护者",
|
||||||
|
"avatar": "https://webstatic.blueakio.com/dynamic/user/NiuBoss123.jpg",
|
||||||
|
"url": "https://www.akio.top/",
|
||||||
|
"i18n": {
|
||||||
|
"name": {
|
||||||
|
"en": "MisakaAkio",
|
||||||
|
"jp": "MisakaAkio"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"zh-Hant": "主要網絡維護者",
|
||||||
|
"en": "Main Network Maintainer",
|
||||||
|
"jp": "主要ネットワークメンテナー"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "members",
|
||||||
|
"title": {
|
||||||
|
"zh-Hans": "主要玩家",
|
||||||
|
"zh-Hant": "主要玩家",
|
||||||
|
"en": "Members",
|
||||||
|
"jp": "メンバー"
|
||||||
|
},
|
||||||
|
"members": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user