引言

分享一个我自用的资源导航主页,这个项目简单易用,支持自定义链接、分类、图标(emoji或图片),还带有一个可选的搜索框。你可以根据自己的需求修改和部署。以下是详细的搭建教程。

功能支持

  • 分类导航​:支持多类别网站列表,每个类别下可添加多个链接。
  • 自定义图标​:支持emoji和图片URL两种图标类型。
  • 响应式设计​:适配桌面和移动端,包含悬停动画和玻璃风格UI。
  • 轻量高效​:部署在Cloudflare Workers上,无需服务器,响应速度快。

部署

  1. 复制代码
// 配置选项
const config = {
  enableSearch: true, // 是否开启搜索框功能,true为开启,false为关闭
};

// 定义分类网站列表
// 每个网站支持以下图标类型:
// - iconType: 'emoji' - 使用emoji图标,需指定icon为emoji字符
// - iconType: 'url' - 使用自定义图片直链,需指定icon为图片URL
const categorizedSites = [
  {
    category: '🔍常用链接',
    items: [
      { url: 'https://blog.mlho.net/', name: '博客', iconType: 'url', icon: 'https://blog.mlho.net/favicon.ico' },
      { url: 'https://pan.baidu.com/', name: '云盘', iconType: 'emoji', icon: '☁️' }
    ]
  },
  {
    category: '🔮其他链接',
    items: [
      { url: 'https://www.aconvert.com/', name: '格式转换', iconType: 'emoji', icon: '🔘' },
      { url: 'https://www.iodraw.com/mind', name: '思维导图', iconType: 'emoji', icon: '🧠' }
    ]
  }
];

// 生成图标HTML的函数
// 根据iconType生成对应的图标HTML,仅支持emoji和自定义URL
const getIconHtml = (site) => {
  if (site.iconType === 'emoji') {
    return `<span class="emoji">${site.icon}</span>`; // 使用emoji图标
  } else if (site.iconType === 'url') {
    return `<img src="${site.icon}" alt="${site.name} icon" class="icon-image">`; // 使用自定义图片URL
  } else {
    return `<span class="emoji">🌐</span>`; // 默认使用emoji(向后兼容)
  }
};

// 生成分类卡片HTML
const categorySections = categorizedSites.map(category => `
  <section class="category-section">
    <h2 class="category-title">${category.category}</h2>
    <div class="card-container">
      ${category.items.map(site => `
        <div class="card">
          ${getIconHtml(site)}
          <a href="${site.url}" target="_blank" rel="noopener noreferrer" class="card-link">${site.name}</a>
        </div>
      `).join('')}
    </div>
  </section>
`).join('');

// 搜索框HTML
const searchHtml = config.enableSearch ? `
  <div class="search-container">
    <form class="search-form" action="https://www.google.com/search" method="get" target="_blank" autocomplete="off">
      <input 
        type="text" 
        name="q" 
        placeholder="输入关键词搜索..." 
        class="search-input"
        aria-label="搜索框"
        value=""
      >
      <span class="search-icon">🔍</span>
      <button type="submit" class="search-button">搜索</button>
    </form>
  </div>
` : ''; // 如果enableSearch为false,则不显示搜索框

// 完整CSS,包含所有动画效果并支持图片图标
const css = `
:root {
  --primary: #4f46e5;
  --primary-light: #6366f1;
  --text: #1e293b;
  --text-light: #64748b;
  --glass: rgba(255, 255, 255, 0.65);
  --glass-border: 1px solid rgba(255, 255, 255, 0.3);
  --shadow-sm: 0 4px 12px rgba(31, 38, 135, 0.1);
  --shadow-md: 0 8px 32px rgba(31, 38, 135, 0.15);
  --shadow-lg: 0 12px 40px rgba(31, 38, 135, 0.25);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  background: linear-gradient(135deg, #e0e7ff 0%, #d1d8f0 100%);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem 1rem;
  color: var(--text);
}

h1 {
  background: linear-gradient(45deg, var(--primary), var(--primary-light));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-size: 2.8rem;
  margin: 1.5rem 0 2rem;
  font-weight: 700;
  letter-spacing: -0.05em;
  transition: transform 0.3s ease;
}

h1:hover {
  transform: scale(1.02);
}

/* 搜索框样式 */
.search-container {
  width: 100%;
  max-width: 680px;
  margin: 1.5rem 0 3rem;
  position: relative;
}

.search-form {
  position: relative;
}

.search-input {
  width: 100%;
  padding: 1.1rem 1.5rem 1.1rem 3.5rem;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 16px;
  background: var(--glass);
  backdrop-filter: blur(16px);
  font-size: 1.1rem;
  color: var(--text);
  box-shadow: var(--shadow-sm);
  transition: all 0.4s cubic-bezier(0.18, 0.89, 0.32, 1.28);
}

.search-input:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.95);
  border-color: rgba(79, 70, 229, 0.4);
  box-shadow: var(--shadow-md);
  transform: scale(1.02);
}

.search-icon {
  position: absolute;
  left: 1.2rem;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0.6;
  z-index: 1;
}

.search-button {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  height: calc(100% - 16px);
  padding: 0 1.8rem;
  border: none;
  border-radius: 12px;
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  color: white;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2);
}

.search-button:hover {
  transform: translateY(-50%) scale(1.05);
  box-shadow: 0 6px 16px rgba(79, 70, 229, 0.3);
}

/* 卡片样式 */
.category-section {
  width: 100%;
  max-width: 1200px;
  margin-bottom: 3rem;
}

.category-title {
  color: var(--primary);
  font-size: 1.8rem;
  margin: 1.5rem 0;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid rgba(79, 70, 229, 0.2);
  transition: transform 0.3s ease;
}

.category-title:hover {
  transform: translateX(5px);
}

.card-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.5rem;
  padding: 0.5rem;
}

.card {
  background: var(--glass);
  backdrop-filter: blur(12px);
  border-radius: 18px;
  border: var(--glass-border);
  padding: 1.8rem 1.5rem;
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;
  min-height: 160px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  box-shadow: var(--shadow-md);
}

.card:hover {
  transform: translateY(-8px) scale(1.02);
  background: rgba(255, 255, 255, 0.85);
  box-shadow: var(--shadow-lg);
}

.emoji {
  font-size: 2.8rem;
  margin-bottom: 1.2rem;
  transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
  display: inline-block;
}

.icon-image {
  width: 48px;
  height: 48px;
  margin-bottom: 1.2rem;
  transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}

.card:hover .emoji,
.card:hover .icon-image {
  transform: rotate(12deg) scale(1.1);
}

.card-link {
  text-decoration: none;
  color: var(--text);
  font-size: 1.2rem;
  font-weight: 600;
  position: relative;
  padding: 0.25rem 0;
  transition: color 0.3s ease;
}

.card-link::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 0;
  height: 2px;
  background: var(--primary);
  transition: width 0.4s ease;
}

.card:hover .card-link::after {
  width: 100%;
}

.card-link:hover {
  color: var(--primary);
}

/* 移动端适配 */
@media (max-width: 768px) {
  h1 { font-size: 2.4rem; }
  .search-container { margin: 2rem 0; }
}

@media (max-width: 640px) {
  body { padding: 1.5rem 1rem; }
  h1 { font-size: 2.2rem; margin: 1rem 0 1.5rem; }
  .card-container { grid-template-columns: repeat(2, minmax(140px, 1fr)); gap: 1rem; }
  .card { min-height: 140px; padding: 1.2rem; }
  .card:hover { transform: translateY(-5px) scale(1.02); }
  .emoji { font-size: 2.2rem; margin-bottom: 0.8rem; }
  .icon-image { width: 40px; height: 40px; margin-bottom: 0.8rem; }
  .card-link { font-size: 1.1rem; }
}

@media (max-width: 480px) {
  .search-button { padding: 0 1.2rem; }
  .search-input { padding-left: 2.8rem; }
  .search-icon { left: 1rem; }
  .card:hover .emoji, .card:hover .icon-image { transform: rotate(8deg) scale(1.05); }
}
`;

// HTML模板
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>资源导航</title>
  <meta name="description" content="快速访问精选资源">
  <link rel="icon" href="https://blog.mlho.net/favicon.ico">
  <style>${css}</style>
</head>
<body>
  <h1>资源导航</h1>

  ${searchHtml}

  ${categorySections}

  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const searchInput = document.querySelector('.search-input');
      if (searchInput) {
        searchInput.value = '';
      }

      const searchForm = document.querySelector('.search-form');
      if (searchForm) {
        searchForm.addEventListener('submit', (e) => {
          setTimeout(() => {
            searchInput.value = '';
          }, 100);
        });
      }
    });
  </script>
</body>
</html>`;

// Cloudflare Workers处理逻辑
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);

  if (url.pathname === '/') {
    return new Response(html, {
      headers: {
        'Content-Type': 'text/html; charset=utf-8',
        'Cache-Control': 'public, max-age=3600'
      }
    });
  }

  return new Response('Not Found', { status: 404 });
}
  1. 登录Cloudflare
    进入Cloudflare仪表盘,选择左侧的“Workers和Pages”选项。
  2. 创建Worker
    点击“创建Worker”,随便输入一个名称(例如resource-nav)。
  3. 粘贴代码
    将修改好的代码粘贴到编辑器中,点击“保存并部署”。
  4. 测试访问
    部署后,你会得到一个默认URL(例如resource-nav.your-username.workers.dev),打开浏览器访问即可看到页面。
  5. 绑定自定义域名(可选)
    在“设置”选项中添加自定义域名(如nav.yourdomain.com),需确保域名已在Cloudflare中管理。

效果展示

效果图

添加新评论

赞助商