index.html 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. {% extends "base.html" %}
  2. {% block title %}逐鹿导航 - 首页{% endblock %}
  3. {% block content %}
  4. <section class="search-area py-4">
  5. <div class="d-flex justify-content-center">
  6. <form method="GET" action="{{ url_for('search') }}" class="search-form w-100" style="max-width:600px;">
  7. <div class="search-box d-flex align-items-center shadow-sm w-100">
  8. <input type="text" name="q" class="form-control search-input"
  9. placeholder="🔍 输入关键词搜索网站..." value="{{ search_query or '' }}">
  10. <button class="btn btn-primary rounded-circle ms-2 btn-search" type="submit">
  11. <i class="bi bi-search"></i>
  12. </button>
  13. </div>
  14. <div class="search-engine mt-3 text-center">
  15. <div class="btn-group" role="group">
  16. {% for name, label in {'bing':'必应','baidu':'百度','google':'谷歌','local':'本地'}.items() %}
  17. <input type="radio" class="btn-check" name="search-engine" id="engine-{{ name }}" value="{{ name }}"
  18. autocomplete="off" {% if name == saved_engine %}checked{% endif %}>
  19. <label class="btn btn-outline-primary btn-sm rounded-pill" for="engine-{{ name }}">{{ label }}</label>
  20. {% endfor %}
  21. </div>
  22. </div>
  23. </form>
  24. </div>
  25. </section>
  26. <!-- ==================== 公共站点和分类 ==================== -->
  27. <section class="content-section container py-4">
  28. {# ===== 公共站点 ===== #}
  29. {% if public_sites %}
  30. {% set pub_cats_dict = {} %}
  31. {% for site in public_sites %}
  32. {% set cat_name = site.category.name if site.category else '未分类' %}
  33. {% if cat_name not in pub_cats_dict %}
  34. {% set pub_cats_dict = pub_cats_dict.update({cat_name: [site]}) or pub_cats_dict %}
  35. {% else %}
  36. {% set _ = pub_cats_dict[cat_name].append(site) %}
  37. {% endif %}
  38. {% endfor %}
  39. {% for cat_name, cat_sites in pub_cats_dict.items() %}
  40. <div class="category mb-4 p-3 bg-white rounded shadow-sm">
  41. <h4 class="category-title mb-3">
  42. <span class="category-accent" data-cat="{{ cat_name }}"></span>{{ cat_name }}
  43. </h4>
  44. <div class="sites-grid">
  45. {% for site in cat_sites %}
  46. <div class="site-card">
  47. <a href="{{ site.url }}" target="_blank" class="card-link">
  48. <div class="card site-item">
  49. <div class="card-top">
  50. {% if site.custom_icon %}
  51. <img src="{{ site.custom_icon }}" onerror="this.src='{{ url_for('static', filename='images/default-icon.png') }}'" class="favicon-img" alt="icon">
  52. {% elif site.icon %}
  53. <img src="{{ site.icon }}" onerror="this.src='{{ url_for('static', filename='images/default-icon.png') }}'" class="favicon-img" alt="icon">
  54. {% else %}
  55. <img src="{{ url_for('static', filename='images/default-icon.png') }}" class="favicon-img" alt="icon">
  56. {% endif %}
  57. <div class="site-info">
  58. <h6 class="site-name">{{ site.name }}</h6>
  59. {% if site.description %}
  60. <p class="site-desc">{{ site.description }}</p>
  61. {% endif %}
  62. </div>
  63. </div>
  64. </div>
  65. </a>
  66. </div>
  67. {% endfor %}
  68. </div>
  69. </div>
  70. {% endfor %}
  71. {% else %}
  72. {# 如果没有公共站点,显示空状态 #}
  73. <div class="empty text-center py-5">
  74. <i class="bi bi-bookmark display-3 text-muted"></i>
  75. <h5 class="text-secondary mt-3">暂无公共站点</h5>
  76. <p class="text-muted small">暂时没有可展示的公共站点,敬请期待</p>
  77. </div>
  78. {% endif %}
  79. </section>
  80. {% endblock %}
  81. {% block scripts %}
  82. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
  83. <style>
  84. body { background-color:#f5f7fa; font-family:"PingFang SC","Microsoft YaHei","Noto Sans SC",sans-serif; color:#333; }
  85. /* 分类标题 */
  86. .category-title {
  87. font-weight:600;
  88. font-size:1.1rem;
  89. display:flex;
  90. align-items:center;
  91. }
  92. .category-accent { width:5px; height:18px; border-radius:3px; margin-right:8px; display:inline-block; }
  93. /* 网格布局 */
  94. .sites-grid { display:grid; grid-template-columns:repeat(5,1fr); gap:14px; text-align:left; align-items:stretch; }
  95. .site-card { transition: transform 0.2s ease, box-shadow 0.3s ease; text-align:left; height:100%; }
  96. .site-card:hover { transform: translateY(-4px); }
  97. /* 卡片 */
  98. .card.site-item {
  99. border:1px solid transparent;
  100. border-radius:10px;
  101. background:rgba(255,255,255,0.95);
  102. backdrop-filter:blur(3px);
  103. box-shadow:0 2px 6px rgba(0,0,0,0.05);
  104. transition: all 0.3s ease;
  105. display:flex; flex-direction:column; justify-content:space-between; align-items:flex-start; height:100%; padding:0.7rem 0.8rem;
  106. }
  107. .card.site-item:hover {
  108. border-color:rgba(64,158,255,0.4);
  109. box-shadow:0 6px 14px rgba(64,158,255,0.25);
  110. transform: translateY(-4px);
  111. }
  112. /* 卡片内容 */
  113. .card-top { display:flex; align-items:center; gap:0.55rem; }
  114. .favicon-img { width:40px; height:40px; border-radius:8px; flex-shrink:0; object-fit:cover; transition:transform 0.25s ease; }
  115. .card.site-item:hover .favicon-img { transform:scale(1.1); }
  116. .site-info { flex:1; text-align:left; }
  117. /* 文字样式 */
  118. .site-name { font-size:0.95rem; font-weight:normal; color:#333; line-height:1.35; margin-bottom:0.1rem; }
  119. .site-desc { font-size:0.8rem; color:#777; line-height:1.35; margin:0; }
  120. /* 响应式 */
  121. @media (max-width:1200px){.sites-grid{grid-template-columns:repeat(4,1fr);}}
  122. @media (max-width:992px){.sites-grid{grid-template-columns:repeat(3,1fr);}}
  123. @media (max-width:768px){
  124. .sites-grid{grid-template-columns:repeat(2,1fr); gap:10px;}
  125. .favicon-img{width:34px;height:34px;}
  126. .site-name{font-size:0.9rem;}
  127. .site-desc{font-size:0.75rem;}
  128. }
  129. @media (max-width:480px){.sites-grid{grid-template-columns:repeat(1,1fr);}}
  130. </style>
  131. <script>
  132. const engineMap = { 'baidu':'https://www.baidu.com/s?wd=','bing':'https://www.bing.com/search?q=','google':'https://www.google.com/search?q=' };
  133. document.addEventListener('DOMContentLoaded', () => {
  134. const form = document.querySelector('.search-form');
  135. const input = document.querySelector('.search-input');
  136. const engineRadios = document.querySelectorAll('input[name="search-engine"]');
  137. const categoryAccents = document.querySelectorAll('.category-accent');
  138. const gradients = [
  139. ['#409EFF','#66b1ff'],
  140. ['#67C23A','#95d475'],
  141. ['#E6A23C','#f3c56b'],
  142. ['#F56C6C','#f89898'],
  143. ['#8E44AD','#b57edc'],
  144. ['#1ABC9C','#48C9B0'],
  145. ['#2D8CF0','#5CADFF']
  146. ];
  147. function hashStringToIndex(str, max) {
  148. let hash = 0;
  149. for (let i=0;i<str.length;i++) hash = (hash*31 + str.charCodeAt(i)) >>> 0;
  150. return hash % max;
  151. }
  152. categoryAccents.forEach(el => {
  153. const idx = hashStringToIndex(el.dataset.cat, gradients.length);
  154. const g = gradients[idx];
  155. el.style.background = `linear-gradient(135deg, ${g[0]}, ${g[1]})`;
  156. });
  157. function setEngineCookie(value) { document.cookie = `search-engine=${value}; max-age=${30 * 24 * 3600}; path=/`; }
  158. engineRadios.forEach(radio => { radio.addEventListener('change', ()=>setEngineCookie(radio.value)); });
  159. form.addEventListener('submit', e => {
  160. const engine = document.querySelector('input[name="search-engine"]:checked').value;
  161. setEngineCookie(engine);
  162. const query = input.value.trim();
  163. if(engine!=='local'){ e.preventDefault(); if(query) window.open(engineMap[engine]+encodeURIComponent(query),'_blank'); }
  164. else if(!query){ e.preventDefault(); alert('请输入搜索关键词'); }
  165. });
  166. });
  167. </script>
  168. {% endblock %}