浏览代码

feat: Add new document tutorial configuration functionality.

dqzboy 1 年之前
父节点
当前提交
8b8b5add63
共有 5 个文件被更改,包括 618 次插入31 次删除
  1. 8 2
      hubcmdui/README.md
  2. 0 0
      hubcmdui/documentation/1724594777670.json
  3. 189 0
      hubcmdui/server.js
  4. 306 25
      hubcmdui/web/admin.html
  5. 115 4
      hubcmdui/web/index.html

+ 8 - 2
hubcmdui/README.md

@@ -6,7 +6,7 @@
   <p align="center">
   <img src="https://github.com/dqzboy/Docker-Proxy/assets/42825450/c187d66f-152e-4172-8268-e54bd77d48bb" width="230px" height="200px">
       <br>
-      <i>Docker镜像加速命令查询获取和镜像搜索UI面板.</i>
+      <i>Docker镜像加速命令查询获取、镜像搜索、配置教程文档展示UI面板.</i>
   </p>
 </div>
 
@@ -107,6 +107,12 @@ docker logs -f [容器ID或名称]
     </tr>
 </table>
 
+<table>
+    <tr>
+        <td width="50%" align="center"><img src="https://github.com/user-attachments/assets/b7efcd39-8757-46f9-ae68-02ca6add40e7"?raw=true"></td>
+    </tr>
+</table>
+
 > 浏览器输入 `服务器地址:30080/admin` 访问后端页面,默认登入账号密码: root/admin@123
 
 <table>
@@ -117,7 +123,7 @@ docker logs -f [容器ID或名称]
 
 <table>
     <tr>
-        <td width="50%" align="center"><img src="https://github.com/user-attachments/assets/7f3196c6-5da7-409a-ab49-1037ad0db1d6"?raw=true"></td>
+        <td width="50%" align="center"><img src="https://github.com/user-attachments/assets/3ffe6a5d-da8c-436a-ae28-033fecf52770"?raw=true"></td>
     </tr>
 </table>
 

文件差异内容过多而无法显示
+ 0 - 0
hubcmdui/documentation/1724594777670.json


+ 189 - 0
hubcmdui/server.js

@@ -42,6 +42,8 @@ app.get('/api/search', async (req, res) => {
 
 const CONFIG_FILE = path.join(__dirname, 'config.json');
 const USERS_FILE = path.join(__dirname, 'users.json');
+const DOCUMENTATION_DIR = path.join(__dirname, 'documentation');
+const DOCUMENTATION_FILE = path.join(__dirname, 'documentation.md');
 
 // 读取配置
 async function readConfig() {
@@ -103,6 +105,54 @@ async function writeUsers(users) {
   await fs.writeFile(USERS_FILE, JSON.stringify({ users }, null, 2), 'utf8');
 }
 
+
+// 确保 documentation 目录存在
+async function ensureDocumentationDir() {
+  try {
+      await fs.access(DOCUMENTATION_DIR);
+  } catch (error) {
+      if (error.code === 'ENOENT') {
+          await fs.mkdir(DOCUMENTATION_DIR);
+      } else {
+          throw error;
+      }
+  }
+}
+
+// 读取文档
+async function readDocumentation() {
+  try {
+    await ensureDocumentationDir();
+    const files = await fs.readdir(DOCUMENTATION_DIR);
+    console.log('Files in documentation directory:', files);  // 添加日志
+
+    const documents = await Promise.all(files.map(async file => {
+      const filePath = path.join(DOCUMENTATION_DIR, file);
+      const content = await fs.readFile(filePath, 'utf8');
+      const doc = JSON.parse(content);
+      return {
+        id: path.parse(file).name,
+        title: doc.title,
+        content: doc.content,
+        published: doc.published
+      };
+    }));
+
+    const publishedDocuments = documents.filter(doc => doc.published);
+    console.log('Published documents:', publishedDocuments);  // 添加日志
+    return publishedDocuments;
+  } catch (error) {
+    console.error('Error reading documentation:', error);
+    throw error;
+  }
+}
+
+// 写入文档
+async function writeDocumentation(content) {
+  await fs.writeFile(DOCUMENTATION_FILE, content, 'utf8');
+}
+
+
 // 登录验证
 app.post('/api/login', async (req, res) => {
   const { username, password, captcha } = req.body;
@@ -202,6 +252,145 @@ app.get('/api/captcha', (req, res) => {
   res.json({ captcha });
 });
 
+
+// API端点:获取文档列表
+app.get('/api/documentation-list', requireLogin, async (req, res) => {
+  try {
+      const files = await fs.readdir(DOCUMENTATION_DIR);
+      const documents = await Promise.all(files.map(async file => {
+          const content = await fs.readFile(path.join(DOCUMENTATION_DIR, file), 'utf8');
+          const doc = JSON.parse(content);
+          return { id: path.parse(file).name, ...doc };
+      }));
+      res.json(documents);
+  } catch (error) {
+      res.status(500).json({ error: '读取文档列表失败' });
+  }
+});
+
+// API端点:保存文档
+app.post('/api/documentation', requireLogin, async (req, res) => {
+  try {
+      const { id, title, content } = req.body;
+      const docId = id || Date.now().toString();
+      const docPath = path.join(DOCUMENTATION_DIR, `${docId}.json`);
+      await fs.writeFile(docPath, JSON.stringify({ title, content, published: false }));
+      res.json({ success: true });
+  } catch (error) {
+      res.status(500).json({ error: '保存文档失败' });
+  }
+});
+
+// API端点:删除文档
+app.delete('/api/documentation/:id', requireLogin, async (req, res) => {
+  try {
+      const docPath = path.join(DOCUMENTATION_DIR, `${req.params.id}.json`);
+      await fs.unlink(docPath);
+      res.json({ success: true });
+  } catch (error) {
+      res.status(500).json({ error: '删除文档失败' });
+  }
+});
+
+// API端点:切换文档发布状态
+app.post('/api/documentation/:id/toggle-publish', requireLogin, async (req, res) => {
+  try {
+      const docPath = path.join(DOCUMENTATION_DIR, `${req.params.id}.json`);
+      const content = await fs.readFile(docPath, 'utf8');
+      const doc = JSON.parse(content);
+      doc.published = !doc.published;
+      await fs.writeFile(docPath, JSON.stringify(doc));
+      res.json({ success: true });
+  } catch (error) {
+      res.status(500).json({ error: '更改发布状态失败' });
+  }
+});
+
+// API端点:获取文档
+app.get('/api/documentation', async (req, res) => {
+  try {
+    const documents = await readDocumentation();
+    console.log('Sending documents:', documents);  // 添加日志
+    res.json(documents);
+  } catch (error) {
+    console.error('Error in /api/documentation:', error);
+    res.status(500).json({ error: '读取文档失败', details: error.message });
+  }
+});
+
+// API端点:保存文档
+app.post('/api/documentation', requireLogin, async (req, res) => {
+  try {
+    const { content } = req.body;
+    await writeDocumentation(content);
+    res.json({ success: true });
+  } catch (error) {
+    res.status(500).json({ error: '保存文档失败' });
+  }
+});
+
+// 获取文档列表函数
+async function getDocumentList() {
+  try {
+    await ensureDocumentationDir();
+    const files = await fs.readdir(DOCUMENTATION_DIR);
+    console.log('Files in documentation directory:', files);
+
+    const documents = await Promise.all(files.map(async file => {
+      try {
+        const filePath = path.join(DOCUMENTATION_DIR, file);
+        const content = await fs.readFile(filePath, 'utf8');
+        return {
+          id: path.parse(file).name,
+          title: path.parse(file).name, // 使用文件名作为标题
+          content: content,
+          published: true // 假设所有文档都是已发布的
+        };
+      } catch (fileError) {
+        console.error(`Error reading file ${file}:`, fileError);
+        return null;
+      }
+    }));
+
+    const validDocuments = documents.filter(doc => doc !== null);
+    console.log('Valid documents:', validDocuments);
+
+    return validDocuments;
+  } catch (error) {
+    console.error('Error reading document list:', error);
+    throw error; // 重新抛出错误,让上层函数处理
+  }
+}
+
+app.get('/api/documentation-list', async (req, res) => {
+  try {
+    const documents = await getDocumentList();
+    res.json(documents);
+  } catch (error) {
+    console.error('Error in /api/documentation-list:', error);
+    res.status(500).json({ 
+      error: '读取文档列表失败', 
+      details: error.message,
+      stack: error.stack
+    });
+  }
+});
+
+app.get('/api/documentation/:id', async (req, res) => {
+  try {
+    const docId = req.params.id;
+    console.log('Fetching document with id:', docId);  // 添加日志
+    const docPath = path.join(DOCUMENTATION_DIR, `${docId}.json`);
+    const content = await fs.readFile(docPath, 'utf8');
+    const doc = JSON.parse(content);
+    console.log('Sending document:', doc);  // 添加日志
+    res.json(doc);
+  } catch (error) {
+    console.error('Error reading document:', error);
+    res.status(500).json({ error: '读取文档失败', details: error.message });
+  }
+});
+
 // 启动服务器
 const PORT = process.env.PORT || 3000;
 app.listen(PORT, () => {

+ 306 - 25
hubcmdui/web/admin.html

@@ -5,6 +5,10 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Docker 镜像代理加速 - 管理面板</title>
     <link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
+
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/editormd.min.css">
+    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/[email protected]/editormd.min.js"></script>
     <style>
         body {
             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Lato', 'Helvetica', 'Arial', sans-serif;
@@ -399,13 +403,17 @@
     </style>
 </head>
 <body>
-    <div class="admin-container hidden" id="adminContainer">
+    <div id="loadingIndicator" style="display: flex; justify-content: center; align-items: center; height: 100vh;">
+        <p>加载中...</p>
+    </div>
+    <div class="admin-container" id="adminContainer" style="display: none;">
         <div class="sidebar">
             <h2>管理面板</h2>
             <ul>
                 <li class="active" data-section="basic-config">基本配置</li>
                 <li data-section="menu-management">菜单管理</li>
                 <li data-section="ad-management">广告管理</li>
+                <li data-section="documentation-management">文档管理</li>            
                 <li data-section="password-change">修改密码</li>
             </ul>
         </div>
@@ -456,7 +464,32 @@
                     </table>
                     <button type="button" class="add-btn" onclick="showNewAdRow()">添加广告</button>
                 </div>
-                
+  
+                <!-- 文档管理部分 -->
+                <div id="documentation-management" class="content-section">
+                    <h2 class="menu-label">文档管理</h2>
+                    <button type="button" onclick="newDocument()">新建文档</button>
+                    <table id="documentTable">
+                        <thead>
+                            <tr>
+                                <th>文章</th>
+                                <th>操作</th>
+                            </tr>
+                        </thead>
+                        <tbody id="documentTableBody">
+                            <!-- 文档列表将在这里动态添加 -->
+                        </tbody>
+                    </table>
+                    <div id="editorContainer" style="display: none;">
+                        <input type="text" id="documentTitle" placeholder="文档标题">
+                        <div id="editormd">
+                            <textarea style="display:none;"></textarea>
+                        </div>
+                        <button type="button" onclick="saveDocument()">保存文档</button>
+                        <button type="button" onclick="cancelEdit()">取消</button>
+                    </div>
+                </div>  
+
                 <div id="password-change" class="content-section">
                     <h2 class="menu-label">修改密码</h2>
                     <label for="currentPassword">当前密码</label>
@@ -471,7 +504,7 @@
         </div>
     </div>
 
-    <div class="login-modal" id="loginModal">
+    <div class="login-modal" id="loginModal" style="display: none;">
         <div class="login-content">
             <div class="login-header">
                 <h2>登入</h2>
@@ -494,6 +527,185 @@
         let adImages = [];
         let isLoggedIn = false;
         let editingIndex = -1; // 用于记录当前编辑的菜单项索引
+        let editor;
+        let currentEditingDoc = null;
+        let documents = [];
+        
+        // 初始化编辑器
+        function initEditor() {
+            if (editor) {
+                console.log('Editor already initialized');
+                return;
+            }
+            try {
+                editor = editormd("editormd", {
+                    width: "100%",
+                    height: 640,
+                    path : "https://cdn.jsdelivr.net/npm/[email protected]/lib/",
+                    theme : "default",
+                    previewTheme : "default",
+                    editorTheme : "default",
+                    markdown : "",
+                    codeFold : true,
+                    saveHTMLToTextarea : true,
+                    searchReplace : true,
+                    watch : true, // 开启实时预览
+                    htmlDecode : "style,script,iframe|on*",
+                    toolbar : true,
+                    toolbarIcons : "full",
+                    placeholder: "请输入Markdown格式的文档...",
+                    emoji : true,
+                    taskList : true,
+                    tocm : true,
+                    tex : true,
+                    flowChart : true,
+                    sequenceDiagram : true,
+                    onload : function() {
+                        console.log('Editor.md loaded successfully');
+                        // 在加载完成后,立即切换到编辑模式
+                        this.unwatch();
+                        this.watch();
+                    }
+                });
+                console.log('Editor initialized successfully');
+            } catch (error) {
+                console.error('Error initializing editor:', error);
+            }
+        }
+
+
+        function newDocument() {
+            currentEditingDoc = null;
+            document.getElementById('documentTitle').value = '';
+            editor.setMarkdown('');
+            showEditor();
+        }
+
+        function showEditor() {
+            document.getElementById('documentTable').style.display = 'none';
+            document.getElementById('editorContainer').style.display = 'block';
+            if (editor) {
+                // 确保每次显示编辑器时都切换到编辑模式
+                editor.unwatch();
+                editor.watch();
+            }
+        }
+
+        function hideEditor() {
+            document.getElementById('documentTable').style.display = 'table';
+            document.getElementById('editorContainer').style.display = 'none';
+        }
+
+        function cancelEdit() {
+            hideEditor();
+        }
+
+        async function saveDocument() {
+            const title = document.getElementById('documentTitle').value.trim();
+            const content = editor.getMarkdown();
+            if (!title) {
+                alert('请输入文档标题');
+                return;
+            }
+            try {
+                const response = await fetch('/api/documentation', {
+                    method: 'POST',
+                    headers: { 'Content-Type': 'application/json' },
+                    body: JSON.stringify({ 
+                        id: currentEditingDoc ? currentEditingDoc.id : null, 
+                        title, 
+                        content 
+                    })
+                });
+                if (response.ok) {
+                    alert('文档保存成功');
+                    hideEditor();
+                    loadDocumentList();
+                } else {
+                    throw new Error('保存失败');
+                }
+            } catch (error) {
+                console.error('保存文档失败:', error);
+                alert('保存文档失败: ' + error.message);
+            }
+        }
+
+        async function loadDocumentList() {
+            try {
+                const response = await fetch('/api/documentation-list');
+                if (!response.ok) {
+                    const errorData = await response.json();
+                    throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.error}, details: ${errorData.details}`);
+                }
+                documents = await response.json();
+                console.log('Received documents:', documents);
+                renderDocumentList();
+            } catch (error) {
+                console.error('加载文档列表失败:', error);
+                alert('加载文档列表失败: ' + error.message);
+            }
+        }
+
+        function renderDocumentList() {
+            const tbody = document.getElementById('documentTableBody');
+            tbody.innerHTML = '';
+            if (documents.length === 0) {
+                tbody.innerHTML = '<tr><td colspan="2">没有找到文档</td></tr>';
+                return;
+            }
+            documents.forEach(doc => {
+                const row = `
+                    <tr>
+                        <td>${doc.title || 'Untitled Document'}</td>
+                        <td>
+                            <button onclick="editDocument('${doc.id}')">编辑</button>
+                            <button onclick="deleteDocument('${doc.id}')">删除</button>
+                            <button onclick="togglePublish('${doc.id}')">${doc.published ? '取消发布' : '发布'}</button>
+                        </td>
+                    </tr>
+                `;
+                tbody.innerHTML += row;
+            });
+        }
+
+        function editDocument(id) {
+            currentEditingDoc = documents.find(doc => doc.id === id);
+            document.getElementById('documentTitle').value = currentEditingDoc.title;
+            editor.setMarkdown(currentEditingDoc.content);
+            showEditor();
+        }
+
+        async function deleteDocument(id) {
+            if (confirm('确定要删除这个文档吗?')) {
+                try {
+                    const response = await fetch(`/api/documentation/${id}`, { method: 'DELETE' });
+                    if (response.ok) {
+                        alert('文档删除成功');
+                        loadDocumentList();
+                    } else {
+                        throw new Error('删除失败');
+                    }
+                } catch (error) {
+                    console.error('删除文档失败:', error);
+                    alert('删除文档失败: ' + error.message);
+                }
+            }
+        }
+
+        async function togglePublish(id) {
+            try {
+                const response = await fetch(`/api/documentation/${id}/toggle-publish`, { method: 'POST' });
+                if (response.ok) {
+                    loadDocumentList();
+                } else {
+                    throw new Error('操作失败');
+                }
+            } catch (error) {
+                console.error('更改发布状态失败:', error);
+                alert('更改发布状态失败: ' + error.message);
+            }
+        }
+
 
         function getMenuItems() {
             return menuItems;
@@ -840,18 +1052,82 @@
             }
         }
 
+        // 加载文档的函数
+        async function loadDocumentation() {
+            try {
+                const response = await fetch('/api/documentation');
+                if (!response.ok) {
+                throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                const documents = await response.json();
+                console.log('Received documents from server:', documents);
+                if (documents.length > 0) {
+                const firstDocument = documents[0];
+                if (!editor) {
+                    console.warn('Editor not initialized, initializing now');
+                    initEditor();
+                }
+                // 等待一小段时间确保编辑器完全初始化
+                setTimeout(() => {
+                    if (editor && typeof editor.setMarkdown === 'function') {
+                    editor.setMarkdown(firstDocument.content);
+                    console.log('Documentation loaded successfully');
+                    } else {
+                    throw new Error('Editor not properly initialized or setMarkdown method not found');
+                    }
+                }, 500);
+                } else {
+                console.log('No documents found');
+                }
+            } catch (error) {
+                console.error('加载文档失败:', error);
+                alert('加载文档失败: ' + error.message);
+            }
+        }
+
+        // 保存文档的函数
+        async function saveDocumentation() {
+            if (!editor) {
+                console.error('Editor not initialized');
+                return;
+            }
+            const content = editor.getMarkdown();
+            try {
+                const response = await fetch('/api/documentation', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ content })
+                });
+                if (!response.ok) {
+                    throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                const result = await response.json();
+                if (result.success) {
+                    alert('文档保存成功');
+                } else {
+                    throw new Error('保存失败');
+                }
+            } catch (error) {
+                console.error('保存文档失败:', error);
+                alert('保存文档失败: ' + error.message);
+            }
+        }
+
         async function loadConfig() {
-        try {
-            const response = await fetch('/api/config');
-            const config = await response.json();
-            document.getElementById('logoUrl').value = config.logo || '';
-            document.getElementById('proxyDomain').value = config.proxyDomain || '';
-            setMenuItems(config.menuItems || []);
-            adImages = config.adImages || [];
-            renderAdItems();
-          } catch (error) {
-            console.error('加载配置失败:', error);
-          }
+            try {
+                const response = await fetch('/api/config');
+                const config = await response.json();
+                document.getElementById('logoUrl').value = config.logo || '';
+                document.getElementById('proxyDomain').value = config.proxyDomain || '';
+                setMenuItems(config.menuItems || []);
+                adImages = config.adImages || [];
+                renderAdItems();
+                loadDocumentation(); // 新增:加载文档
+            } catch (error) {
+                console.error('加载配置失败:', error);
+            }
         }
 
         async function login() {
@@ -866,9 +1142,9 @@
                 });
                 if (response.ok) {
                     isLoggedIn = true;
-                    localStorage.setItem('isLoggedIn', 'true');  // 存储登录状态
+                    localStorage.setItem('isLoggedIn', 'true');
                     document.getElementById('loginModal').style.display = 'none';
-                    document.getElementById('adminContainer').classList.remove('hidden');
+                    document.getElementById('adminContainer').style.display = 'flex';
                     loadConfig();
                 } else {
                     const errorData = await response.json();
@@ -939,25 +1215,28 @@
                 if (response.ok) {
                     isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
                     if (isLoggedIn) {
-                        document.getElementById('loginModal').style.display = 'none';
-                        document.getElementById('adminContainer').classList.remove('hidden');
-                        loadConfig();
+                        loadDocumentList(); // 加载文档列表
+                        document.getElementById('adminContainer').style.display = 'flex';
+                        await loadConfig();
+                        initEditor(); // 初始化编辑器
                     } else {
-                        document.getElementById('loginModal').style.display = 'block';
+                        document.getElementById('loginModal').style.display = 'flex';
                         refreshCaptcha();
                     }
                 } else {
-                    localStorage.removeItem('isLoggedIn');
-                    document.getElementById('loginModal').style.display = 'block';
-                    refreshCaptcha();
+                    throw new Error('Session check failed');
                 }
             } catch (error) {
+                console.error('Error during initialization:', error);
                 localStorage.removeItem('isLoggedIn');
-                document.getElementById('loginModal').style.display = 'block';
+                document.getElementById('loginModal').style.display = 'flex';
                 refreshCaptcha();
+            } finally {
+                document.getElementById('loadingIndicator').style.display = 'none';
             }
         };
 
+
         function updateAdImage(adImages) {
             const adContainer = document.getElementById('adContainer');
             adContainer.innerHTML = '';
@@ -1002,7 +1281,9 @@
         document.addEventListener('DOMContentLoaded', function() {
         const sidebarItems = document.querySelectorAll('.sidebar li');
         const contentSections = document.querySelectorAll('.content-section');
-
+        if (isLoggedIn) {
+            initEditor();
+        }
         function showSection(sectionId) {
             contentSections.forEach(section => {
                 if (section.id === sectionId) {

+ 115 - 4
hubcmdui/web/index.html

@@ -5,6 +5,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Docker 镜像代理加速</title>
     <link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
+    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
     <style>
         body {
             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Lato', 'Helvetica', 'Arial', sans-serif;
@@ -419,6 +420,7 @@
         <div class="tab-container">
             <div class="tab active" onclick="switchTab('accelerate')">镜像加速</div>
             <div class="tab" onclick="switchTab('search')">镜像搜索</div>
+            <div class="tab" onclick="switchTab('documentation')">文档教程</div>
         </div>
 
         <!-- 镜像加速内容 -->
@@ -443,6 +445,12 @@
             <div id="searchResults"></div>
         </div>
 
+         <!-- 文档教程内容 -->
+         <div id="documentationContent" class="content">
+            <div id="documentList"></div>
+            <div id="documentationText"></div>
+        </div>
+
         <!-- 广告容器 -->
         <div class="carousel" id="carousel">
             <div class="carousel-inner" id="carouselInner">
@@ -485,21 +493,23 @@
             tabs.forEach(tab => tab.classList.remove('active'));
             contents.forEach(content => content.classList.remove('active'));
 
-            document.querySelector(`.tab:nth-child(${tabName === 'accelerate' ? '1' : '2'}`).classList.add('active');
+            document.querySelector(`.tab:nth-child(${tabName === 'accelerate' ? '1' : (tabName === 'search' ? '2' : '3')}`).classList.add('active');
             document.getElementById(`${tabName}Content`).classList.add('active');
 
             // 重置显示
             document.getElementById('searchResults').style.display = 'none';
             document.getElementById('result').style.display = 'none';
-            document.getElementById('carousel').style.display = 'flex';
             document.getElementById('backToTopBtn').style.display = 'none';
+            document.getElementById('carousel').style.display = 'flex'; // 默认显示广告
 
-            if (tabName === 'accelerate') {
+            if (tabName === 'documentation') {
+                fetchDocumentation();
+            } else if (tabName === 'accelerate') {
                 const imageInput = document.getElementById('imageInput').value.trim();
                 if (imageInput) {
                     generateCommands(imageInput);
                 }
-            } else {
+            } else if (tabName === 'search') {
                 document.getElementById('searchInput').value = '';
             }
         }
@@ -642,6 +652,8 @@
 
             const searchResults = document.getElementById('searchResults');
             searchResults.innerHTML = '正在搜索...';
+
+            // 显示和隐藏广告逻辑
             searchResults.style.display = 'block';
             document.getElementById('result').style.display = 'none';
             document.getElementById('carousel').style.display = 'none';
@@ -708,11 +720,108 @@
             }
         }
 
+
+        async function fetchDocumentation() {
+            try {
+                const response = await fetch('/api/documentation');
+                if (!response.ok) {
+                    throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                const documents = await response.json();
+                console.log('Fetched documents:', documents);
+
+                const documentList = window.document.getElementById('documentList');
+                const documentationText = window.document.getElementById('documentationText');
+                
+                if (Array.isArray(documents) && documents.length > 0) {
+                    documentList.innerHTML = '<h2>文档列表</h2>';
+                    const ul = window.document.createElement('ul');
+                    documents.forEach(doc => {
+                        if (doc && doc.id && doc.title) {
+                            const li = window.document.createElement('li');
+                            li.innerHTML = `<a href="javascript:void(0);" onclick="showDocument('${doc.id}')">${doc.title}</a>`;
+                            ul.appendChild(li);
+                        }
+                    });
+                    documentList.appendChild(ul);
+                    
+                    // 默认显示第一篇文档
+                    if (documents[0] && documents[0].id) {
+                        showDocument(documents[0].id);
+                    } else {
+                        documentationText.innerHTML = '无法显示文档,文档 ID 无效';
+                    }
+                } else {
+                    documentationText.innerHTML = '暂无文档内容';
+                }
+                window.document.getElementById('carousel').style.display = 'none';
+            } catch (error) {
+                console.error('获取文档列表失败:', error);
+                const documentationText = window.document.getElementById('documentationText');
+                documentationText.innerHTML = '加载文档列表失败,请稍后再试。错误详情: ' + error.message;
+                window.document.getElementById('carousel').style.display = 'flex';
+            }
+        }
+
+        async function showDocument(id) {
+            try {
+                console.log('Attempting to show document with id:', id);
+                const response = await fetch(`/api/documentation/${id}`);
+                if (!response.ok) {
+                    throw new Error(`HTTP error! status: ${response.status}`);
+                }
+                const document = await response.json();
+                console.log('Fetched document:', document);
+
+                const documentationText = window.document.getElementById('documentationText');
+                
+                if (document && document.content && document.content.trim() !== '') {
+                    documentationText.innerHTML = `<h2>${document.title || '无标题'}</h2>` + marked.parse(document.content);
+                } else {
+                    documentationText.innerHTML = '该文档没有内容或格式不正确';
+                }
+            } catch (error) {
+                console.error('获取文档内容失败:', error);
+                const documentationText = window.document.getElementById('documentationText');
+                documentationText.innerHTML = '加载文档内容失败,请稍后再试。错误详情: ' + error.message;
+            }
+        }
+
         // 获取并加载配置
         async function loadConfig() {
             try {
                 const response = await fetch('/api/config');
                 const config = await response.json();
+
+                if (config.adImages && Array.isArray(config.adImages)) {
+                    const carouselInner = document.getElementById('carouselInner');
+                    carouselInner.innerHTML = ''; // 清空广告容器
+                    config.adImages.forEach((ad, index) => {
+                        const adItem = document.createElement('div');
+                        adItem.className = 'carousel-item';
+                        if (index === 0) {
+                            adItem.classList.add('active');
+                        }
+                        const adImage = document.createElement('img');
+                        adImage.src = ad.url;
+                        adImage.alt = ad.alt || '广告图片';
+                        adImage.style.width = "100%";
+                        adImage.style.height = "100%";
+                        adItem.appendChild(adImage);
+                        carouselInner.appendChild(adItem);
+
+                        // 添加点击事件监听器
+                        adItem.addEventListener('click', function() {
+                            window.open(ad.link, '_blank');
+                        });
+
+                        // 改变鼠标样式,表明可以点击
+                        adItem.style.cursor = 'pointer';
+                    });
+                    items = document.querySelectorAll('.carousel-item');
+                    updateCarousel();
+                    startAutoPlay(); // 启动自动播放
+                }
                 if (config.logo) {
                     document.querySelector('.logo').src = config.logo;
                 }
@@ -781,5 +890,7 @@
             }
         });
     </script>
+
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/2.0.3/marked.min.js"></script>
 </body>
 </html>

部分文件因为文件数量过多而无法显示