From 8c10beb26289156c2c094eceac0ae46bb59e4eca Mon Sep 17 00:00:00 2001 From: fishcpy Date: Wed, 21 Oct 2026 19:03:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9A=82=E6=97=B6=E7=A6=81=E6=AD=A2?= =?UTF-8?q?=E4=B8=AD=E5=9B=BD=E5=A4=A7=E9=99=86=E7=94=A8=E6=88=B7=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vitepress/data/locales/en.ts | 5 ++ .vitepress/data/locales/index.ts | 4 ++ .vitepress/data/locales/zh.ts | 5 ++ .../theme/components/HistoryReleases.vue | 30 ++++++++- .vitepress/theme/components/MirrorGrid.vue | 43 +++++++++--- .vitepress/theme/components/RegionGate.vue | 32 +++++++++ .../theme/composables/useRegionGuard.ts | 66 +++++++++++++++++++ .vitepress/theme/index.ts | 2 + src/download.md | 4 ++ src/en/download.md | 4 ++ 10 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 .vitepress/theme/components/RegionGate.vue create mode 100644 .vitepress/theme/composables/useRegionGuard.ts diff --git a/.vitepress/data/locales/en.ts b/.vitepress/data/locales/en.ts index ce7bf7b..1209aa4 100644 --- a/.vitepress/data/locales/en.ts +++ b/.vitepress/data/locales/en.ts @@ -68,6 +68,11 @@ export const en: LocaleMessages = { alsoDownload: 'Or download directly from:', }, + region: { + blocked: 'Downloads are not available in your country or region', + hint: 'Sorry, this content is currently unavailable in your region.', + }, + history: { title: 'Historical Releases', subtitle: 'Select a release, choose source, then download the installer.', diff --git a/.vitepress/data/locales/index.ts b/.vitepress/data/locales/index.ts index 338e0b9..c937c65 100644 --- a/.vitepress/data/locales/index.ts +++ b/.vitepress/data/locales/index.ts @@ -62,6 +62,10 @@ export interface LocaleMessages { historicalBtn: string; alsoDownload: string; }; + region: { + blocked: string; + hint: string; + }; history: { title: string; subtitle: string; diff --git a/.vitepress/data/locales/zh.ts b/.vitepress/data/locales/zh.ts index 9b8b18d..7047b9b 100644 --- a/.vitepress/data/locales/zh.ts +++ b/.vitepress/data/locales/zh.ts @@ -69,6 +69,11 @@ export const zh: LocaleMessages = { alsoDownload: '你也可以直接在下方的链接中下载', }, + region: { + blocked: '您所在的国家或地区暂不支持下载', + hint: '很抱歉,该内容目前无法在您的所在地区提供。', + }, + history: { title: '历史版本下载', subtitle: '选择版本并切换下载源后下载对应安装包。', diff --git a/.vitepress/theme/components/HistoryReleases.vue b/.vitepress/theme/components/HistoryReleases.vue index b9c7619..e263bdc 100644 --- a/.vitepress/theme/components/HistoryReleases.vue +++ b/.vitepress/theme/components/HistoryReleases.vue @@ -2,6 +2,7 @@ import { useData } from 'vitepress'; import { computed, onBeforeUnmount, onMounted, ref } from 'vue'; import { getMessages, type Lang } from '../../data/i18n'; +import { useRegionGuard } from '../composables/useRegionGuard'; interface ReleaseAsset { name: string; @@ -54,6 +55,13 @@ const messages = computed(() => { return getMessages(currentLang).history; }); +// biome-ignore lint/correctness/noUnusedVariables: used in Vue template +const { + blocked, + message: blockedMessage, + hint: blockedHint, +} = useRegionGuard(); + function formatDate(value: string) { if (!value) return ''; const date = new Date(value); @@ -282,7 +290,11 @@ function handleOutsideClick(event: MouseEvent) { -
+
+
{{ blockedMessage }}
+
{{ blockedHint }}
+
+
+import { useRegionGuard } from '../composables/useRegionGuard'; + interface MirrorItem { href: string; label: string; @@ -7,18 +9,27 @@ interface MirrorItem { defineProps<{ items: MirrorItem[]; }>(); + +// biome-ignore lint/correctness/noUnusedVariables: used in Vue template +const { blocked, message, hint } = useRegionGuard(); @@ -48,6 +59,22 @@ defineProps<{ color: var(--vp-c-brand-1); } +.mirror-blocked { + grid-column: 1 / -1; + padding: 0.75rem 1rem; + background: var(--vp-c-bg-soft); + border: 1px dashed var(--vp-c-divider); + border-radius: 8px; + text-align: center; + color: var(--vp-c-text-2); +} + +.mirror-blocked-hint { + margin-top: 0.35rem; + font-size: 0.875rem; + color: var(--vp-c-text-3); +} + @media (max-width: 768px) { .mirror-grid { grid-template-columns: 1fr; diff --git a/.vitepress/theme/components/RegionGate.vue b/.vitepress/theme/components/RegionGate.vue new file mode 100644 index 0000000..c0ef2b3 --- /dev/null +++ b/.vitepress/theme/components/RegionGate.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/.vitepress/theme/composables/useRegionGuard.ts b/.vitepress/theme/composables/useRegionGuard.ts new file mode 100644 index 0000000..25abbae --- /dev/null +++ b/.vitepress/theme/composables/useRegionGuard.ts @@ -0,0 +1,66 @@ +import { useData } from 'vitepress'; +import { computed, onMounted, ref } from 'vue'; +import { getMessages, type Lang } from '../../data/i18n'; + +const TRACE_URL = 'https://www.cloudflare.com/cdn-cgi/trace'; +const TRACE_TIMEOUT = 5000; + +const CHINA_TIME_ZONES = new Set([ + 'Asia/Shanghai', + 'Asia/Chongqing', + 'Asia/Harbin', + 'Asia/Urumqi', + 'Asia/Kashgar', + 'PRC', +]); + +const blocked = ref(false); +let checkStarted = false; + +function isChinaTimeZone(): boolean { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? ''; + return CHINA_TIME_ZONES.has(timeZone); +} + +async function requestTraceLocation(): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), TRACE_TIMEOUT); + try { + const response = await fetch(TRACE_URL, { + cache: 'no-store', + signal: controller.signal, + }); + if (!response.ok) return ''; + const text = await response.text(); + return (/(?:^|\n)loc=([^\s\r]+)/.exec(text)?.[1] ?? '').toUpperCase(); + } catch { + return ''; + } finally { + clearTimeout(timer); + } +} + +function startRegionCheck() { + if (checkStarted) return; + checkStarted = true; + if (isChinaTimeZone()) { + blocked.value = true; + return; + } + void requestTraceLocation().then((loc) => { + if (loc === 'CN') blocked.value = true; + }); +} + +export function useRegionGuard() { + const { lang } = useData(); + onMounted(startRegionCheck); + const region = computed( + () => getMessages((lang.value || 'zh-CN') as Lang).region, + ); + return { + blocked, + message: computed(() => region.value.blocked), + hint: computed(() => region.value.hint), + }; +} diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index a206f4b..4d138e5 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -12,6 +12,7 @@ import HistoryReleases from './components/HistoryReleases.vue'; import HomeUnderline from './components/HomeUnderline.vue'; import ImageGallery from './components/ImageGallery.vue'; import MirrorGrid from './components/MirrorGrid.vue'; +import RegionGate from './components/RegionGate.vue'; import SiteFooter from './components/SiteFooter.vue'; import VideoSection from './components/VideoSection.vue'; @@ -36,6 +37,7 @@ export default { app.component('Copy', CopyText); // 注册自定义组件 app.component('MirrorGrid', MirrorGrid); + app.component('RegionGate', RegionGate); app.component('AboutPage', AboutPage); app.component('HistoryReleases', HistoryReleases); app.component('VideoSection', VideoSection); diff --git a/src/download.md b/src/download.md index ca2ca42..f561c36 100644 --- a/src/download.md +++ b/src/download.md @@ -32,8 +32,12 @@ keywords: PCL下载,Minecraft启动器下载,PCL社区版下载,启动器下载 如果你有 Mirror 酱 CDK 可以直接使用此高速下载源 + + [Mirror 酱下载](https://mirrorchyan.com/zh/projects?rid=PCL2-CE&source=pcl2ce-web){.download-btn} + + 你也可以直接在下方的链接中下载 ## 下载链接 diff --git a/src/en/download.md b/src/en/download.md index 275e077..9ae761d 100644 --- a/src/en/download.md +++ b/src/en/download.md @@ -32,8 +32,12 @@ By clicking any download button below and running the program, you agree to our If you have a MirrorChyan CDK, you can use this high-speed download source directly + + [MirrorChyan Download](https://mirrorchyan.com/en/projects?rid=PCL2-CE&source=pcl2ce-gh-release){.download-btn} + + You can also download directly from the links below ## Download Links