@@ -207,6 +207,40 @@ const backgroundStyle = pageBackgroundConfig.enable
207207 <ConfigCarrier ></ConfigCarrier >
208208 <slot />
209209
210+ <!-- 安全跳转提示弹窗 -->
211+ <div id =" safe-redirect-modal" class =" fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm hidden" >
212+ <div class =" bg-[var(--card-bg)] rounded-lg shadow-lg p-6 max-w-md w-full mx-4 border border-[var(--line-divider)] relative" >
213+ <!-- 关闭按钮 -->
214+ <button id =" redirect-close" class =" absolute top-3 right-3 text-[var(--text-75)] hover:text-[var(--text-90)] transition-colors" >
215+ <svg xmlns =" http://www.w3.org/2000/svg" width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
216+ <line x1 =" 18" y1 =" 6" x2 =" 6" y2 =" 18" ></line >
217+ <line x1 =" 6" y1 =" 6" x2 =" 18" y2 =" 18" ></line >
218+ </svg >
219+ </button >
220+ <div class =" flex items-center gap-2 mb-4" >
221+ <div class =" text-green-500" >
222+ <svg xmlns =" http://www.w3.org/2000/svg" width =" 24" height =" 24" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
223+ <polyline points =" 20 6 9 17 4 12" ></polyline >
224+ </svg >
225+ </div >
226+ <h3 class =" text-lg font-bold text-[var(--text-90)]" >安全跳转提示</h3 >
227+ </div >
228+ <p class =" text-[var(--text-75)] mb-4" >您即将访问外部链接:</p >
229+ <div class =" bg-[var(--btn-card-bg-hover)] rounded-md p-3 mb-4 break-all" >
230+ <span id =" external-link-url" class =" text-sm" ></span >
231+ </div >
232+ <div class =" bg-green-50 border border-green-200 text-green-800 rounded-md p-3 mb-4 flex items-center gap-2" >
233+ <svg xmlns =" http://www.w3.org/2000/svg" width =" 16" height =" 16" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
234+ <polyline points =" 20 6 9 17 4 12" ></polyline >
235+ </svg >
236+ <span class =" text-sm" >外部链接使用 HTTPS 加密连接</span >
237+ </div >
238+ <button id =" redirect-confirm" class =" w-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-md transition-colors" >
239+ 立即前往
240+ </button >
241+ </div >
242+ </div >
243+
210244 <!-- increase the page height during page transition to prevent the scrolling animation from jumping -->
211245 <div id =" page-height-extend" class =" hidden h-[300vh]" ></div >
212246 </body >
@@ -598,6 +632,110 @@ window.onresize = () => {
598632 document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
599633}
600634
635+ // 安全跳转提示功能
636+ function initSafeRedirect() {
637+ const modal = document.getElementById('safe-redirect-modal');
638+ const urlDisplay = document.getElementById('external-link-url');
639+ const confirmBtn = document.getElementById('redirect-confirm');
640+ let redirectUrl = '';
641+
642+ // 关闭弹窗
643+ function closeModal() {
644+ if (modal) {
645+ modal.classList.add('hidden');
646+ }
647+ redirectUrl = '';
648+ }
649+
650+ // 确认跳转
651+ function confirmRedirect() {
652+ if (redirectUrl) {
653+ window.open(redirectUrl, '_blank');
654+ closeModal();
655+ }
656+ }
657+
658+ // 点击确认按钮
659+ if (confirmBtn) {
660+ confirmBtn.addEventListener('click', confirmRedirect);
661+ }
662+
663+ // 关闭按钮点击
664+ const closeBtn = document.getElementById('redirect-close');
665+ if (closeBtn) {
666+ closeBtn.addEventListener('click', closeModal);
667+ }
668+
669+ // 点击弹窗外部关闭
670+ if (modal) {
671+ modal.addEventListener('click', (e) => {
672+ if (e.target === modal) {
673+ closeModal();
674+ }
675+ });
676+ }
677+
678+ // 处理链接点击 - 使用捕获阶段确保最先执行
679+ function handleLinkClick(e: Event) {
680+ const target = e.target as HTMLElement;
681+ const anchor = target.closest('a');
682+ if (!anchor) return;
683+
684+ const href = anchor.getAttribute('href');
685+ if (!href || href.startsWith('#') || href.startsWith('javascript:')) return;
686+
687+ // 相对路径是内部链接
688+ if (href.startsWith('/')) {
689+ return;
690+ }
691+
692+ // 检查是否为外部链接
693+ try {
694+ const url = new URL(href, window.location.origin);
695+ const hostname = url.hostname;
696+ const isInternal = hostname === 'allen2030.com' || hostname.endsWith('.allen2030.com');
697+
698+ if (!isInternal && url.protocol.startsWith('http')) {
699+ // 立即阻止默认行为和传播
700+ e.preventDefault();
701+ e.stopPropagation();
702+ e.stopImmediatePropagation();
703+
704+ redirectUrl = href;
705+
706+ if (urlDisplay) {
707+ urlDisplay.textContent = href;
708+ }
709+
710+ if (modal) {
711+ modal.classList.remove('hidden');
712+ }
713+ }
714+ } catch (error) {
715+ console.error('Invalid URL:', error);
716+ }
717+ }
718+
719+ // 使用捕获阶段监听,确保在其他监听器之前执行
720+ document.addEventListener('click', handleLinkClick, true);
721+ }
722+
723+ // 确保DOM加载完成后初始化
724+ if (document.readyState === 'loading') {
725+ document.addEventListener('DOMContentLoaded', initSafeRedirect);
726+ } else {
727+ initSafeRedirect();
728+ }
729+
730+ // 页面切换时重新初始化
731+ if (window?.swup?.hooks) {
732+ window.swup.hooks.on('page:view', initSafeRedirect);
733+ } else {
734+ document.addEventListener('swup:enable', () => {
735+ window.swup.hooks.on('page:view', initSafeRedirect);
736+ });
737+ }
738+
601739</script >
602740
603741<script >
0 commit comments