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.
|
Before Width: | Height: | Size: 7.5 MiB After Width: | Height: | Size: 1.5 MiB |
+3
-2
@@ -55,7 +55,7 @@ a { color: inherit; text-decoration: none; }
|
|||||||
.home-collage { position: relative; height: 648px; margin-top: 50px; }.home-collage-tile { position: absolute; border-radius: 13px; background: #d9d9d9; }.home-collage-wide { top: 0; left: 0; width: 41.4%; height: 359px; }.home-collage-medium { top: 0; left: 42.57%; width: 28.87%; height: 359px; }.home-collage-small { top: 376px; left: 27.28%; width: 44.3%; height: 272px; }.home-collage-bottom { top: 376px; left: 0; width: 25.83%; height: 272px; }.home-collage-tall { top: 0; right: 0; width: 27.1%; height: 648px; }
|
.home-collage { position: relative; height: 648px; margin-top: 50px; }.home-collage-tile { position: absolute; border-radius: 13px; background: #d9d9d9; }.home-collage-wide { top: 0; left: 0; width: 41.4%; height: 359px; }.home-collage-medium { top: 0; left: 42.57%; width: 28.87%; height: 359px; }.home-collage-small { top: 376px; left: 27.28%; width: 44.3%; height: 272px; }.home-collage-bottom { top: 376px; left: 0; width: 25.83%; height: 272px; }.home-collage-tall { top: 0; right: 0; width: 27.1%; height: 648px; }
|
||||||
|
|
||||||
.news-page { padding: 30px 0 60px; }
|
.news-page { padding: 30px 0 60px; }
|
||||||
.friendly-links-hero { background: var(--policy-hero-image) center 26.71% / 100% auto no-repeat; }
|
.friendly-links-hero { background: var(--policy-hero-image) center 28.2% / 100% auto no-repeat; }
|
||||||
.friendly-links-page-inner { gap: 50px; }
|
.friendly-links-page-inner { gap: 50px; }
|
||||||
.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; }
|
||||||
@@ -65,7 +65,8 @@ a { color: inherit; text-decoration: none; }
|
|||||||
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); }
|
||||||
.friendly-link-card-content { display: flex; flex: 1; flex-direction: column; align-items: center; justify-content: center; gap: 16.42px; padding: 32.83px 10.94px; }
|
.friendly-link-card-content { display: flex; flex: 1; flex-direction: column; align-items: center; justify-content: center; gap: 16.42px; padding: 32.83px 10.94px; }
|
||||||
.friendly-link-card-heading { display: flex; align-items: center; justify-content: center; gap: 16px; min-width: 0; font-size: 27.36px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
.friendly-link-card-heading { display: flex; align-items: center; justify-content: center; gap: 16px; min-width: 0; font-size: 27.36px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
||||||
.friendly-link-card-heading img { width: 65.67px; height: 65.67px; overflow: hidden; border-radius: 8px; object-fit: cover; }
|
.friendly-link-card-logo { display: flex; width: 65.67px; height: 65.67px; flex: 0 0 65.67px; align-items: center; justify-content: center; overflow: hidden; border-radius: 10.944px; background: #fff; }
|
||||||
|
.friendly-link-card-logo img { display: block; width: 65.67px; height: 65.67px; object-fit: cover; }
|
||||||
.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; }
|
||||||
|
|||||||
@@ -3,18 +3,69 @@ import type { CSSProperties } from "react";
|
|||||||
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
||||||
import { getLocaleMessages, type Locale } from "@/lib/i18n";
|
import { getLocaleMessages, type Locale } from "@/lib/i18n";
|
||||||
|
|
||||||
type LinkEntry = { name: string; description: string; logo: string; url?: string; archived?: boolean };
|
type LocalizedText = Partial<Record<Locale, string>>;
|
||||||
type LocalizedText = Record<Locale, string>;
|
type LinkEntry = {
|
||||||
type LinkSection = { id: string; title: LocalizedText; links: LinkEntry[] };
|
name: string;
|
||||||
|
description: string;
|
||||||
|
logo: string;
|
||||||
|
url?: string;
|
||||||
|
archived?: boolean;
|
||||||
|
i18n?: { name?: LocalizedText; description?: LocalizedText };
|
||||||
|
};
|
||||||
|
type LinkSection = { id: string; title: Record<Locale, string>; links: LinkEntry[] };
|
||||||
|
|
||||||
const linkSections = links.sections as LinkSection[];
|
const linkSections = links.sections as LinkSection[];
|
||||||
|
|
||||||
function FriendlyLinkCard({ link }: { link: LinkEntry }) {
|
function localized(value: string, translations: LocalizedText | undefined, locale: Locale) {
|
||||||
const content = <><span className="friendly-link-card-content"><span className="friendly-link-card-heading"><img src={link.logo} alt="" width={48} height={48} /><span>{link.name}</span></span><span className="friendly-link-card-description">{link.description}</span></span>{link.archived && <span className="friendly-link-card-archive">已归档</span>}</>;
|
return translations?.[locale] ?? value;
|
||||||
return link.url ? <a className="friendly-link-card" href={link.url} target="_blank" rel="noreferrer" aria-label={`访问 ${link.name}`}>{content}</a> : <article className="friendly-link-card">{content}</article>;
|
}
|
||||||
|
|
||||||
|
function FriendlyLinkCard({ link, locale, visitLabel }: { link: LinkEntry; locale: Locale; visitLabel: string }) {
|
||||||
|
const name = localized(link.name, link.i18n?.name, locale);
|
||||||
|
const description = localized(link.description, link.i18n?.description, locale);
|
||||||
|
const content = (
|
||||||
|
<>
|
||||||
|
<span className="friendly-link-card-content">
|
||||||
|
<span className="friendly-link-card-heading">
|
||||||
|
<span className="friendly-link-card-logo"><img src={link.logo} alt="" width={66} height={66} /></span>
|
||||||
|
<span>{name}</span>
|
||||||
|
</span>
|
||||||
|
<span className="friendly-link-card-description">{description}</span>
|
||||||
|
</span>
|
||||||
|
{link.archived && <span className="friendly-link-card-archive">已归档</span>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return link.url ? (
|
||||||
|
<a className="friendly-link-card" href={link.url} target="_blank" rel="noreferrer" aria-label={`${visitLabel} ${name}`}>
|
||||||
|
{content}
|
||||||
|
</a>
|
||||||
|
) : <article className="friendly-link-card">{content}</article>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FriendlyLinksPage({ locale }: { locale: Locale }) {
|
export function FriendlyLinksPage({ locale }: { locale: Locale }) {
|
||||||
const messages = getLocaleMessages(locale);
|
const messages = getLocaleMessages(locale);
|
||||||
const heroStyle = { "--policy-hero-image": 'url("/links/friendly-links-hero.png")' } as CSSProperties;
|
const heroStyle = { "--policy-hero-image": 'url("/links/friendly-links-hero.png")' } as CSSProperties;
|
||||||
return <main className="policy-page friendly-links-page"><div className="policy-page-inner friendly-links-page-inner"><section className="policy-hero friendly-links-hero" aria-labelledby="friendly-links-title" style={heroStyle}><div className="policy-hero-inner"><h1 id="friendly-links-title">{messages.footer.legal[2]}</h1></div></section><div className="friendly-links-content">{linkSections.map((section) => <section className="friendly-links-section" key={section.id} aria-labelledby={`friendly-links-${section.id}`}><h2 id={`friendly-links-${section.id}`}>{section.title[locale]}</h2><div className="friendly-links-grid">{section.links.map((link, index) => <FriendlyLinkCard key={`${section.id}-${link.name}-${index}`} link={link} />)}</div></section>)}</div></div><div className="page-bottom-nav"><div className="page-bottom-nav-inner"><PageBreadcrumb items={[{ label: messages.common.home, href: "/" }, { label: messages.footer.legal[2] }]} /></div></div></main>;
|
const visitLabels: Record<Locale, string> = { "zh-Hans": "访问", "zh-Hant": "造訪", en: "Visit", jp: "訪問" };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="policy-page friendly-links-page">
|
||||||
|
<div className="policy-page-inner friendly-links-page-inner">
|
||||||
|
<section className="policy-hero friendly-links-hero" aria-labelledby="friendly-links-title" style={heroStyle}>
|
||||||
|
<div className="policy-hero-inner"><h1 id="friendly-links-title">{messages.footer.legal[2]}</h1></div>
|
||||||
|
</section>
|
||||||
|
<div className="friendly-links-content">
|
||||||
|
{linkSections.map((section) => (
|
||||||
|
<section className="friendly-links-section" key={section.id} aria-labelledby={`friendly-links-${section.id}`}>
|
||||||
|
<h2 id={`friendly-links-${section.id}`}>{section.title[locale]}</h2>
|
||||||
|
<div className="friendly-links-grid">
|
||||||
|
{section.links.map((link, index) => <FriendlyLinkCard key={`${section.id}-${link.name}-${index}`} link={link} locale={locale} visitLabel={visitLabels[locale]} />)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="page-bottom-nav"><div className="page-bottom-nav-inner"><PageBreadcrumb items={[{ label: messages.common.home, href: "/" }, { label: messages.footer.legal[2] }]} /></div></div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-4
@@ -4,9 +4,27 @@
|
|||||||
"id": "services",
|
"id": "services",
|
||||||
"title": { "zh-Hans": "服务提供", "zh-Hant": "服務提供", "en": "Service providers", "jp": "サービス提供" },
|
"title": { "zh-Hans": "服务提供", "zh-Hant": "服務提供", "en": "Service providers", "jp": "サービス提供" },
|
||||||
"links": [
|
"links": [
|
||||||
{ "name": "德威云", "description": "Minecraft 服务器提供方", "logo": "/links/fetarute.png", "url": "https://www.deviy.cn/" },
|
{
|
||||||
{ "name": "腾讯云", "description": "CDN、对象存储 提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
"name": "德威云",
|
||||||
{ "name": "蓝色秋生", "description": "网页服务提供方,对象存储、CDN 维护方", "logo": "/links/blueakio.svg", "url": "https://www.blueakio.com/" }
|
"description": "Minecraft 服务器提供方",
|
||||||
|
"logo": "/links/fetarute.png",
|
||||||
|
"url": "https://www.deviy.cn/",
|
||||||
|
"i18n": { "description": { "en": "Minecraft server provider", "jp": "Minecraft サーバー提供元" } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "腾讯云",
|
||||||
|
"description": "CDN、对象存储提供方",
|
||||||
|
"logo": "/links/fetarute.png",
|
||||||
|
"url": "https://cloud.tencent.com/",
|
||||||
|
"i18n": { "description": { "en": "CDN and object storage provider", "jp": "CDN・オブジェクトストレージ提供元" } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "蓝色秋生",
|
||||||
|
"description": "网页服务提供方,对象存储、CDN 维护方",
|
||||||
|
"logo": "/links/blueakio.svg",
|
||||||
|
"url": "https://www.blueakio.com/",
|
||||||
|
"i18n": { "description": { "en": "Web service, object storage, and CDN provider", "jp": "ウェブサービス、オブジェクトストレージ、CDN の提供元" } }
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -16,7 +34,7 @@
|
|||||||
{ "name": "德威云", "description": "Minecraft 服务器提供方", "logo": "/links/fetarute.png", "url": "https://deviy.cn/" },
|
{ "name": "德威云", "description": "Minecraft 服务器提供方", "logo": "/links/fetarute.png", "url": "https://deviy.cn/" },
|
||||||
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
||||||
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
||||||
{ "name": "蓝色秋生", "description": "网页服务提供方,对象存储、CDN 维护方", "logo": "/links/fetarute.png", "url": "https://liqiusheng.cn/" },
|
{ "name": "蓝色秋生", "description": "网页服务提供方,对象存储、CDN 维护方", "logo": "/links/blueakio.svg", "url": "https://liqiusheng.cn/" },
|
||||||
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" },
|
||||||
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" }
|
{ "name": "腾讯云", "description": "CDN、对象存储提供方", "logo": "/links/fetarute.png", "url": "https://cloud.tencent.com/" }
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user