优化SEO
This commit is contained in:
@@ -12,7 +12,8 @@
|
|||||||
- 留言墙:接入 Twikoo 后可在页面中留言。
|
- 留言墙:接入 Twikoo 后可在页面中留言。
|
||||||
- 全站搜索:通过 Typesense 搜索并筛选时间线、照片、文章和同学公开资料。
|
- 全站搜索:通过 Typesense 搜索并筛选时间线、照片、文章和同学公开资料。
|
||||||
- PWA:支持添加到主屏幕,并离线访问站点外壳及已浏览的同源内容。
|
- PWA:支持添加到主屏幕,并离线访问站点外壳及已浏览的同源内容。
|
||||||
- 字体加密:生产构建会随机混淆页面中的汉字,并生成按需裁剪的 WOFF2 字体,降低静态内容被直接复制或抓取的风险。
|
- SEO:默认生产构建保留可读正文,并生成 canonical、结构化数据、`robots.txt` 与站点地图,便于搜索引擎抓取。
|
||||||
|
- 可选字体保护:需要降低静态内容被直接复制的风险时,可单独执行受保护构建。
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
@@ -34,14 +35,15 @@ npm run dev
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev # 启动开发服务器
|
npm run dev # 启动开发服务器
|
||||||
npm run build # 构建静态站点到 dist
|
npm run build # 构建适合搜索引擎抓取的静态站点到 dist
|
||||||
|
npm run build:protected # 可选:构建带汉字字体混淆的版本
|
||||||
npm run preview # 预览构建结果
|
npm run preview # 预览构建结果
|
||||||
npm run search:sync # 将公开内容同步到 Typesense
|
npm run search:sync # 将公开内容同步到 Typesense
|
||||||
```
|
```
|
||||||
|
|
||||||
`npm run build` 会在 Astro 构建完成后加密页面字体,再生成带内容版本号的 Service Worker,并预缓存页面、样式、脚本、加密字体、应用图标和首页主视觉。照片等大体积资源采用访问后缓存,第三方接口始终走网络。
|
`npm run build` 会在 Astro 构建完成后生成带内容版本号的 Service Worker,并预缓存页面、样式、脚本、应用图标和首页主视觉。照片等大体积资源采用访问后缓存,第三方接口始终走网络。
|
||||||
|
|
||||||
字体加密只作用于 `dist` 中的生产页面,不修改源码,也不会影响本地开发。每次构建默认生成新的随机映射;如需可复现产物,可设置 `FONT_ENCRYPTION_SEED`。`code`、`pre`、表单属性以及脚本动态生成的内容会保留原文,以免影响复制和交互。字体加密只能提高批量采集成本,不能替代访问控制或版权保护。
|
`npm run build:protected` 会额外混淆 `dist` 页面中的汉字并生成按需裁剪的 WOFF2 字体。该模式会降低搜索引擎理解正文的能力,不适合需要 SEO 的正式部署。字体混淆不修改源码,也不能替代访问控制或版权保护。
|
||||||
|
|
||||||
## Typesense 全站搜索
|
## Typesense 全站搜索
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import { defineConfig } from "astro/config";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://example.com",
|
site: "https://www.2024612.xyz/",
|
||||||
vite: {
|
vite: {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
+2
-1
@@ -5,7 +5,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build && node scripts/encrypt-font.mjs && node scripts/build-pwa.mjs",
|
"build": "astro build && node scripts/build-pwa.mjs",
|
||||||
|
"build:protected": "astro build && node scripts/encrypt-font.mjs && node scripts/build-pwa.mjs",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"search:key": "node --env-file-if-exists=.env --env-file-if-exists=.env.local scripts/create-typesense-search-key.mjs",
|
"search:key": "node --env-file-if-exists=.env --env-file-if-exists=.env.local scripts/create-typesense-search-key.mjs",
|
||||||
"search:sync": "node --env-file-if-exists=.env --env-file-if-exists=.env.local --import tsx scripts/sync-typesense.ts"
|
"search:sync": "node --env-file-if-exists=.env --env-file-if-exists=.env.local --import tsx scripts/sync-typesense.ts"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "/",
|
"id": "/",
|
||||||
"name": "2024届612班纪念网站",
|
"name": "高平一中612班纪念网站",
|
||||||
"short_name": "612班",
|
"short_name": "612班",
|
||||||
"description": "保存2024届612班共同走过的校园时光,也记录毕业后的我们。",
|
"description": "保存高平一中2024届612班共同走过的校园时光,也记录毕业后的我们。",
|
||||||
"lang": "zh-CN",
|
"lang": "zh-CN",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"scope": "/",
|
"scope": "/",
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
import { site } from "../data/site";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
image?: string;
|
||||||
|
noindex?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title = site.name,
|
||||||
|
description = site.description,
|
||||||
|
image = site.heroImage,
|
||||||
|
noindex = false
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
|
const documentTitle = title === site.name || title.includes(site.name)
|
||||||
|
? title
|
||||||
|
: `${title} · ${site.name}`;
|
||||||
|
const baseUrl = Astro.site ?? new URL(site.url);
|
||||||
|
const canonicalUrl = new URL(Astro.url.pathname, baseUrl);
|
||||||
|
const imageUrl = new URL(image, baseUrl);
|
||||||
|
const robots = noindex
|
||||||
|
? "noindex, nofollow"
|
||||||
|
: "index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1";
|
||||||
|
const structuredData = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@graph": [
|
||||||
|
{
|
||||||
|
"@type": "WebSite",
|
||||||
|
"@id": `${new URL("/", baseUrl)}#website`,
|
||||||
|
url: new URL("/", baseUrl).toString(),
|
||||||
|
name: site.name,
|
||||||
|
alternateName: [site.shortName, `${site.cohort}${site.shortName}`],
|
||||||
|
description: site.description,
|
||||||
|
inLanguage: "zh-CN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": `${canonicalUrl}#webpage`,
|
||||||
|
url: canonicalUrl.toString(),
|
||||||
|
name: documentTitle,
|
||||||
|
description,
|
||||||
|
isPartOf: { "@id": `${new URL("/", baseUrl)}#website` },
|
||||||
|
inLanguage: "zh-CN"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
---
|
||||||
|
|
||||||
|
<title>{documentTitle}</title>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<meta name="robots" content={robots} />
|
||||||
|
<link rel="canonical" href={canonicalUrl} />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:locale" content="zh_CN" />
|
||||||
|
<meta property="og:site_name" content={site.name} />
|
||||||
|
<meta property="og:title" content={documentTitle} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:url" content={canonicalUrl} />
|
||||||
|
<meta property="og:image" content={imageUrl} />
|
||||||
|
<meta property="og:image:alt" content={`${site.name}校园记忆`} />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content={documentTitle} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content={imageUrl} />
|
||||||
|
|
||||||
|
<script is:inline type="application/ld+json" set:html={JSON.stringify(structuredData)} />
|
||||||
@@ -907,7 +907,7 @@
|
|||||||
"displayDate": "2025.05.28",
|
"displayDate": "2025.05.28",
|
||||||
"sourceUrl": "https://mp.weixin.qq.com/s/BzwaFT3_-lTwkz1keEexZg",
|
"sourceUrl": "https://mp.weixin.qq.com/s/BzwaFT3_-lTwkz1keEexZg",
|
||||||
"cover": "/assets/article-covers/cover-325470909f3b.webp",
|
"cover": "/assets/article-covers/cover-325470909f3b.webp",
|
||||||
"excerpt": "网站群于今日起上线试运营。 共三个站点: 主站点:https://class612.bond",
|
"excerpt": "网站群于今日起上线试运营。 共三个站点: 主站点:https://www.2024612.xyz/",
|
||||||
"category": "校园通知",
|
"category": "校园通知",
|
||||||
"fileName": "网站群上线通知.md",
|
"fileName": "网站群上线通知.md",
|
||||||
"coverSourceUrl": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/RKNa5pic8piampaibDJNlrCiawqhia3LzibyNa3MNNlEAUAOqz6Fxyurqr4ic3YokNjqMPazGibr1vl2lQX67PLwVWWFlg/640?wx_fmt=jpeg"
|
"coverSourceUrl": "https://mmbiz.qpic.cn/sz_mmbiz_jpg/RKNa5pic8piampaibDJNlrCiawqhia3LzibyNa3MNNlEAUAOqz6Fxyurqr4ic3YokNjqMPazGibr1vl2lQX67PLwVWWFlg/640?wx_fmt=jpeg"
|
||||||
|
|||||||
+8
-2
@@ -1,11 +1,17 @@
|
|||||||
export const site = {
|
export const site = {
|
||||||
className: "2024届612班",
|
name: "高平一中612班",
|
||||||
|
shortName: "612班",
|
||||||
|
className: "高平一中612班",
|
||||||
|
cohort: "2024届",
|
||||||
|
url: "https://www.2024612.xyz/",
|
||||||
title: "毕业后,我们依然在场",
|
title: "毕业后,我们依然在场",
|
||||||
subtitle:
|
subtitle:
|
||||||
"把散落在相册、聊天记录和心里的瞬间放回同一个地方。这里先留给高中的三年,也留给一年后的我们。",
|
"把散落在相册、聊天记录和心里的瞬间放回同一个地方。这里先留给高中的三年,也留给一年后的我们。",
|
||||||
|
description:
|
||||||
|
"高平一中2024届612班纪念网站,收录三年时间线、班级照片、青春刊物和同学近况,保存我们共同走过的高中时光。",
|
||||||
anniversary: "2024.06 - 2026.06 · 毕业周年纪念",
|
anniversary: "2024.06 - 2026.06 · 毕业周年纪念",
|
||||||
heroImage: "/assets/campus-hero.png",
|
heroImage: "/assets/campus-hero.png",
|
||||||
footer: "2024届612班纪念网站"
|
footer: "高平一中612班纪念网站"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const contactLinks = [
|
export const contactLinks = [
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
---
|
---
|
||||||
import "../styles/global.css";
|
import "../styles/global.css";
|
||||||
import packageJson from "../../package.json";
|
import packageJson from "../../package.json";
|
||||||
|
import SeoHead from "../components/SeoHead.astro";
|
||||||
import { contactLinks, navItems, site } from "../data/site";
|
import { contactLinks, navItems, site } from "../data/site";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
image?: string;
|
||||||
|
noindex?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title = site.title } = Astro.props;
|
const {
|
||||||
|
title = site.name,
|
||||||
|
description = site.description,
|
||||||
|
image = site.heroImage,
|
||||||
|
noindex = false
|
||||||
|
} = Astro.props;
|
||||||
const buildTime = new Intl.DateTimeFormat("zh-CN", {
|
const buildTime = new Intl.DateTimeFormat("zh-CN", {
|
||||||
timeZone: "Asia/Shanghai",
|
timeZone: "Asia/Shanghai",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
@@ -26,14 +35,13 @@ const buildTime = new Intl.DateTimeFormat("zh-CN", {
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="referrer" content="no-referrer" />
|
<meta name="referrer" content="no-referrer" />
|
||||||
<meta name="description" content={site.subtitle} />
|
<SeoHead {title} {description} {image} {noindex} />
|
||||||
<meta name="theme-color" content="#156b70" />
|
<meta name="theme-color" content="#156b70" />
|
||||||
<meta name="application-name" content={site.className} />
|
<meta name="application-name" content={site.className} />
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||||
<meta name="apple-mobile-web-app-title" content={site.className} />
|
<meta name="apple-mobile-web-app-title" content={site.className} />
|
||||||
<title>{title}</title>
|
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/icons/pwa-192.png" />
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/pwa-192.png" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
---
|
---
|
||||||
import "../styles/anniversary.css";
|
import "../styles/anniversary.css";
|
||||||
|
import SeoHead from "../components/SeoHead.astro";
|
||||||
|
import { site } from "../data/site";
|
||||||
|
|
||||||
const memoryCards = [
|
const memoryCards = [
|
||||||
{
|
{
|
||||||
@@ -36,6 +38,8 @@ const gallery = [
|
|||||||
const backgroundMusic = "/assets/anniversary/background-music.mp3";
|
const backgroundMusic = "/assets/anniversary/background-music.mp3";
|
||||||
const anniversaryVideo = "/assets/anniversary/anniversary-video.mp4";
|
const anniversaryVideo = "/assets/anniversary/anniversary-video.mp4";
|
||||||
const videoPoster = "/assets/campus-hero.png";
|
const videoPoster = "/assets/campus-hero.png";
|
||||||
|
const anniversaryDescription =
|
||||||
|
"高平一中2024届612班毕业周年专题,重温三年里的时间切片、班级影像和毕业后的共同回忆。";
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@@ -43,11 +47,12 @@ const videoPoster = "/assets/campus-hero.png";
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>2024届12班周年专题</title>
|
<SeoHead
|
||||||
<meta
|
title="毕业周年专题"
|
||||||
name="description"
|
description={anniversaryDescription}
|
||||||
content="2024届12班周年专题页,收下那些毕业之后仍然清晰的瞬间。"
|
image={videoPoster}
|
||||||
/>
|
/>
|
||||||
|
<meta name="theme-color" content="#2d433c" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="anniversary-page">
|
<main class="anniversary-page">
|
||||||
@@ -55,7 +60,7 @@ const videoPoster = "/assets/campus-hero.png";
|
|||||||
<img class="hero-media" src="/assets/campus-hero.png" alt="校园记忆" />
|
<img class="hero-media" src="/assets/campus-hero.png" alt="校园记忆" />
|
||||||
<div class="hero-shade" aria-hidden="true"></div>
|
<div class="hero-shade" aria-hidden="true"></div>
|
||||||
<nav class="topbar" aria-label="周年专题导航">
|
<nav class="topbar" aria-label="周年专题导航">
|
||||||
<a class="home-link" href="/">2024届12班</a>
|
<a class="home-link" href="/">{site.name}</a>
|
||||||
<div class="topbar-links">
|
<div class="topbar-links">
|
||||||
<a href="#memory">时间切片</a>
|
<a href="#memory">时间切片</a>
|
||||||
<a href="#gallery">影像回放</a>
|
<a href="#gallery">影像回放</a>
|
||||||
@@ -151,7 +156,7 @@ const videoPoster = "/assets/campus-hero.png";
|
|||||||
<h2 id="letter-title">写给毕业后的我们</h2>
|
<h2 id="letter-title">写给毕业后的我们</h2>
|
||||||
<p>
|
<p>
|
||||||
后来的我们会遇见新的城市、新的课程、新的工作和新的自己。可只要有人再次提起那间教室、那次考试、那张合照,
|
后来的我们会遇见新的城市、新的课程、新的工作和新的自己。可只要有人再次提起那间教室、那次考试、那张合照,
|
||||||
2024届12班就会短暂地重新集合。
|
高平一中612班就会短暂地重新集合。
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
愿每一次回到这里,都不是为了停在过去,而是确认:那些一起认真生活过的日子,已经成为继续往前走的底气。
|
愿每一次回到这里,都不是为了停在过去,而是确认:那些一起认真生活过的日子,已经成为继续往前走的底气。
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ const categoryCounts = Object.fromEntries(
|
|||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="青春刊物 · 2024届612班">
|
<BaseLayout
|
||||||
|
title="青春刊物"
|
||||||
|
description="收录高平一中612班的班级日志、校园时刻、毕业典礼与节日来信,按时间重新翻阅共同的高中记忆。"
|
||||||
|
>
|
||||||
<main class="page-main publication-page">
|
<main class="page-main publication-page">
|
||||||
<section class="page-hero publication-hero">
|
<section class="page-hero publication-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ const answerCardQuestions = [
|
|||||||
let questionNumber = 0;
|
let questionNumber = 0;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${examIntro.title} · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`${examIntro.title} · ${site.className}`}
|
||||||
|
description={examIntro.description}
|
||||||
|
>
|
||||||
<main class="page-main exam-page">
|
<main class="page-main exam-page">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
@@ -141,7 +144,7 @@ let questionNumber = 0;
|
|||||||
{
|
{
|
||||||
fillQuestion?.mode === "unordered-names" ? (
|
fillQuestion?.mode === "unordered-names" ? (
|
||||||
<div class="name-fill-grid" aria-label="第 14 题姓名默写作答">
|
<div class="name-fill-grid" aria-label="第 14 题姓名默写作答">
|
||||||
{Array.from({ length: fillQuestion.blankCount }, (_, blankIndex) => (
|
{Array.from({ length: fillQuestion.blankCount ?? 1 }, (_, blankIndex) => (
|
||||||
<label>
|
<label>
|
||||||
<span>{blankIndex + 1}</span>
|
<span>{blankIndex + 1}</span>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import BaseLayout from "../../layouts/BaseLayout.astro";
|
|||||||
import { site } from "../../data/site";
|
import { site } from "../../data/site";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`成绩分析 · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`成绩分析 · ${site.className}`}
|
||||||
|
description="高平一中612班综合素质检测的本地成绩分析页。"
|
||||||
|
noindex
|
||||||
|
>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero result-hero">
|
<section class="page-hero result-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { galleryIntro, photoTopics } from "../data/photos";
|
|||||||
import { site } from "../data/site";
|
import { site } from "../data/site";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${galleryIntro.title} · ${site.className}`}>
|
<BaseLayout title={`${galleryIntro.title} · ${site.className}`} description={galleryIntro.text}>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ export function getStaticPaths() {
|
|||||||
const { topic } = Astro.props;
|
const { topic } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${topic.title} · ${galleryIntro.title} · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`${topic.title} · ${galleryIntro.title} · ${site.className}`}
|
||||||
|
description={topic.text}
|
||||||
|
image={topic.cover || site.heroImage}
|
||||||
|
>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const previewPhotoTopics = photoTopics.slice(0, 4);
|
|||||||
const previewPeople = people.slice(0, 6);
|
const previewPeople = people.slice(0, 6);
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={site.title}>
|
<BaseLayout title={site.name} description={site.description}>
|
||||||
<section
|
<section
|
||||||
id="top"
|
id="top"
|
||||||
class="hero"
|
class="hero"
|
||||||
|
|||||||
+4
-1
@@ -72,7 +72,10 @@ const displayCities = layoutCityMarkers();
|
|||||||
const isDenseMap = displayCities.length > 12;
|
const isDenseMap = displayCities.length > 12;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`班级地图 · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`班级地图 · ${site.className}`}
|
||||||
|
description="查看高平一中612班同学毕业后的公开城市分布,记录我们从同一间教室走向不同地方的轨迹。"
|
||||||
|
>
|
||||||
<main class="page-main map-page">
|
<main class="page-main map-page">
|
||||||
<section class="page-hero map-hero">
|
<section class="page-hero map-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { site } from "../data/site";
|
|||||||
import twikooThemeUrl from "../styles/twikoo2.css?url";
|
import twikooThemeUrl from "../styles/twikoo2.css?url";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${messagesIntro.title} · ${site.className}`}>
|
<BaseLayout title={`${messagesIntro.title} · ${site.className}`} description={messagesIntro.text}>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
|||||||
import { site } from "../data/site";
|
import { site } from "../data/site";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`暂时离线 · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`暂时离线 · ${site.className}`}
|
||||||
|
description="高平一中612班纪念网站的离线提示页。"
|
||||||
|
noindex
|
||||||
|
>
|
||||||
<main class="offline-page">
|
<main class="offline-page">
|
||||||
<section class="offline-card" aria-labelledby="offline-title">
|
<section class="offline-card" aria-labelledby="offline-title">
|
||||||
<p class="offline-mark" aria-hidden="true">612</p>
|
<p class="offline-mark" aria-hidden="true">612</p>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { people, peopleIntro } from "../data/people";
|
|||||||
import { site } from "../data/site";
|
import { site } from "../data/site";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${peopleIntro.title} · ${site.className}`}>
|
<BaseLayout title={`${peopleIntro.title} · ${site.className}`} description={peopleIntro.text}>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ export function getStaticPaths() {
|
|||||||
const { person } = Astro.props;
|
const { person } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${person.name} · ${peopleIntro.title} · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`${person.name} · ${peopleIntro.title} · ${site.className}`}
|
||||||
|
description={`${person.name}的近况:${person.text}`}
|
||||||
|
image={person.photo || site.heroImage}
|
||||||
|
>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero person-hero">
|
<section class="page-hero person-hero">
|
||||||
<div class="section-inner person-hero-inner">
|
<div class="section-inner person-hero-inner">
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { APIRoute } from "astro";
|
||||||
|
import { site as siteData } from "../data/site";
|
||||||
|
|
||||||
|
export const prerender = true;
|
||||||
|
|
||||||
|
export const GET: APIRoute = ({ site }) => {
|
||||||
|
const baseUrl = site ?? new URL(siteData.url);
|
||||||
|
const sitemapUrl = new URL("/sitemap.xml", baseUrl);
|
||||||
|
const body = [
|
||||||
|
"User-agent: *",
|
||||||
|
"Allow: /",
|
||||||
|
`Sitemap: ${sitemapUrl}`,
|
||||||
|
""
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
return new Response(body, {
|
||||||
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -13,7 +13,11 @@ const typesenseConfig = {
|
|||||||
const isConfigured = Boolean(typesenseConfig.host && typesenseConfig.apiKey);
|
const isConfigured = Boolean(typesenseConfig.host && typesenseConfig.apiKey);
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`全站搜索 · ${site.className}`}>
|
<BaseLayout
|
||||||
|
title={`全站搜索 · ${site.className}`}
|
||||||
|
description="搜索高平一中612班的时间线、照片、青春刊物和同学近况。"
|
||||||
|
noindex
|
||||||
|
>
|
||||||
<main>
|
<main>
|
||||||
<section class="page-hero search-hero">
|
<section class="page-hero search-hero">
|
||||||
<div class="section-heading">
|
<div class="section-heading">
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import type { APIRoute } from "astro";
|
||||||
|
import { people } from "../data/people";
|
||||||
|
import { photoTopics } from "../data/photos";
|
||||||
|
import { site as siteData } from "../data/site";
|
||||||
|
|
||||||
|
export const prerender = true;
|
||||||
|
|
||||||
|
const staticRoutes = [
|
||||||
|
"/",
|
||||||
|
"/timeline/",
|
||||||
|
"/gallery/",
|
||||||
|
"/articles/",
|
||||||
|
"/people/",
|
||||||
|
"/map/",
|
||||||
|
"/exam/",
|
||||||
|
"/messages/",
|
||||||
|
"/anniversary/"
|
||||||
|
];
|
||||||
|
|
||||||
|
const escapeXml = (value: string) =>
|
||||||
|
value
|
||||||
|
.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<")
|
||||||
|
.replaceAll(">", ">")
|
||||||
|
.replaceAll('"', """)
|
||||||
|
.replaceAll("'", "'");
|
||||||
|
|
||||||
|
export const GET: APIRoute = ({ site }) => {
|
||||||
|
const baseUrl = site ?? new URL(siteData.url);
|
||||||
|
const routes = [
|
||||||
|
...staticRoutes,
|
||||||
|
...photoTopics.map((topic) => `/gallery/${topic.slug}/`),
|
||||||
|
...people.map((person) => `/people/${person.slug}/`)
|
||||||
|
];
|
||||||
|
const urls = [...new Set(routes)]
|
||||||
|
.map((route) => ` <url><loc>${escapeXml(new URL(route, baseUrl).toString())}</loc></url>`)
|
||||||
|
.join("\n");
|
||||||
|
const body = [
|
||||||
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||||
|
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
||||||
|
urls,
|
||||||
|
"</urlset>",
|
||||||
|
""
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
return new Response(body, {
|
||||||
|
headers: { "Content-Type": "application/xml; charset=utf-8" }
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -4,7 +4,7 @@ import { site } from "../data/site";
|
|||||||
import { timeline, timelineIntro } from "../data/timeline";
|
import { timeline, timelineIntro } from "../data/timeline";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={`${timelineIntro.title} · ${site.className}`}>
|
<BaseLayout title={`${timelineIntro.title} · ${site.className}`} description={timelineIntro.text}>
|
||||||
<main class="page-main">
|
<main class="page-main">
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
<div class="section-inner">
|
<div class="section-inner">
|
||||||
|
|||||||
Reference in New Issue
Block a user