打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

微件:主页脚本

来自EaseCation Wiki

<script>

(function() {

   'use strict';
   
   // 配置
   const CONFIG = {
       iframeSrc: window.ECWikiAIConfig?.src || 'https://service.wiki.easecation.net/ai',
       fabPosition: window.ECWikiAIConfig?.position || { bottom: '32px', right: '32px' },
       mobileBreakpoint: 640,
       debounceDelay: 150
   };
   
   // 防止重复初始化
   if (document.getElementById('ec-ai-fab')) {
       console.log('[EC Wiki AI] 已初始化,跳过');
       return;
   }
   
   // AI 图标 SVG
   const AI_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="width:20px;height:20px;vertical-align:middle;"><path d="M15.9991 2.99995C19.3131 2.99995 22 5.69516 22 8.9941V21H8.00099C4.68693 21 2.00001 18.3048 2.00001 15.0058V11H4.00001V15.0058C4.00001 17.2042 5.79547 19 8.00099 19H20V8.9941C20 6.79576 18.2046 4.99995 15.9991 4.99995H10V2.99995H15.9991ZM10 13H8.00002V11H10V13ZM16 13H14V11H16V13ZM3.52931 1.31928C3.70584 0.89349 4.29418 0.893492 4.47071 1.31928L4.72364 1.93061C5.15555 2.9734 5.96155 3.80612 6.97462 4.25679L7.6924 4.57612C8.10268 4.75894 8.10263 5.35615 7.6924 5.53902L6.93263 5.87691C5.94498 6.31619 5.15339 7.11941 4.71388 8.12789L4.46681 8.69332C4.28636 9.10745 3.71366 9.10745 3.53321 8.69332L3.28614 8.12789C2.84661 7.11942 2.05506 6.31619 1.06739 5.87691L0.307623 5.53902C-0.102517 5.35615 -0.102565 4.75894 0.307623 4.57612L1.0254 4.25679C2.03845 3.80613 2.84446 2.97343 3.27638 1.93061L3.52931 1.31928Z"></path></svg>`;
   
   // 判断是否为移动端
   const isMobile = () => window.innerWidth <= CONFIG.mobileBreakpoint;
   
   // 防抖函数
   const debounce = (fn, delay) => {
       let timer = null;
       return function(...args) {
           clearTimeout(timer);
           timer = setTimeout(() => fn.apply(this, args), delay);
       };
   };
   
   // 存储事件处理器引用,用于清理
   const eventHandlers = {};
   
   // 创建样式
   const style = document.createElement('style');
   style.textContent = `
       #ec-ai-fab {
           position: fixed;
           bottom: ${CONFIG.fabPosition.bottom};
           right: ${CONFIG.fabPosition.right};
           width: 48px;
           height: 48px;
           padding: 0;
           background: #206588;
           color: #fff;
           border: none;
           border-radius: 14px;
           cursor: pointer;
           font-size: 14px;
           font-weight: 600;
           font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
           box-shadow: 0 2px 8px rgba(0,0,0,0.15);
           z-index: 10000;
           display: flex;
           align-items: center;
           justify-content: center;
           transition: opacity 0.4s ease, transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
           user-select: none;
           opacity: 0;
       }
       
       #ec-ai-fab.visible {
           opacity: 1;
       }
       
       #ec-ai-fab:hover {
           background: #0090e0;
           transform: translateY(-2px);
           box-shadow: 0 4px 12px rgba(0,0,0,0.2);
       }
       
       #ec-ai-fab:active {
           transform: translateY(0);
       }
       
       /* 移动端悬浮按钮适配 */
       @media (max-width: ${CONFIG.mobileBreakpoint}px) {
           #ec-ai-fab {
               bottom: 20px;
               right: 20px;
               width: 40px;
               height: 40px;
               padding: 0;
           }
       }
       
       /* 遮罩层 */
       #ec-ai-overlay {
           position: fixed;
           top: 0;
           left: 0;
           width: 100%;
           height: 100%;
           background: rgba(0,0,0,0.4);
           z-index: 10001;
           opacity: 0;
           visibility: hidden;
           transition: all 0.3s ease;
       }
       
       #ec-ai-overlay.active {
           opacity: 1;
           visibility: visible;
       }
       
       /* iframe 容器 - 桌面端右侧滑出 */
       #ec-ai-sidebar {
           position: fixed;
           top: 0;
           right: 0;
           width: 500px;
           height: 100vh;
           background: #fff;
           border-left: 1px solid #e1e4e8;
           box-shadow: -2px 0 8px rgba(0,0,0,0.08);
           z-index: 10002;
           display: flex;
           flex-direction: column;
           transform: translateX(100%);
           opacity: 0;
           transition: transform 0.3s ease, opacity 0.3s ease;
       }
       
       #ec-ai-sidebar.mobile {
           width: 100%;
           border-left: none;
       }
       
       #ec-ai-sidebar.active {
           transform: translateX(0);
           opacity: 1;
       }
       
       /* 始终预留滚动条空间,防止隐藏时出现布局抖动 */
       html {
           scrollbar-gutter: stable;
       }
       
       /* 头部栏 */
       #ec-ai-header {
           background: #fff;
           border-bottom: 1px solid #e1e4e8;
           padding: 12px 16px;
           display: flex;
           justify-content: space-between;
           align-items: center;
           flex-shrink: 0;
           height: 56px;
       }
       
       #ec-ai-title {
           font-size: 16px;
           font-weight: 600;
           color: #24292e;
           display: flex;
           align-items: center;
           gap: 8px;
       }
       
       /* 关闭按钮 - 始终显示在右上角 */
       #ec-ai-close {
           width: 32px;
           height: 32px;
           border: none;
           background: transparent;
           color: #666;
           cursor: pointer;
           border-radius: 6px;
           display: flex;
           align-items: center;
           justify-content: center;
           font-size: 18px;
           font-weight: 300;
           transition: all 0.2s;
       }
       
       #ec-ai-close:hover {
           background: #f5f7fa;
           color: #24292e;
       }
       
       /* 关闭按钮文字(X) */
       #ec-ai-close::before {
           content: "×";
           line-height: 1;
       }
       
       /* iframe 容器 */
       #ec-ai-iframe-container {
           flex: 1;
           position: relative;
           background: #f5f7fa;
       }
       
       #ec-ai-iframe {
           width: 100%;
           height: 100%;
           border: none;
           display: block;
       }
       
       /* 加载指示器 */
       #ec-ai-loader {
           position: absolute;
           top: 50%;
           left: 50%;
           transform: translate(-50%, -50%);
           display: flex;
           gap: 4px;
       }
       
       #ec-ai-loader span {
           width: 8px;
           height: 8px;
           background: #206588;
           border-radius: 50%;
           animation: ec-ai-pulse 1.4s infinite;
       }
       
       #ec-ai-loader span:nth-child(2) { animation-delay: 0.2s; }
       #ec-ai-loader span:nth-child(3) { animation-delay: 0.4s; }
       
       @keyframes ec-ai-pulse {
           0%, 100% { opacity: 0.4; transform: scale(1); }
           50% { opacity: 1; transform: scale(1.2); }
       }
       
       /* 加载错误提示 */
       #ec-ai-loader .ec-ai-error {
           text-align: center;
           padding: 20px;
           color: #666;
           font-size: 14px;
       }
       
       /* 滚动条样式 */
       #ec-ai-sidebar ::-webkit-scrollbar {
           width: 6px;
           height: 6px;
       }
       
       #ec-ai-sidebar ::-webkit-scrollbar-track {
           background: transparent;
       }
       
       #ec-ai-sidebar ::-webkit-scrollbar-thumb {
           background: #64acc4;
           border-radius: 3px;
       }
       
       #ec-ai-sidebar ::-webkit-scrollbar-thumb:hover {
           background: #4a8fa6;
       }
       
       #ec-ai-sidebar {
           scrollbar-width: thin;
           scrollbar-color: #64acc4 transparent;
       }
       
       /* 深色模式 - 合并所有深色样式 */
       @media (prefers-color-scheme: dark) {
           /* 滚动条 */
           #ec-ai-sidebar ::-webkit-scrollbar-thumb {
               background: #28494f;
           }
           
           #ec-ai-sidebar ::-webkit-scrollbar-thumb:hover {
               background: #3a6b73;
           }
           
           #ec-ai-sidebar {
               scrollbar-color: #28494f transparent;
           }
           
           /* 侧边栏 */
           #ec-ai-sidebar {
               background: #1a1a1a;
               border-left-color: #444;
           }
           
           #ec-ai-header {
               background: #2a2a2a;
               border-bottom-color: #444;
           }
           
           #ec-ai-title {
               color: #e0e0e0;
           }
           
           #ec-ai-close {
               color: #aaa;
           }
           
           #ec-ai-close:hover {
               background: #333;
               color: #e0e0e0;
           }
           
           #ec-ai-iframe-container {
               background: #1a1a1a;
           }
           
           #ec-ai-loader span {
               background: #64b5f6;
           }
           
           #ec-ai-loader .ec-ai-error {
               color: #aaa;
           }
       }
   `;
   document.head.appendChild(style);
   
   // 创建悬浮按钮
   const fab = document.createElement('button');
   fab.id = 'ec-ai-fab';
   fab.innerHTML = AI_ICON_SVG;
   fab.title = '打开 AI 助手';
   fab.setAttribute('aria-label', '打开 AI 助手');
   fab.addEventListener('click', openSidebar);
   document.body.appendChild(fab);
   
   // 触发淡入动画
   requestAnimationFrame(() => {
       fab.classList.add('visible');
   });
   
   // 创建遮罩层
   const overlay = document.createElement('div');
   overlay.id = 'ec-ai-overlay';
   overlay.addEventListener('click', closeSidebar);
   document.body.appendChild(overlay);
   
   // 创建侧边栏容器
   const sidebar = document.createElement('div');
   sidebar.id = 'ec-ai-sidebar';
   
   // 根据当前设备类型设置初始类名
   if (isMobile()) {
       sidebar.classList.add('mobile');
   }
   
   // 创建头部
   const header = document.createElement('div');
   header.id = 'ec-ai-header';
   
   const titleDiv = document.createElement('div');
   titleDiv.id = 'ec-ai-title';
   titleDiv.innerHTML = 'EaseCation Wiki AI';
   
   const closeBtn = document.createElement('button');
   closeBtn.id = 'ec-ai-close';
   closeBtn.title = '关闭';
   closeBtn.setAttribute('aria-label', '关闭');
   closeBtn.addEventListener('click', closeSidebar);
   
   header.appendChild(titleDiv);
   header.appendChild(closeBtn);
   sidebar.appendChild(header);
   
   // 创建 iframe 容器
   const iframeContainer = document.createElement('div');
   iframeContainer.id = 'ec-ai-iframe-container';
   
   // 创建加载动画
   const loader = document.createElement('div');
   loader.id = 'ec-ai-loader';
   loader.innerHTML = '';
   iframeContainer.appendChild(loader);
   
   // 创建 iframe(懒加载)
   let iframe = null;
   
   sidebar.appendChild(iframeContainer);
   document.body.appendChild(sidebar);
   
   // 监听窗口大小变化(带防抖)
   eventHandlers.resize = debounce(function() {
       if (isMobile()) {
           sidebar.classList.add('mobile');
       } else {
           sidebar.classList.remove('mobile');
       }
   }, CONFIG.debounceDelay);
   window.addEventListener('resize', eventHandlers.resize);
   
   // 键盘快捷键支持
   eventHandlers.keydown = function(e) {
       // ESC 关闭
       if (e.key === 'Escape' && sidebar.classList.contains('active')) {
           closeSidebar();
       }
       // 开启快捷键: Ctrl/Cmd + Shift + A
       if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'A') {
           e.preventDefault();
           openSidebar();
       }
   };
   document.addEventListener('keydown', eventHandlers.keydown);
   
   // 监听系统主题变化并通知 iframe
   eventHandlers.themeChange = function(e) {
       if (iframe && iframe.contentWindow) {
           try {
               iframe.contentWindow.postMessage({
                   type: 'theme-change',
                   prefersDark: e.matches
               }, '*');
           } catch (err) {
               // 忽略跨域错误
           }
       }
   };
   const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
   if (darkModeQuery.addEventListener) {
       darkModeQuery.addEventListener('change', eventHandlers.themeChange);
   } else if (darkModeQuery.addListener) {
       // 兼容旧浏览器
       darkModeQuery.addListener(eventHandlers.themeChange);
   }
   
   // 打开侧边栏
   function openSidebar() {
       sidebar.classList.toggle('mobile', isMobile());
       document.body.style.overflow = 'hidden';
       sidebar.classList.add('active');
       overlay.classList.add('active');
       
       // 首次加载 iframe
       if (!iframe) {
           iframe = document.createElement('iframe');
           iframe.id = 'ec-ai-iframe';
           iframe.src = CONFIG.iframeSrc;
           iframe.title = 'EaseCation Wiki AI 助手';
           iframe.setAttribute('allow', 'fullscreen');
           iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms');
           
           // 加载完成后隐藏 loader
           eventHandlers.iframeLoad = function() {
               loader.style.display = 'none';
               // 发送当前主题
               if (iframe.contentWindow) {
                   try {
                       iframe.contentWindow.postMessage({
                           type: 'theme-change',
                           prefersDark: darkModeQuery.matches
                       }, '*');
                   } catch (err) {
                       // 忽略跨域错误
                   }
               }
           };
           iframe.addEventListener('load', eventHandlers.iframeLoad);
           
           // 加载失败处理
           eventHandlers.iframeError = function() {

loader.innerHTML = '

加载失败,请刷新重试

';

           };
           iframe.addEventListener('error', eventHandlers.iframeError);
           
           iframeContainer.appendChild(iframe);
       }
   }
   
   // 关闭侧边栏
   function closeSidebar() {
       sidebar.classList.remove('active');
       overlay.classList.remove('active');
       document.body.style.overflow = ;
   }
   
   // 清理方法
   function destroy() {
       // 移除事件监听
       window.removeEventListener('resize', eventHandlers.resize);
       document.removeEventListener('keydown', eventHandlers.keydown);
       if (darkModeQuery.removeEventListener) {
           darkModeQuery.removeEventListener('change', eventHandlers.themeChange);
       } else if (darkModeQuery.removeListener) {
           darkModeQuery.removeListener(eventHandlers.themeChange);
       }
       
       if (iframe) {
           iframe.removeEventListener('load', eventHandlers.iframeLoad);
           iframe.removeEventListener('error', eventHandlers.iframeError);
       }
       
       // 移除 DOM 元素
       style.remove();
       fab.remove();
       overlay.remove();
       sidebar.remove();
       
       // 清理 body overflow
       document.body.style.overflow = ;
       
       // 清理全局接口
       delete window.ECWikiAI;
       
       console.log('[EC Wiki AI] 已销毁');
   }
   
   // 全局接口
   window.ECWikiAI = {
       open: openSidebar,
       close: closeSidebar,
       toggle: function() {
           if (sidebar.classList.contains('active')) {
               closeSidebar();
           } else {
               openSidebar();
           }
       },
       isOpen: function() {
           return sidebar.classList.contains('active');
       },
       destroy: destroy
   };
   
   console.log('[EC Wiki AI] 加载成功');

})();

</script>