diff --git a/assets/style.css b/assets/style.css index 5c9f166..8dff03d 100644 --- a/assets/style.css +++ b/assets/style.css @@ -919,11 +919,27 @@ body .content.expanded { transition: all 0.3s ease; } +/* 网站卡片 favicon 图片样式,与图标尺寸保持一致 */ +.site-card .favicon-icon { + display: inline-block; + width: 1.8rem; + height: 1.8rem; + margin-bottom: 1rem; + border-radius: 4px; + object-fit: cover; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + .site-card:hover i { transform: scale(1.1); color: var(--accent-hover); } +.site-card:hover .favicon-icon { + transform: scale(1.1); + box-shadow: 0 0 0 1px var(--border-color); +} + .site-card h3 { font-size: 1rem; margin-bottom: 0.5rem; diff --git a/src/generator.js b/src/generator.js index f8917aa..24c2362 100644 --- a/src/generator.js +++ b/src/generator.js @@ -303,6 +303,10 @@ function ensureConfigDefaults(config) { result.profile = result.profile || {}; result.social = result.social || []; result.categories = result.categories || []; + // 图标配置默认值 + result.icons = result.icons || {}; + // icons.mode: manual | favicon, 默认 favicon + result.icons.mode = result.icons.mode || 'favicon'; // 站点基本信息默认值 result.site.title = result.site.title || 'MeNav导航'; diff --git a/src/helpers/conditions.js b/src/helpers/conditions.js index 4cd3197..af0330a 100644 --- a/src/helpers/conditions.js +++ b/src/helpers/conditions.js @@ -159,6 +159,20 @@ function not(value, options) { return !value ? options.fn(this) : options.inverse(this); } +/** + * 判断URL是否为http/https + * @param {string} url 输入URL + * @param {object} options Handlebars选项 + * @returns {string} 渲染结果 + * @example {{#ifHttpUrl url}}...{{else}}...{{/ifHttpUrl}} + */ +function ifHttpUrl(url, options) { + if (typeof url === 'string' && /^https?:\/\//i.test(url)) { + return options.fn(this); + } + return options.inverse(this); +} + // 导出所有条件判断助手函数 module.exports = { ifEquals, @@ -169,5 +183,6 @@ module.exports = { and, or, orHelper, - not -}; \ No newline at end of file + not, + ifHttpUrl +}; diff --git a/src/helpers/utils.js b/src/helpers/utils.js index 1a8dbbc..0bd9128 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -166,6 +166,21 @@ function keys(object) { return Object.keys(object); } +/** + * 对字符串进行URL组件编码(encodeURIComponent) + * @param {string} text 输入文本 + * @returns {string} 编码后的字符串 + * @example {{encodeURIComponent url}} + */ +function encodeURIComponentHelper(text) { + if (text === undefined || text === null) return ''; + try { + return encodeURIComponent(String(text)); + } catch (e) { + return ''; + } +} + // 导出所有工具类助手函数 module.exports = { slice, @@ -175,5 +190,6 @@ module.exports = { last, range, pick, - keys -}; \ No newline at end of file + keys, + encodeURIComponent: encodeURIComponentHelper +}; diff --git a/src/script.js b/src/script.js index 74367b9..896fde0 100644 --- a/src/script.js +++ b/src/script.js @@ -53,7 +53,8 @@ window.MeNav = { element.setAttribute('data-description', newData.description); } if (newData.icon) { - const iconElement = element.querySelector('i'); + // 优先更新站点卡片中的回退图标(favicon模式下存在) + const iconElement = element.querySelector('i.icon-fallback') || element.querySelector('i'); if (iconElement) { iconElement.className = newData.icon; } @@ -178,16 +179,41 @@ window.MeNav = { newSite.setAttribute('data-icon', data.icon || 'fas fa-link'); newSite.setAttribute('data-description', data.description || ''); - // 添加内容 - newSite.innerHTML = ` - -
${data.description || ''}
- `; + // 添加内容(根据图标模式渲染) + try { + const cfg = window.MeNav && typeof window.MeNav.getConfig === 'function' ? window.MeNav.getConfig() : null; + const iconsMode = (cfg && (cfg.data?.icons?.mode || cfg.icons?.mode)) || 'favicon'; + if (iconsMode === 'favicon' && data.url && /^https?:\/\//i.test(data.url)) { + const faviconUrl = `https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(data.url)}&size=32`; + newSite.innerHTML = ` + +${data.description || ''}
+ `; + } else { + newSite.innerHTML = ` + +${data.description || ''}
+ `; + } + } catch (e) { + newSite.innerHTML = ` + +${data.description || ''}
+ `; + } // 添加到DOM sitesGrid.appendChild(newSite); + + // 移除"暂无网站"提示(如果存在) const emptyMessage = sitesGrid.querySelector('.empty-sites'); if (emptyMessage) { @@ -504,7 +530,7 @@ document.addEventListener('DOMContentLoaded', () => { const title = card.querySelector('h3')?.textContent?.toLowerCase() || ''; const description = card.querySelector('p')?.textContent?.toLowerCase() || ''; const url = card.href || card.getAttribute('href') || '#'; - const icon = card.querySelector('i')?.className || ''; + const icon = card.querySelector('i.icon-fallback')?.className || card.querySelector('i')?.className || ''; // 将卡片信息添加到索引中 searchIndex.items.push({ diff --git a/templates/components/site-card.hbs b/templates/components/site-card.hbs index a605ac1..5b7fa8f 100644 --- a/templates/components/site-card.hbs +++ b/templates/components/site-card.hbs @@ -7,8 +7,26 @@ data-url="{{url}}" data-icon="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}}" data-description="{{#if description}}{{description}}{{else}}{{url}}{{/if}}"> - + {{#ifEquals @root.icons.mode "favicon"}} + {{#ifHttpUrl url}} + +{{#if description}}{{description}}{{else}}{{url}}{{/if}}
-{{/if}} \ No newline at end of file +{{/if}}