修复一下 Handsome 主题顶部统计图表不显示的问题
问题点击导航栏饼图图标，下拉面板里各图表一直 Loading...原因统计图表由 core.min.js 驱动，点击饼图后依次：加载 ECharts → 请求统计数据 → 渲染图表，任一步失败都不显示。步骤失败原因加载 EChartsECHART_CDN 配错，路径拼成 .../echarts.min.js/echarts.min.js → 404请求数据接口 /?action=get_blog_statistic 被 CDN 缓存污染，返回首页 HTML 而非 JSON渲染图表前两步成功才会执行修复步骤core.min.js 是混淆文件，而且以后还要升级，不宜直接改。ECharts 路径可通过配置修正；统计接口 URL 写死在 core 里，只能在 footer 拦截 $.getJSON 改写请求地址。1. 修正 ECharts 路径编辑 usr/themes/handsome/component/header.php，将 ECHART_CDN 改为本地静态目录：ECHART_CDN: '<?php echo rtrim(STATIC_PATH, "/"); ?>/static/js',core 会拼上 /echarts.min.js，最终加载 {STATIC_PATH}static/js/echarts.min.js。使用自定义静态 CDN 时需同步该文件。2. 拦截统计接口编辑 usr/themes/handsome/component/footer.php，在 core.min.js 引用之后插入脚本。core 写死的 /?action=get_blog_statistic 在 CDN 下会拿到首页缓存，拦截后改走 /index.php?action=... 即可绕过。代码：[hide] [/hide]验证强刷页面，点击饼图图标，图表正常显示即可。
功能介绍恋爱块是 Handsome 主题侧边栏中的一个温馨小组件，用于展示情侣之间的相处时间。它包含两个头像、一个跳动的心形图标，以及实时更新的相处时长计时器。组件位置文件路径：/usr/themes/handsome/component/sidebar.php，放在sidebar块中，广告位置之前。参数修改方法1. 修改头像位置：找到以下两行代码 修改方法：将 nk= 后面的数字替换为你们的 QQ 号码获取 QQ 头像 URL 的方法：打开 https://q2.qlogo.cn/g?b=qq&nk=你的QQ号&s=640将 你的QQ号 替换为实际的 QQ 号码s=640 表示头像大小为 640×640 像素2. 修改初始时间位置：找到以下代码行var create_time = Math.round(new Date(Date.UTC(2025, 03, 17, 0, 0, 0)).getTime() / 1000);参数说明：参数含义范围2025年份任意年份03月份0-11（0=1月, 11=12月）17日期1-310小时0-230分钟0-590秒钟0-59修改示例：改成 2024 年 5 月 20 日 14:30:00var create_time = Math.round(new Date(Date.UTC(2024, 04, 20, 14, 30, 0)).getTime() / 1000);⚠️ 注意：月份是从 0 开始计数的1月 = 02月 = 13月 = 2...12月 = 113. 修改跳转链接位置：找到以下代码（出现两次）修改方法：将 https://xuanxi.love/ 替换为你想要的链接示例： 4. 调整动画速度位置：找到以下代码 修改方法：改变 2s 的值时间效果1.2s较快（每秒跳动 0.6 次）1.8s中等2s较慢（默认）2.4s更慢3s非常慢示例： 5. 修改标题文本位置：找到以下代码<?php _me("恋爱") ?>修改方法：改变引号内的文本示例：<?php _me("我们的故事") ?> <?php _me("Love") ?>6. 修改头像大小位置：找到以下代码（两个头像都有） 修改方法：改变 width 和 height 的值示例： 7. 修改 SVG 心形文件位置：找到以下代码 修改方法：将 src 属性中的 URL 替换为你的 SVG 文件地址示例： 如何获取 SVG 文件地址：将你的 SVG 文件上传到服务器或 CDN获取文件的完整 URL将 URL 替换到 src 属性中SVG 文件要求：格式：.svg 文件大小：建议 35×35 像素或更大（会自动缩放）颜色：支持任何颜色，建议与主题配色一致8. 修改心形大小位置：找到以下代码 修改方法：改变 width 和 height 的值示例： 9. 修改时间显示格式位置：找到以下代码currentTimeHtml = '一起走过 ' + currentTime[0] + '年 ' + currentTime[1] + '天 ' + currentTime[2] + '时';修改方法：改变字符串内容示例：// 显示完整时间（包括分钟） currentTimeHtml = '一起走过 ' + currentTime[0] + '年 ' + currentTime[1] + '天 ' + currentTime[2] + '时 ' + currentTime[3] + '分'; // 只显示天数 currentTimeHtml = '相处 ' + currentTime[1] + ' 天'; // 英文显示 currentTimeHtml = 'Together for ' + currentTime[0] + ' years ' + currentTime[1] + ' days';代码基础版 <?php _me("恋爱") ?> SVG 文件引入版（推荐） <?php _me("恋爱") ?> SVG 内嵌代码版（完整版） <?php _me("恋爱") ?> <path d="M5.1 831.1h253.5c2.8 0 5.1-2.3 5.1-5.1s-2.3-5.1-5.1-5.1H5.1c-2.8 0-5.1 2.3-5.1 5.1 0 2.9 2.3 5.1 5.1 5.1z m314.3 0h55.8c2.8 0 5.1-2.3 5.1-5.1s-2.3-5.1-5.1-5.1h-55.8c-2.8 0-5.1 2.3-5.1 5.1 0.1 2.9 2.3 5.1 5.1 5.1z m106.5 0h253.5c2.8 0 5.1-2.3 5.1-5.1s-2.3-5.1-5.1-5.1H425.9c-2.8 0-5.1 2.3-5.1 5.1 0 2.9 2.3 5.1 5.1 5.1z m314.4 0H796c2.8 0 5.1-2.3 5.1-5.1s-2.3-5.1-5.1-5.1h-55.8c-2.8 0-5.1 2.3-5.1 5.1 0.1 2.9 2.4 5.1 5.2 5.1z m106.4 0h172.4c2.8 0 5.1-2.3 5.1-5.1s-2.3-5.1-5.1-5.1H846.7c-2.8 0-5.1 2.3-5.1 5.1 0.1 2.9 2.3 5.1 5.1 5.1z" fill="#3B0404"></path> <path d="M409.6 803.5H86.9c-14 0-25.4-11.4-25.4-25.4 0-14 11.4-25.4 25.4-25.4h118.2c-10.1-3.4-17.3-12.9-17.3-24 0-10.9 6.8-20.1 16.4-23.7h-73.9c-14 0-25.4-11.4-25.4-25.4 0-14 11.4-25.4 25.4-25.4h130.1l-40.8-40.8-1.8-1.8c-88.1-88.1-88.1-231 0-319.1s231-88.1 319.1 0l1.8 1.8 1.8-1.8c88.1-88.1 231-88.1 319.1 0 88.1 88.1 88.1 231 0 319.1l-1.8 1.8L560.2 911c-11.9 11.9-31.1 11.9-43 0L409.6 803.5zM54.5 753.1c-14 0-25.4-11.4-25.4-25.4 0-14 11.4-25.4 25.4-25.4s25.4 11.4 25.4 25.4c0 14.1-11.4 25.4-25.4 25.4z" fill="#FF5E5E"></path> <path d="M668.1 228.8c67.9-9.7 139.3 11.6 191.5 63.8 88.1 88.1 88.1 231 0 319.1l-1.8 1.8L560.2 911c-11.9 11.9-31.1 11.9-43 0l-5.1-5.1 291.3-291.4 1.8-1.8c88.1-88.1 88.1-231 0-319.1-38.3-38.2-87-59.9-137.1-64.8z" fill="#CB2828"></path> <path d="M196 633.3c2 2 2 5.2 0 7.2s-5.2 2-7.2 0L165.3 617l-1-1-0.3-0.3-0.5-0.5c-54.3-54.3-77.4-130.8-63.8-204.9 0.5-2.8 3.1-4.6 5.9-4.1 2.8 0.5 4.6 3.1 4.1 5.9-13 70.9 9.1 144 61 195.9l0.5 0.5 0.3 0.3 1.1 1.1 23.4 23.4z m-85.3-226c-0.6 2.7-3.2 4.5-6 3.9-2.7-0.6-4.5-3.2-3.9-6 0.3-1.7 0.7-3.4 1.1-5.1 0.6-2.7 3.3-4.4 6.1-3.8 2.7 0.6 4.4 3.3 3.8 6.1-0.4 1.6-0.7 3.3-1.1 4.9z m17.4-51.2c-1.2 2.5-4.3 3.6-6.8 2.4s-3.6-4.3-2.4-6.8c11.1-23 26-44 44.5-62.5 44.3-44.3 103.3-68.1 164.6-67.7 2.8 0 5.1 2.3 5 5.1 0 2.8-2.3 5.1-5.1 5-58.6-0.3-115 22.4-157.4 64.7-17.5 17.7-31.8 37.8-42.4 59.8zM333 231.7c-2.8-0.1-5-2.4-4.9-5.2 0.1-2.8 2.4-5 5.2-4.9 1.7 0 3.5 0.1 5.2 0.2 2.8 0.1 4.9 2.5 4.8 5.3-0.1 2.8-2.5 4.9-5.3 4.8-1.7-0.1-3.4-0.2-5-0.2z m272.4-6c2.7-0.5 5.4 1.3 5.9 4s-1.3 5.4-4 5.9c-42.8 8.3-82.3 29-113.9 60.6l-0.4 0.4-1.5 1.5c-2 2-5.2 2-7.2-0.1-0.6-0.6-1.2-1.2-1.7-1.8-26.9-26.9-59.7-46-95.5-56.2-2.7-0.8-4.3-3.6-3.5-6.3 0.8-2.7 3.6-4.3 6.3-3.5 36.7 10.4 70.3 29.8 98.2 57 32.6-31.9 73.2-53 117.3-61.5z m6.6 9c-2.8 0.5-5.4-1.4-5.9-4.1-0.5-2.8 1.4-5.4 4.1-5.9 1.7-0.3 3.4-0.6 5.1-0.8 2.8-0.4 5.3 1.5 5.8 4.3 0.4 2.8-1.5 5.3-4.3 5.8-1.5 0.2-3.2 0.5-4.8 0.7z m54.6-2.5c-2.8-0.2-4.9-2.7-4.7-5.4 0.2-2.8 2.7-4.9 5.4-4.7 54.4 4.2 105.7 27.5 145.1 66.9 23.1 23.1 40.7 50.3 52.2 80.2 1 2.6-0.3 5.5-2.9 6.6-2.6 1-5.5-0.3-6.6-2.9-11-28.5-27.8-54.6-49.9-76.7-37.6-37.7-86.6-59.9-138.6-64zM857 377.5c-0.9-2.6 0.4-5.5 3.1-6.5 2.6-0.9 5.5 0.4 6.5 3.1 0.6 1.6 1.1 3.3 1.7 4.9 0.9 2.7-0.6 5.5-3.2 6.4-2.7 0.9-5.5-0.6-6.4-3.2-0.6-1.6-1.2-3.1-1.7-4.7z m11.9 53.1c-0.3-2.8 1.8-5.3 4.6-5.5 2.8-0.3 5.3 1.8 5.5 4.6 6.7 68.3-17 136.2-66.5 185.7l-1.8 1.8-36.8 36.8c-2 2-5.2 2-7.2 0s-2-5.2 0-7.2l36.8-36.8c0.6-0.6 1.2-1.2 1.8-1.7 47.3-47.5 69.9-112.5 63.6-177.7zM763.1 650.2c2-2 5.2-2 7.2 0s2 5.2 0 7.2l-3.6 3.6c-2 2-5.2 2-7.2 0s-2-5.2 0-7.2l3.6-3.6z m-39.4 39.5c2-2 5.2-2 7.2 0s2 5.2 0 7.2L551.6 876.1c-2 2-5.2 2-7.2 0s-2-5.2 0-7.2l179.3-179.2zM540.9 872.5c2-2 5.2-2 7.2 0s2 5.2 0 7.2l-3.6 3.6c-2 2-5.2 2-7.2 0s-2-5.2 0-7.2l3.6-3.6z m-39.1 38.3c-9.8 6.4-22.9 5.2-31.4-3.1L367.8 805.1c-2-2-5.2-2-7.2 0s-2 5.2 0 7.2L463.3 915c12 11.6 30.3 13.3 44.1 4.4 2.3-1.5 3-4.7 1.5-7-1.6-2.4-4.7-3.1-7.1-1.6z" fill="#3B0404"></path> <path d="M494.3 514.2c-8.4 0-15.2-6.8-15.2-15.2s6.8-15.2 15.2-15.2c8.4 0 15.2 6.8 15.2 15.2s-6.8 15.2-15.2 15.2z m91.3 0c-8.4 0-15.2-6.8-15.2-15.2s6.8-15.2 15.2-15.2 15.2 6.8 15.2 15.2-6.8 15.2-15.2 15.2zM465.1 583.3c18.7 22.9 46.6 36.5 76.7 36.5 29.2 0 56.3-12.7 74.9-34.4 1.8-2.1 1.6-5.3-0.5-7.2-2.1-1.8-5.3-1.6-7.2 0.5-16.8 19.5-41.1 30.9-67.3 30.9-27 0-52.1-12.2-68.8-32.7-1.8-2.2-5-2.5-7.1-0.7s-2.5 5-0.7 7.1z" fill="#3B0404"></path> <path d="M428.4 559.9c-8.4 0-15.2-6.8-15.2-15.2 0-8.4 6.8-15.2 15.2-15.2 8.4 0 15.2 6.8 15.2 15.2 0 8.4-6.8 15.2-15.2 15.2z m223.1 0c-8.4 0-15.2-6.8-15.2-15.2 0-8.4 6.8-15.2 15.2-15.2s15.2 6.8 15.2 15.2c0 8.4-6.8 15.2-15.2 15.2z" fill="#CB2828"></path> <path d="M332.1 483.8c-30.8 0-55.8-25-55.8-55.8s25-55.8 55.8-55.8 55.8 25 55.8 55.8-25 55.8-55.8 55.8z m415.8 0c-30.8 0-55.8-25-55.8-55.8s25-55.8 55.8-55.8 55.8 25 55.8 55.8-25 55.8-55.8 55.8z" fill="#F54040"></path> <path d="M871.4 125.7v-0.3c0-14.9 12.1-26.9 26.9-26.9s26.9 12.1 26.9 26.9V160.6c0 10.5-8.5 19-19 19H871c-14.9 0-26.9-12.1-26.9-26.9s12.1-26.9 26.9-26.9c0.2-0.1 0.3-0.1 0.4-0.1z" fill="#FF5E5E"></path> 代码参考了小刘同学的文章。爱心下载我找了四个爱心svg，有需要的话可以下载使用：[hide]链接：https://pan.quark.cn/s/b63c88fb9040?pwd=CDSg提取码：CDSg[/hide]
春节到了，给typecho博客网站添加两对3D红灯笼
今天是农历腊月18，距离小年23还有5天，距离春节还有12天，给网站上加俩红灯笼🏮，增添一下气氛。普通版这个版本的是从网上教程里找到的，效果如下：完整denglong.js代码：function createDengContainer() { const container = document.createElement('div'); container.className = 'deng-container'; // 从当前脚本的 URL 获取参数 const scriptSrc = document.currentScript.src; const urlParams = new URLSearchParams(scriptSrc.split('?')[1]); // 获取 '?' const customText = urlParams.get('text'); // 获取参数名为'text'的值 // 将获取的文本分割为字符数组，如果没有提供文本，则使用默认的“新年快乐” const texts = customText ? customText.split('') : ['新', '年', '快', '乐']; texts.forEach((text, index) => { const box = document.createElement('div'); box.className = `deng-box deng-box${index + 1}`; const deng = document.createElement('div'); deng.className = 'deng'; const xian = document.createElement('div'); xian.className = 'xian'; const dengA = document.createElement('div'); dengA.className = 'deng-a'; const dengB = document.createElement('div'); dengB.className = 'deng-b'; const dengT = document.createElement('div'); dengT.className = 'deng-t'; dengT.textContent = text; dengB.appendChild(dengT); dengA.appendChild(dengB); deng.appendChild(xian); deng.appendChild(dengA); const shuiA = document.createElement('div'); shuiA.className = 'shui shui-a'; const shuiC = document.createElement('div'); shuiC.className = 'shui-c'; const shuiB = document.createElement('div'); shuiB.className = 'shui-b'; shuiA.appendChild(shuiC); shuiA.appendChild(shuiB); deng.appendChild(shuiA); box.appendChild(deng); container.appendChild(box); }); document.body.appendChild(container); } // 添加CSS样式 function addStyles() { const style = document.createElement('style'); style.type = 'text/css'; style.textContent = ` .deng-container { position: relative; overflow: hidden; top: 10px; opacity: 0.9; z-index: 9999; pointer-events: none; } .deng-box { position: fixed; right: 10px; top: 12px; } .deng-box1 { position: fixed; top: 15px; left: 20px; } .deng-box2 { position: fixed; top: 12px; left: 130px; } .deng-box3 { position: fixed; top: 10px; right: 120px; } .deng { position: relative; width: 120px; height: 90px; background: rgba(216, 0, 15, .8); border-radius: 50% 50%; animation: swing 3s infinite ease-in-out; box-shadow: -5px 5px 50px 4px #fa6c00; } .deng-a { width: 100px; height: 90px; background: rgba(216, 0, 15, .1); border-radius: 50%; border: 2px solid #dc8f03; margin-left: 7px; display: flex; justify-content: center; } .deng-b { width: 65px; height: 83px; background: rgba(216, 0, 15, .1); border-radius: 60%; border: 2px solid #dc8f03; } .xian { position: absolute; top: -20px; left: 60px; width: 2px; height: 20px; background: #dc8f03; } .shui-a { position: relative; width: 5px; height: 20px; margin: -5px 0 0 59px; animation: swing 4s infinite ease-in-out; transform-origin: 50% -45px; background: orange; border-radius: 0 0 5px 5px; } .shui-b { position: absolute; top: 14px; left: -2px; width: 10px; height: 10px; background: #dc8f03; border-radius: 50%; } .shui-c { position: absolute; top: 18px; left: -2px; width: 10px; height: 35px; background: orange; border-radius: 0 0 0 5px; } .deng:before, .deng:after { content: " "; display: block; position: absolute; border-radius: 5px; border: solid 1px #dc8f03; background: linear-gradient(to right, #dc8f03, orange, #dc8f03, orange, #dc8f03); } .deng:before { top: -7px; left: 29px; height: 12px; width: 60px; z-index: 999; } .deng:after { bottom: -7px; left: 10px; height: 12px; width: 60px; margin-left: 20px; } .deng-t { font-family: '华文行楷', Arial, Lucida Grande, Tahoma, sans-serif; font-size: 3.2rem; color: #dc8f03; font-weight: 700; line-height: 85px; text-align: center; } @media (max-width: 768px) { .deng-t { font-size: 2.5rem; } .deng-box1,.deng-box2{ transform: scale(0.5); transform-origin: left top ; } .deng-box3,.deng-box4 { transform: scale(0.5); transform-origin: right top ; } .deng-box1 { position: fixed; top: 8px; left: 5px; } .deng-box2 { position: fixed; top: 8px; left: 60px; } .deng-box3 { position: fixed; top: 8px; right: 60px; } .deng-box4 { position: fixed; top: 8px; right: 5px; } } @keyframes swing { 0% { transform: rotate(-10deg); } 50% { transform: rotate(10deg); } 100% { transform: rotate(-10deg); } } `; document.head.appendChild(style); } // 引入时调用 function init() { addStyles(); createDengContainer(); } // 调用初始化函数 init();3D改进版这个版本是我在普通版基础上做的改动，结合了3D效果，并优化了一下细节，效果如下：完整版denglong.js代码：[hide]function createDengContainer() { const container = document.createElement('div'); container.className = 'deng-container'; const scriptSrc = document.currentScript ? document.currentScript.src : ''; const urlParams = new URLSearchParams(scriptSrc.split('?')[1]); const customText = urlParams.get('text'); const texts = customText ? customText.split('') : ['新', '年', '快', '乐']; texts.forEach((text, index) => { const box = document.createElement('div'); box.className = `deng-box deng-box${index + 1}`; // 3D 容器 (负责摆动) const lantern3D = document.createElement('div'); lantern3D.className = 'lantern-3d'; // 吊线 const xian = document.createElement('div'); xian.className = 'xian'; // --- 【核心修改 2】开始：创建简单的盖子 --- // 不再需要复杂的层叠结构，回归最简单的单层 div const capTop = document.createElement('div'); capTop.className = 'lantern-cap-top'; // 添加提环 const capLoop = document.createElement('div'); capLoop.className = 'cap-loop'; capTop.appendChild(capLoop); const capBottom = document.createElement('div'); capBottom.className = 'lantern-cap-bottom'; // --- 【核心修改 2】结束 --- // 灯笼主体 (负责自转) const lanternBody = document.createElement('div'); lanternBody.className = 'lantern-body'; // 内部光源 const lanternLight = document.createElement('div'); lanternLight.className = 'lantern-light'; lanternBody.appendChild(lanternLight); // 创建瓣片 for (let i = 0; i < 10; i++) { const rib = document.createElement('div'); rib.className = 'rib'; rib.style.transform = `rotateY(${i * 36}deg)`; lanternBody.appendChild(rib); } // 文字 const textDeng = document.createElement('div'); textDeng.className = 'deng-t'; textDeng.textContent = text; // 吊尾 const tassel = document.createElement('div'); tassel.className = 'tassel-total'; // --- 【核心修改 1】开始：中国结改回平面结构 --- tassel.innerHTML = ` `; // --- 【核心修改 1】结束 --- // --- 组装 --- lantern3D.appendChild(xian); // 【关键一步】：把盖子加到 lantern3D，而不是 lanternBody // 这样它们就不会跟着主体自转了！ lantern3D.appendChild(capTop); lantern3D.appendChild(capBottom); lantern3D.appendChild(lanternBody); // 主体在盖子后面转 lantern3D.appendChild(textDeng); lantern3D.appendChild(tassel); box.appendChild(lantern3D); container.appendChild(box); }); document.body.appendChild(container); } function addStyles() { const style = document.createElement('style'); style.type = 'text/css'; style.textContent = ` .deng-container { position: fixed; top: 40px; left: 0; width: 100%; height: 0; z-index: 99999; pointer-events: none; perspective: 800px; } .deng-box { position: fixed; z-index: 999; } .deng-box1 { left: 40px; top: -10px; animation-delay: 0s; } .deng-box2 { left: 180px; top: 30px; animation-delay: 0.5s; } .deng-box3 { right: 180px; top: 30px; animation-delay: 1s; } .deng-box4 { right: 40px; top: -10px; animation-delay: 1.5s; } .xian { position: absolute; left: 60px; width: 3px; background: #ffca28; height: 1000px; top: -1000px; box-shadow: 0 0 5px rgba(255, 202, 40, 0.6); z-index: 1; } /* 容器负责整体摆动 */ .lantern-3d { position: relative; width: 120px; height: 100px; transform-style: preserve-3d; transform-origin: 50% 0; animation: swingNatural 5s infinite ease-in-out; } /* 主体负责自转 */ .lantern-body { position: absolute; width: 100%; height: 100%; transform-style: preserve-3d; animation: rotateBody 18s infinite linear; z-index: 5; /* 确保在盖子下面一点 */ } .rib { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 50%; border: 1px solid rgba(255, 202, 40, 0.6); box-sizing: border-box; background: radial-gradient(circle at 50% 50%, rgba(216,0,15,0) 20%, rgba(216,0,15,0.7) 60%, rgba(216,0,15,0.95) 100%); backface-visibility: visible; } /* --- 【核心修改 2】CSS：简化的、不旋转的盖子 --- */ .lantern-cap-top, .lantern-cap-bottom { position: absolute; left: 35px; width: 50px; height: 12px; /* 使用明亮的金色渐变，看起来像金属 */ background: linear-gradient(to bottom, #ffd700, #ffca28, #b8860b); border: 1px solid #ffca28; border-radius: 4px; z-index: 20; /* 确保盖在旋转体上面 */ /* 增加一点投影，制造悬浮在表面的立体感 */ box-shadow: 0 2px 5px rgba(0,0,0,0.4); } .lantern-cap-top { top: -6px; border-bottom: none; } .lantern-cap-bottom { bottom: -6px; border-top: none; } /* 提环 */ .cap-loop { position: absolute; left: 18px; top: -8px; width: 14px; height: 8px; border: 2px solid #ffca28; border-bottom: none; border-radius: 10px 10px 0 0; } /* --- 【核心修改 2】结束 --- */ .lantern-light { position: absolute; width: 60px; height: 60px; top: 20px; left: 30px; background: #ffeb3b; border-radius: 50%; filter: blur(18px); opacity: 0.9; animation: flicker 3s infinite ease-in-out; } .deng-t { position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; font-size: 3rem; color: #ffca28; font-weight: 700; font-family: "华文行楷", "KaiTi", serif; text-shadow: 0 0 5px #ff6a00, 0 0 20px #ff0000; transform: translateZ(62px); backface-visibility: hidden; -webkit-font-smoothing: antialiased; z-index: 30; } .tassel-total { position: absolute; top: 100px; left: 60px; width: 0; height: auto; transform-style: preserve-3d; animation: tasselSwing 5s infinite ease-in-out; animation-delay: 0.5s; } .tassel-bead { position: absolute; left: -6px; top: 5px; width: 12px; height: 12px; background: radial-gradient(circle at 30% 30%, #fff, #ef5350); border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.3); z-index: 5; } /* --- 【核心修改 1】CSS：改回扁平的中国结 --- */ .tassel-knot-flat { position: absolute; left: -8px; top: 18px; width: 16px; height: 16px; background: #d8000f; /* 纯色背景 */ border: 1px solid #ffca28; transform: rotate(45deg); /* 旋转成菱形 */ box-shadow: 0 2px 5px rgba(0,0,0,0.3); z-index: 4; /* 移除圆角和球体渐变 */ } /* --- 【核心修改 1】结束 --- */ .tassel-threads { position: absolute; left: -7px; top: 32px; width: 14px; height: 70px; background: repeating-linear-gradient(90deg, #d8000f, #d8000f 2px, #ff5252 2.5px, #d8000f 3px); border-radius: 2px 2px 5px 5px; mask-image: linear-gradient(to bottom, black 80%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, black 80%, transparent 100%); } @keyframes flicker { 0%, 100% { opacity: 0.8; transform: scale(1); } 50% { opacity: 1; transform: scale(1.05); } } @keyframes swingNatural { 0% { transform: rotateX(-4deg) rotateZ(-2deg); } 25% { transform: rotateX(2deg) rotateZ(3deg); } 50% { transform: rotateX(4deg) rotateZ(2deg); } 75% { transform: rotateX(-2deg) rotateZ(-3deg); } 100% { transform: rotateX(-4deg) rotateZ(-2deg); } } @keyframes tasselSwing { 0% { transform: rotateX(-5deg) rotateZ(-5deg); } 50% { transform: rotateX(5deg) rotateZ(5deg); } 100% { transform: rotateX(-5deg) rotateZ(-5deg); } } @keyframes rotateBody { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } } @media (max-width: 768px) { .deng-box { transform: scale(0.6); } .deng-box1 { left: 10px; } .deng-box2 { left: 80px; } .deng-box3 { right: 80px; } .deng-box4 { right: 10px; } } `; document.head.appendChild(style); } function init() { addStyles(); createDengContainer(); } init();[/hide]引用方式1、新建一个denglong.js文件放在你的网站的资源目录下。2、在网站首页的 前引用，比如： 3、handsome主题直接放在自定义尾部即可4、text=春节快乐，text后面的四个字即显示在四个灯笼里面的字，默认是“新年快乐”
CategoryBatchManager - Typecho后台文章分类批量管理插件
插件简介CategoryBatchManager 是一款 Typecho 后台文章分类批量管理插件，主要功能：在 文章管理列表 中提供批量操作：“移动到分类…”：将选中文章的分类替换为所选分类；“添加分类…”：在原有分类基础上追加新分类；“移除分类…”：从文章已有分类中移除某些分类。在文章列表每一行的 “分类”列 尾部增加一个小的 “分类” 链接：点击可快速为单篇文章调整分类。整体操作基于 AJAX 完成，避免整页刷新，提升后台管理效率。环境要求Typecho：版本 1.2.1PHP：版本 7.0.0 及以上（兼容 PHP 8.0）插件在激活时会自动检查当前环境版本，如果版本过低，会在后台插件页给出明确错误提示，防止在不兼容环境中运行。安装步骤将插件代码放入 Typecho 的插件目录：路径结构如下：usr/ plugins/ CategoryBatchManager/ Plugin.php Action.php登录 Typecho 后台 → 控制台 → 插件，找到：插件名称：CategoryBatchManager（文章分类批量管理）点击 “启动” 插件：如果 PHP / Typecho 版本不满足要求，会直接提示：CategoryBatchManager 需要 PHP 7.0 或更高版本CategoryBatchManager 需要 Typecho 1.2.1符合要求则启动成功。功能使用说明1. 批量分类操作打开后台：控制台 → 文章 → 所有文章。在列表顶部/底部的 “选中项” 下拉菜单中，你会看到新增的 3 个选项：“移动到分类…”“添加分类…”“移除分类…”使用步骤：勾选需要操作的多篇文章（左侧复选框）。点击其中一个批量分类操作（例如“移动到分类…”）。页面会弹出一个分类选择弹窗，特点：列出所有分类，按照层级缩进展示，支持子分类；顶部带有 搜索框，输入可筛选分类；支持多选分类（按住 Ctrl/多次勾选）。选择好目标分类后点击 “确定”：插件会通过 AJAX 请求 /action/category-batch；后台更新 table.relationships 中 cid-mid 的分类关系；前端会直接更新列表中对应文章的“分类”列，无需刷新整页。三种操作的具体逻辑：移动到分类（move）对选中文章：先删除该文章所有“分类”关联（仅限 metas.type = 'category' 的关系），再写入你新选择的分类。适用于“文章从 A 版块整体迁移到 B 版块”。例如：将此三篇文章从实践教程移动到性能测试添加分类（add）在当前已有分类基础上追加你新选择的分类；已存在的关系不会重复添加。适用于“批量为一批文章增加一个额外分类”。例如：为飞牛nas分类下的三篇文章添加环境配置分类移除分类（remove）仅删除你选中的那些分类与文章的关系；未勾选的其它分类不受影响。适用于“批量清理某个分类下的文章归属”。例如：移除上面加的环境配置分类2. 单篇文章快速分类修改在 文章列表的“分类”列 中，每一行分类的后面会有一个小的 灰色“分类” 链接（字体较小）：点击这一行的小“分类”链接：插件会自动读取当前这一篇文章所属的分类（解析该单元格内原有的分类链接 URL 中的 category=mid 参数）。弹出与批量操作相同的分类选择弹窗。弹窗内会 预先勾选该文章当前的分类。在弹窗中重新勾选你想要的分类，然后点击 “确定”：对该篇文章执行 “移动到分类” 操作，即覆盖原分类关系（可单选可多选）。列表中的这一行“分类”列会立即更新。这个功能特别适合对少数文章进行微调，而不必进入 write-post.php 编辑页面。3. 批量分类刷新计数在 分类管理页面（manage-categories.php） 的顶部左侧会自动出现一个 "刷新分类计数" 按钮：位置：分类管理页面顶部左侧，紧挨着现有的操作按钮样式：蓝色按钮 .btn btn-s，与 Typecho 后台风格一致功能：遍历数据库中所有分类（type='category'）重新统计每个分类下已发布文章的数量更新 table.metas 表中对应的 count 字段权限与安全所有批量操作都基于 Typecho 的后台权限体系：仅当用户已登录并且具备 编辑及以上权限（$user->pass('editor', true)）时，才允许调用批量分类接口。后端接口路径：POST /action/category-batch参数：do=batchop=move|add|removecid[]：文章 ID 列表mid[]：分类 ID 列表插件只会操作 metas.type = 'category' 的关系，不会影响标签等其它 metas。常见问题看不到新增的批量菜单项？确认已启动 CategoryBatchManager 插件；确认当前页面是 manage-posts.php（文章管理列表），插件只在这个页面注入 JS/HTML；清理浏览器缓存后再试。分类弹窗里没有任何分类？检查后台是否已经创建了分类；插件通过 \Widget\Metas\Category\Rows 获取分类数据，与编辑文章页面的分类列表是同一来源。操作后分类没变？看浏览器控制台是否有 AJAX 请求报错；检查当前登录用户是否拥有 editor 以上权限。下载地址[hide]Github:https://github.com/BXCQ/CategoryBatchManager迅雷云盘：https://pan.xunlei.com/s/VOjPLgIL7aT8KHwit5tGQu9SA1?pwd=umje#[/hide]
发现在bing中能搜索到，但没展示完整，可能是第一次发的字太少了。现在用ai重写了，首页也暂时去掉了倒计时，然后等60天的时候我再回来。给handsome主题的首页加一个精美的倒计时组件，支持多种节日倒计时，包括春节、元旦、情人节等。具有现代化的设计风格，完美适配响应式布局和夜间模式。功能特性现代化设计：采用卡片式设计，支持圆角、阴影和渐变效果响应式布局：完美适配手机、平板和桌面设备夜间模式支持：自动适配主题的暗色模式实时更新：每秒自动刷新倒计时高度可定制：支持自定义日期、标题、颜色和样式动画效果：秒数显示带有脉冲动画效果多节日支持：可轻松切换为不同节日的倒计时效果预览已支持响应式和夜间模式。效果如下：快速使用方法一：通过后台设置（推荐）登录您的Handsome主题后台进入 开发者设置 页面找到 首页列表最前方广告位 选项将下方完整代码复制粘贴到该位置保存设置即可方法二：直接编辑主题文件如果熟悉代码编辑，也可以直接编辑主题文件：文件位置：usr/themes/handsome/在首页模板文件中添加代码代码如下：[hide] 2026年 - 春节倒计时 -- 天 -- 时 -- 分 -- 秒 [/hide]
为什么需要插件监控？作为 Typecho 博客的站长，你是否遇到过以下困扰：插件过多导致性能下降：安装了太多插件，不知道哪些在拖慢网站速度数据库臃肿：插件创建的数据表越来越多，数据库变得臃肿不堪内存占用不明：不知道哪些插件占用了大量内存资源插件管理混乱：忘记哪些插件已启用，哪些插件长期闲置PluginMonitor 插件正是为解决这些问题而设计的。核心功能系统信息监控PHP 版本检测：实时显示服务器 PHP 版本信息内存使用监控：当前内存使用量、峰值内存、内存限制数据库大小统计：分别显示插件数据库占用和总数据库大小插件数量统计：已启用/已安装插件数量对比插件状态监控启用状态：清晰显示每个插件的启用/禁用状态版本信息：显示插件版本号和作者信息文件统计：统计每个插件的文件数量空间占用：精确计算每个插件占用的磁盘空间描述信息：显示插件的功能描述智能数据库分析表类型识别：自动区分系统表和插件表存储引擎：显示每个表的存储引擎类型数据行数：统计表中的数据行数表大小：精确计算每个表占用的存储空间安装与使用下载地址[hide]https://github.com/BXCQ/PluginMonitor[/hide]启用插件登录 Typecho 后台进入「控制台」->「插件」找到「插件监控工具」并启用启用后在后台管理菜单中会出现「插件监控」选项使用环境Typecho 版本：1.2.1PHP 版本：8.0 及以上数据库：MySQL配置选项自动刷新设置不自动刷新：手动刷新，节省服务器资源5秒/10秒/30秒/60秒：根据需求设置自动刷新间隔显示选项显示未启用的插件：选择是否在列表中显示未启用的插件数据库表显示范围：仅显示插件创建的表（默认）显示所有表（系统+主题+插件）实际使用效果系统概览使用插件后，可以在一个页面看到：PHP 版本：8.0.15内存使用：45.2MB / 512MB峰值内存：67.8MB插件数据库占用：2.3MB数据库总大小：15.7MB已启用/已安装数量：12 / 25插件列表每个插件都会显示详细信息：插件名称：ModernAdmin - 一个美化 Typecho 后台的插件版本：1.5.1作者：璇状态：已启用文件数：47占用空间：2.1MB数据库表分析插件表：显示插件创建的数据表系统表：Typecho 核心表（comments、contents、metas 等）表大小：精确到字节的存储空间统计界面设计特色响应式设计桌面端：完整的表格显示，信息一目了然移动端：自适应布局，关键信息优先显示深色模式：支持系统深色模式，护眼更舒适用户体验优化固定列宽：防止内容过长影响布局状态标识：用颜色区分插件启用状态悬停提示：鼠标悬停显示详细信息一键刷新：手动刷新按钮，实时更新数据总结PluginMonitor 插件为 Typecho 博客管理带来了全新的体验：全面监控：系统、插件、数据库三位一体监控 直观显示：清晰的数据展示和状态标识 智能分析：自动识别插件表和系统表 性能优化：帮助发现和解决性能问题 开源免费：MIT 协议，完全开源 如果你是 Typecho 博客的站长，强烈推荐安装这个插件。它不仅能让用户更好地了解博客的运行状态，还能帮助优化性能，提升用户体验。本文由 PluginMonitor 插件作者撰写，如有问题欢迎在评论区反馈。
前言在自定义主题的某天，AI摘要突然不显示了，前前后后改了好几次，通过重启插件无数次、移除全部自定义js和css、关闭pjax、一一恢复改过的页面代码，均未果。这次是通过本文第二个方法改好的，所以至今不知道突然不显示的原因。下面是解决AISummary插件的摘要块不显示的三种方案，前提是摘要内容已正确生成且保存。针对handsome主题，其他主题可借鉴思路。技术环境博客系统: Typecho 1.2.1主题: Handsome 10.1.0插件: AISummary v1.1.0解决方案方案一：修改插件核心逻辑直接修改 Plugin.php 中的 customContent 方法，将摘要插入到内容末尾：// 修改前 $content = $summaryString . $content; // 修改后 $content = $content . $summaryString;方案二：主题模板直接集成（推荐）在主题模板文件中直接添加摘要显示逻辑，绕过插件的 customContent 方法。实施步骤在插件设置中将"正文摘要显示样式"设置为"默认"或者"自定义"，推荐设置为自定义。在主题模板中添加摘要显示代码在 usr/themes/handsome/post.php 第74行的 下方添加：[hide] <?php // 添加AI摘要显示 $options = Typecho_Widget::widget('Widget_Options')->plugin('AISummary'); if ($options->summaryStyle !== '0') { $summaryField = $options->field ?: 'summary'; $db = Typecho_Db::get(); $row = $db->fetchRow($db->select('str_value')->from('table.fields')->where('cid = ?', $this->cid)->where('name = ?', $summaryField)); if ($row && !empty($row['str_value'])) { $pureSummary = str_replace("{{text}}", $row['str_value'], $options->prefix); // 根据插件配置选择样式 if ($options->summaryStyle === '1') { // 使用默认引用样式 $summaryString = ' ' . $pureSummary . ' '; } else if ($options->summaryStyle === '2') { // 使用自定义样式，只添加class，让CSS控制样式 $summaryString = ' ' . $pureSummary . ' '; } else { // 默认样式 $summaryString = ' ' . $pureSummary . ' '; } echo $summaryString; } } ?>[/hide]这种方式不依赖于Typecho的钩子系统，直接在主题模板中添加摘要显示代码。方案三：针对开启了前台Vditor解析方式第一种：1、打开文件：usr/themes/handsome/libs/content/PostContent.php2、找到第382行（大约在第380-385行之间）3、将$content = $obj->text;改为$content = $obj->content;4、保存文件$obj->text是vditor原始的Markdown文本，没有经过任何插件处理$obj->content是经过Typecho插件系统处理后的HTML内容，包括AISummary插件的customContent方法添加的摘要通过使用$obj->content，确保所有插件的钩子都能正常工作第二种：在handsome插件后台切换vidtor为typecho自带的即可下面是自定义模板的文章：[post cid="335" /]
概述FriendsRSS 是一款专为 Typecho 博客系统设计的智能友链 RSS/Atom 聚合插件。它能够自动检测、解析和聚合您友链博客的最新文章，通过多种展示方式（短代码、API接口、RSS输出、前台页面）让您的博客成为友圈动态的聚合中心。插件功能核心功能友链来源集成：从 Typecho links 表的指定分类读取友链，自动参与聚合。兼容性增强：当指定分类不存在时，自动获取所有友链，完美兼容只有一种友链的主题博客。排除功能：支持配置排除检测的博客网址，每行一个，支持部分匹配。RSS 自动检测：采用"三轮检测"策略（常用路径/feed和/feed/ → HTML 解析 → 其他常见路径），并对检测结果进行 10天缓存（RSS地址相对稳定）。定时检测功能：支持配置定时检测RSS地址间隔（默认240小时/10天），RSS地址缓存时间自动与定时检测间隔保持一致。定时解析功能：支持配置定时解析间隔（默认6小时），提供独立的定时任务脚本，支持命令行和HTTP调用。手动配置 RSS：后台提供"选择友链 + 输入 RSS 地址"的表单，保存到 rss_config.json 作为覆盖与纠偏。短代码功能：在任意文章或页面中使用 rss 短代码来显示友链RSS聚合内容，支持 limit 参数控制显示文章数量。多格式支持：兼容 RSS 2.0 / Atom 1.0 / RSS 1.0，解析标题、链接、作者、摘要与发布时间等字段。文章聚合与排序：为每个博客限制抓取数量，并按发布时间 倒序 排列，支持聚合总数上限。缓存与并发保护：聚合结果持久化缓存，内置 锁文件 防止并发重复任务；可读取已有缓存以快速展示。日志记录：记录检测、解析与网络状态，便于排查异常与性能问题。输出功能RSS订阅地址：/action/friends-rss?do=rss - 生成标准RSS 2.0格式的聚合订阅源API接口地址：/action/friends-rss?do=page - 返回JSON格式的聚合文章数据前台展示页面：/action/friends-rss?do=pageview - 美观的前台展示页面，支持响应式设计[collapse status="false" title="示例图片查看"][/collapse]管理界面统计卡片：显示友链数量、文章数量、最后更新时间、已配置RSS数量等统计信息操作卡片：定时检测RSS地址：显示当前检测间隔和下次执行时间，提供"立即执行"按钮定时解析RSS内容：显示当前解析间隔和下次执行时间，提供"立即执行"按钮添加RSS地址：手动配置友链RSS地址的表单界面访问地址区块：显示所有可用的访问地址和友链分类信息友链与文章列表：显示友链列表和最新聚合文章，支持RSS配置状态标识日志显示区域：实时显示操作日志，支持自动滚动到最新内容短代码功能基本用法：在任意文章或页面中使用 rss 短代码来显示友链RSS聚合内容参数支持：支持 limit 参数控制显示文章数量块状设计：使用简洁美观的块状设计，包含博客信息、文章标题、摘要等响应式布局：自动适配移动端设备，优化显示效果网络与兼容性优化请求优化：优先使用 cURL，请求超时与重定向受控内容清理：自动清理与修复常见 XML/文本问题、摘要长度截断错误处理：完善的异常处理和日志记录机制并发保护：使用锁文件防止重复任务执行缓存策略：智能缓存管理，支持缓存统计和清理定时任务功能独立脚本：提供 cron.php 独立定时任务脚本HTTP调用：支持通过HTTP请求触发定时任务状态管理：记录定时任务执行状态和下次执行时间安全验证：支持密钥验证防止未授权访问Ajax功能实时检测：支持SSE（Server-Sent Events）实时显示RSS检测进度批量操作：支持批量检测RSS地址和清除缓存进度反馈：实时显示操作进度和结果错误处理：完善的错误处理和用户反馈机制安装与配置将插件文件夹上传到 usr/plugins/ 目录。在 Typecho 后台启用插件 FriendsRSS。在插件设置页配置友链来源、每个博客抓取文章数、聚合总数上限与缓存时间等参数。Github地址：[hide]https://github.com/BXCQ/FriendsRSS[/hide]配置参数说明友链分类：指定需要聚合的友链分组。兼容性说明：Handsome主题：填写"ten"获取全站链接，填写"one"获取内页链接，填写"good"获取推荐链接其他主题：通常留空即可，插件会自动获取所有友链当指定分类不存在时，插件会自动获取所有友链，确保兼容性排除检测的博客网址：每行一个网址，支持部分匹配。例如：example.com - 排除所有包含example.com的友链https://blog.example.com - 排除特定博客留空表示不排除任何友链每个博客文章数：每个博客抓取与展示的文章数量限制。最大文章数：聚合后的文章总数上限。定时解析间隔：定时解析友链RSS的间隔时间（小时），默认6小时，设置为0表示禁用。文章缓存时间会自动与定时解析间隔保持一致。定时检测间隔：定时检测友链RSS地址的间隔时间（小时），默认240小时（10天），设置为0表示禁用。RSS地址缓存时间会自动与定时检测间隔保持一致。启用前台展示：是否开放前台聚合页与 RSS 输出。使用方法检测 RSS：在后台点击"定时检测RSS地址"卡片中的"立即执行"按钮，自动为友链站点识别 RSS/Atom 地址并保存。定时检测：在后台点击"定时检测RSS地址"，按配置的间隔时间自动检测友链RSS地址。可在宝塔面板计划任务重添加shell脚本# 使用 curl 访问触发更新，-s 表示静默模式，> /dev/null 丢弃输出 curl -s "https://www.example.com/action/friends-rss?do=cron" > /dev/null 2>&1 # 或者打印一下时间，方便看日志（可选） echo "FriendsRSS Cron Executed at $(date)"手动添加：在"添加 RSS 地址"区域选择目标友链并填写正确的订阅地址后保存。聚合文章：点击"定时解析RSS内容"，按时间排序聚合最新文章并写入缓存。访问输出：RSS订阅地址：/action/friends-rss?do=rssAPI接口地址：/action/friends-rss?do=page（需在设置中启用）前台展示页面：/action/friends-rss?do=pageview（需在设置中启用）性能与稳定性超时控制：为单次请求与连接设置合理超时并限制重定向次数。并发保护：使用锁文件/状态标记避免重复聚合任务并减少服务器压力。分轮检测：优先常用路径，其次 HTML 解析，最后其他常见路径以提升成功率与速度。缓存策略缓存类型缓存时间说明RSS地址缓存与定时检测间隔一致RSS地址相对稳定，与定时检测间隔同步，减少重复检测文章内容缓存与定时解析间隔一致确保定时解析有意义，避免缓存过期后立即重新解析失败结果缓存1小时避免重复请求失败的RSS源，减少服务器压力定时策略配置项默认值说明定时解析间隔6小时定时解析友链RSS的间隔时间，设置为0表示禁用文章缓存时间自动同步与定时解析间隔保持一致，确保定时解析有意义定时检测间隔240小时（10天）定时检测友链RSS地址的间隔时间，设置为0表示禁用RSS地址缓存时间自动同步与定时检测间隔保持一致，减少重复检测实际效果缓存过期时重新执行定时任务执行时缓存刚好过期，获取最新内容设计思路：以"逻辑一致性"为核心，确保定时任务与缓存时间同步，提升用户体验和系统效率。RSS短代码使用基本用法在任何文章或页面中使用 rss 短代码来显示友链RSS聚合内容，使用简洁的块状设计：rss默认显示10篇最新文章，使用块状设计。或者rss limit="5"只显示5篇最新文章。显示内容每个文章块包含：博客信息头部：博客名称、作者信息、发布时间文章标题：可点击跳转到原文文章摘要：自动截取的文章概要（200字符以内）参数说明limit类型: 数字默认值: 10说明: 显示文章的数量注意不要放置多个，会重复显示。设计特点块状设计简洁美观：每篇文章独立成块，清晰区分信息完整：包含博客、作者、时间、标题、摘要视觉层次：合理的间距和颜色搭配悬停效果：鼠标悬停时显示阴影效果响应式设计移动端优化：自动适配小屏幕设备布局调整：移动端下头部信息垂直排列字体缩放：移动端下适当调整字体大小注意事项缓存机制: 短代码使用缓存机制，文章更新可能有延迟性能考虑: 建议在高流量站点限制显示数量（limit参数）样式兼容: 短代码样式已针对常见主题优化，如有冲突可自定义CSS安全性: 所有外部内容都经过HTML转义处理错误处理与兼容性超时处理：如果你的友链过多且开启了CDN，可以在CDN中设置超时时间，从默认的30s到60s或者更高。网络异常与 HTTP 错误：会被记录并跳过，继续处理其他源。XML 内容清理：在解析前进行清理，尽量兼容 RSS 2.0、Atom 1.0 与 RSS 1.0 常见格式。扩展要求：建议启用 cURL、SimpleXML 等常用扩展并确保插件对缓存目录拥有写入权限。更新日志日期版本更新类型变更内容2025/8/31v2.2.1缓存机制修复• 缓存机制修复：修复缓存机制导致RSS配置无法及时更新的问题：增加配置文件时间戳检查，当配置文件比缓存文件更新时自动强制刷新缓存，确保能读取到最新的手动配置RSS地址• 调试信息增强：增强RSS配置读取调试信息：添加详细的配置文件读取日志，特别针对特定友链配置状态的检查和记录，便于问题排查• 思路：以"缓存一致性"为核心，确保配置文件更新后能及时生效，提升手动配置RSS的可靠性2025/8/31v2.2.0配置文件修复• 配置文件修复：修复RSS配置文件和缓存目录缺失导致的配置读取失败问题：在构造函数中自动创建rss_config.json文件和缓存目录，确保系统正常运行• 错误处理完善：完善错误处理机制：增加文件读取、JSON解析和缓存操作的详细错误日志记录，改进后台RSS配置保存逻辑• 思路：以"健壮性优先"为核心，确保插件在各种环境下都能正常工作，提升系统稳定性2025/8/25v2.1.6定时检测RSS功能• 定时检测RSS功能：新增定时检测RSS功能：支持配置定时检测RSS地址间隔（默认240小时/10天），RSS地址缓存时间自动与定时检测间隔保持一致• 定时检测界面：管理界面优化：新增"定时检测RSS地址"操作卡片，显示当前检测间隔和下次执行时间• 按钮简化：移除重复的"手动检测"按钮，只保留"立即执行"按钮，避免功能重复• 缓存策略：缓存策略优化：RSS地址缓存时间从固定7天改为与定时检测间隔同步，提升系统一致性• 短代码文档：新增RSS短代码使用说明，包含基本用法、参数说明、使用示例和设计特点• 思路：以"逻辑一致性"为核心，确保定时检测与RSS地址缓存同步，提升用户体验和系统效率2025/8/25v2.1.0RSS地址检测• RSS地址检测：新增RSS地址检测功能：自动检测友链博客的RSS地址，支持多种常见RSS路径，提供手动添加功能• 管理界面重构：管理界面重构：新增RSS地址管理、检测日志显示、定时任务设置等功能模块• 缓存机制优化：缓存机制优化：RSS地址缓存24小时，RSS内容缓存可配置，提升系统性能• 思路：以"自动化检测"为核心，减少手动配置工作量，提升用户体验2025/8/25v2.1.5友链分类兼容• 友链分类兼容：友链分类兼容性增强：当指定分类不存在时，自动获取所有友链，完美兼容只有一种友链的主题博客• 排除功能：新增排除功能：支持配置排除检测的博客网址，每行一个，支持部分匹配，提供更灵活的友链管理• 分类统计界面：管理界面优化：新增"可用分类"统计卡片，在访问地址区域显示所有可用的友链分类及当前使用状态• 配置界面优化：配置界面优化：友链分类设置项默认值改为空，添加详细的兼容性说明，帮助用户正确配置不同主题的友链• 思路：以"最大兼容性"为核心，确保插件能在各种Typecho主题环境下正常工作，提升用户体验2025/8/25v2.1.4日志显示修复• 日志显示修复：修复日志显示错误：将"用时20s"等错误显示修正为实际请求用时，使用microtime()精确计算请求耗时• 日志自动滚动：管理界面体验优化：日志显示区域自动滚动到最新内容，无需手动滚动查看最新日志• 策略文档：新增缓存策略和定时解析策略表格说明，更直观地展示系统设计思路2025/8/25v2.1.3缓存策略优化• 缓存策略优化：缓存策略优化：RSS地址缓存时间从24小时延长至7天（更符合RSS地址相对稳定的特点），文章内容缓存时间自动与定时解析间隔保持一致，移除独立的缓存时间设置项• 定时解析功能：新增定时解析功能：支持配置定时解析间隔（默认6小时），提供独立的定时任务脚本，支持命令行和HTTP调用• 配置界面简化：简化配置界面：移除"缓存时间"设置项，避免用户困惑，定时解析间隔设置中说明缓存时间会自动同步• 思路：以"逻辑一致性"为核心，确保定时解析与缓存时间同步，提升用户体验和系统效率2025/8/25v2.1.2界面优化• 缓存时间优化：RSS缓存设置优化：将缓存时间单位从秒改为天，默认值从360000秒（100小时）调整为10天，更符合用户使用习惯• 移除超时设置：移除请求超时设置项：删除未使用的timeout配置项，简化插件设置界面• 界面名称优化：管理界面名称优化：将"检测友链RSS"改为"检测友链RSS地址"，"定时解析控制"改为"定时解析RSS内容"，"前台展示页面"改为"前台接口地址"，"预览页面"改为"前台展示页面"• 思路：以"用户体验优化"为核心，简化配置项，统一命名规范，提升界面友好度2025/8/25v2.1.1管理界面优化• 预览页面地址：新增预览页面地址：在访问地址区域添加 /action/friends-rss?do=pageview 预览链接• 管理界面优化：管理界面优化：将定时解析控制块替换解析RSS功能，移除定时任务设置说明，简化操作流程• 移除预览页面块：移除预览页面块：简化管理界面，减少冗余展示区域• 错误日志显示：新增错误日志显示：在管理页面中显示 error.log 日志内容，便于问题排查• 思路：以"简化操作 + 增强监控"为核心，提升管理效率和问题诊断能力2025/8/24v2.0.4UI布局优化• UI布局优化：后台"添加 RSS 地址"区域 UI 调整：输入框与选择框长度比例优化（输入 65% / 选择 30%），取消选择框固定高度，移动端响应式更佳• select对齐修复：解决 Typecho 默认 select 固定高度 28px 导致与输入框不对齐的问题• 思路：表单布局采用 flex，优先保障主要输入区可用性与可读性，减少视觉噪点2025/8/24v2.0.3管理面板重构• 管理面板重构：管理面板重构：新增统计卡片、操作卡片与访问地址区块；友链与文章列表支持状态徽标，信息呈现更直观• 最后更新修复：修复"最后更新"在无缓存时显示 1970 的问题，改为显示"暂无"• 思路：采用 模块化卡片布局，将"检测/解析/添加"动作解耦，提升可维护性与扩展空间2025/8/23v2.0.2锁文件机制• 锁文件机制：新增聚合任务 锁文件 机制；提供缓存清理与缓存统计；聚合结果持久化读取，页面可直接消费缓存• 并发解析修复：修复并发解析导致缓存文件被覆盖/损坏的偶发问题；修复缓存为空时解析流程可能抛错的情况• 思路：从"实时拉取"转为 缓存优先 的数据策略，后台按需触发解析，降低对外部源的耦合与压力2025/8/23v2.0.1三轮RSS检测• 三轮RSS检测：引入 三轮 RSS 自动检测；为检测结果设置 24 小时缓存；细化请求超时与重定向限制，并记录响应头• 链接解析修复：修复部分站点 HTML 中相对链接解析错误；修正 Content-Type 判断大小写不一致导致的识别失败• 思路：采用"快速路径优先 → HTML 解析 → 兜底常见路径"的分轮策略，先提升成功率与速度，再做细化校验2025/8/23v2.0.0解析器升级• 解析器升级：解析器升级：支持 RSS 2.0 / Atom 1.0 / RSS 1.0；清理与截断描述文本；新增 rss_config.json 用于手动配置；按发布时间倒序聚合并限制总量• XML解析修复：修复特殊字符、BOM 与控制字符导致 XML 解析失败的问题；增强 HTML 实体修复• 思路：以"可靠解析"为核心：先清理再解析，最大化容错；将必要的清洗逻辑前置，保证数据质量2025/8/23v1.5.0RSS输出功能• RSS输出功能：提供聚合 RSS 输出 与可选 前台展示页面；后台展示最近文章与来源信息• 时间排序修复：修复文章缺失时间时排序不稳定，统一回退到当前时间• 思路：以 /action/friends-rss 为统一入口，降低主题/路由差异带来的接入成本2025/8/23v1.4.0多源聚合功能• 多源聚合功能：支持多源聚合与发布时间排序；可配置"每博客文章数"与"聚合总数"• 内存优化：优化大列表处理的内存开销，改为先按需切片再渲染• 思路：将"数量限制"前移到聚合阶段，减少后续处理成本2025/8/23v1.3.0摘要截断功能• 摘要截断功能：自动 截断摘要，保证列表简洁可读• HTML清理修复：修复未清理 HTML 标签导致的样式外溢问题• 思路：以摘要优先，保证信息密度与浏览效率2025/8/23v1.2.0手动添加RSS功能• 手动添加RSS功能：后台新增"手动添加 RSS"能力，为自动检测失败站点提供纠偏• JSON读写修复：修复首次保存配置时空文件读取异常，增强 json 读写容错• 思路：自动检测与 人工纠偏 并行，提高覆盖率与准确度2025/8/23v1.1.0性能参数配置• 性能参数配置：增加缓存时间、重定向上限与超时等基础性能参数配置• 超时设置修复：修复部分环境下超时设置不生效的问题• 思路：坚持"可控超时"原则，保证失败快速返回与系统整体可用性2025/8/23v1.0.0初始版本• 初始版本：初始版本：多源 RSS/Atom 聚合、后台基础管理与展示• Bug：无• 思路：提供最小可用集合，优先打通"来源 → 解析 → 聚合 → 展示"的闭环
前言在搭建个人博客时，我们通常需要集成多个统计服务来跟踪网站访问数据，如 Google Analytics、百度统计、51.la、统计鸟、UStat、Umami 等。但面对这些统计脚本，我们经常会遇到以下困惑：应该选择同步安装还是异步安装？统计代码应该放在 还是 底部？不同的选择会如何影响页面性能和数据准确性？本文将从技术原理出发，结合各大统计服务商的官方建议，为您提供清晰的选择策略和最佳实践方案。核心概念：同步 vs 异步安装为什么选择很重要？网页加载统计脚本时面临一个根本矛盾：数据准确性 vs 页面性能。不同的安装方式直接影响：首屏渲染速度：用户看到页面内容的快慢统计数据完整性：能否准确捕获所有访问行为用户体验质量：页面是否流畅无闪烁两种安装方式对比为了帮助您做出正确选择，我们用图表直观对比两种方式：对比维度同步安装 (Synchronous)异步安装 (Asynchronous)加载方式脚本按顺序逐个加载，必须等待前一个完成多个脚本同时并行下载，互不阻塞页面表现用户看到白屏直到关键脚本加载完成页面内容边加载边显示，更快可见加载速度⚠️ 较慢，串行等待✅ 较快，并行处理页面闪烁✅ 无闪烁，内容稳定⚠️ 可能闪烁（A/B测试场景）执行顺序✅ 可控，按加载顺序执行⚠️ 不可预测，完成时间决定适用场景A/B测试、关键业务逻辑一般统计、性能优先网站可视化对比同步安装效果：异步安装效果： 闪烁现象：异步加载时，页面先显示原始内容，脚本加载完成后快速切换到最终内容，形成视觉闪烁。参考资料：SiteSpect - Sync vs Async JavaScript Tags，NotifyVisitors - Synchronous and Asynchronous Scripts技术实现方式对比三种主流加载技术加载技术代码示例执行时机适用场景async 参考：Google Analytics 官方文档百度统计推荐位置：混合部署最佳实践：在 中声明全局变量，在 底部动态加载脚本 参考：百度统计官方文档51.la 统计重要更新：51.la 现已推出 V6 版本，安装方式与 V5 版本有重要不同。V6 版本（最新推荐）推荐位置： 部分（ 标签前）安装方式：同步安装，支持多种安装选项根据官方指南，V6 版本提供以下安装选择：同步安装（推荐）：确保统计准确性，避免页面闪烁异步安装：提升页面加载性能旧式安装：兼容旧版本的安装方式JS文件用：适用于特定技术架构本地化集成：支持本地部署需求 V5 版本（传统方式）推荐位置： 底部官方建议：将统计代码放在接近 的位置 注意：请以 51.la 官方提供的最新安装代码为准，不同版本和不同站点的代码可能有所差异。参考：51.la 官网，51.la V6 使用指南UStat 统计推荐位置： 部分建议做法：使用 async 属性，放在 前 Umami Analytics推荐位置： 部分特点：开源、隐私友好，支持 async defer 统计鸟等其他统计工具推荐位置：根据脚本特性选择原则：异步脚本优先放 ，同步脚本放 底部 使用插件或自定义功能如果主题不支持直接修改，可以：使用统计插件：如 Google Analytics 插件后台自定义代码：在 Typecho 后台的"自定义代码"选项中添加functions.php 注入：通过主题的 functions.php 文件注入脚本缓存和静态化的考虑缓存环境下的脚本加载HTML缓存：不影响脚本的加载和执行顺序CDN缓存：可能影响脚本文件的加载速度，但不改变执行逻辑浏览器缓存：有助于提高脚本加载速度静态化网站的统计脚本对于使用静态化插件的 Typecho 站点：确保统计代码包含在静态化的模板中动态统计功能不受影响，因为执行发生在浏览器端注意更新统计配置后需要重新生成静态文件性能优化建议脚本加载优先级关键统计脚本（如 GA）： + async辅助统计脚本： 底部或延迟加载可选统计脚本：条件加载或懒加载资源合并与优化// 统一的统计脚本管理器 (function() { 'use strict'; // 延迟加载非关键统计脚本 window.addEventListener('load', function() { // 加载 51.la var script51 = document.createElement('script'); script51.src = '//js.users.51.la/YOUR_51LA_ID.js'; document.head.appendChild(script51); // 加载其他统计脚本 // ... }); })();错误处理和回退方案// UStat脚本错误处理加载，由于之前使用这个出现过问题导致页面某些资源被卡10s，所以就用这个做示例 最终建议总结推荐的统计脚本部署策略统计服务推荐位置加载方式特点说明Google Analytics async官方推荐，数据准确性优先百度统计混合部署动态插入head声明变量 + body动态加载51.la V6 同步安装新版本，支持多种安装方式UStat async早期触发，数据完整性Umami async defer轻量级，隐私友好统计鸟 底部动态异步国产统计，性能优先通用原则异步优先：尽量使用 async 或 defer 属性按需选择：根据服务商建议和业务需求选择位置性能监控：定期检查页面加载性能指标数据验证：确保统计数据的准确性和完整性同步与异步安装的选择策略选择同步安装的情况：A/B测试脚本：避免页面闪烁，确保用户看到正确的页面版本关键业务逻辑：需要确保脚本执行顺序的核心功能小型轻量脚本：对页面性能影响较小的统计代码选择异步安装的情况：性能优先网站：首屏加载速度是关键指标大型统计脚本：文件较大或加载时间较长的脚本辅助功能脚本：不影响页面核心功能的统计工具最佳实践建议：核心统计脚本：采用异步加载，放在 部分辅助统计工具：采用异步加载，放在 底部A/B测试工具：根据具体需求选择，平衡性能和用户体验定期评估：监控页面性能指标，适时调整加载策略针对不同场景的建议性能敏感站点：优先将统计脚本放在 底部数据敏感站点：关键统计脚本放在 部分平衡方案：核心统计放 ，辅助统计放底部结语统计脚本的放置位置选择并没有标准答案，需要根据具体的业务需求、性能要求和统计服务商的建议来综合判断。现代浏览器的异步加载能力已经大大减少了脚本位置对性能的影响，关键在于：正确理解同步与异步：明确两种加载方式的优劣势，根据实际需求选择关注服务商更新：如51.la V6等新版本可能提供更优化的安装方式平衡性能与准确性：在统计数据完整性和页面加载性能之间找到最佳平衡点持续优化调整：随着网站发展和技术演进，适时调整统计脚本部署策略对于 Typecho 用户，建议根据本文的分析结合自己的实际情况，选择合适的部署策略，并定期监控网站性能和统计数据的准确性。特别需要注意各统计服务的版本更新，及时采用最新的最佳实践。参考资料MDN Web Docs - Script elementJavaScript.info - Scripts: async, deferSiteSpect - Sync vs Async JavaScript TagsNotifyVisitors - Synchronous and Asynchronous ScriptsGoogle Analytics 官方文档Analytics Mania - GA 脚本位置分析百度统计开发者文档51.la 官网51.la V6 使用指南Umami Analytics 文档Stack Overflow - 脚本加载位置讨论Typecho 主题开发文档本文基于当前主流浏览器和统计服务的最新文档整理而成，如有更新请以官方文档为准。
插件介绍AiChat是一款专为Typecho博客设计的AI聊天插件，它可以让您轻松地在博客中添加一个AI对话界面。该插件具有以下特点：美观的聊天界面，支持Markdown渲染完美适配PC端和移动端支持深色模式支持本地存储聊天记录注册用户可在多设备间同步对话历史支持一键复制对话内容兼容主流Typecho主题（本文以Handsome主题为例）安装步骤1. 下载插件首先，您需要下载AiChat插件。您可以通过以下方式获取：迅雷网盘下载：[hide]https://pan.xunlei.com/s/VOkBx1ymbfLEA9sPSHIvIwNpA1?pwd=vb9b#[/hide]下载完成后，您将得到一个名为AiChat.rar的压缩包。2. 上传并解压插件将下载好的压缩包解压，您会得到一个AiChat文件夹。将这个文件夹上传到您的Typecho博客的/usr/plugins/目录下。然后将ai-chat.php文件放置在handsome主题目录下。3. 激活插件登录到您的Typecho后台，进入"控制台 > 插件"页面，找到"AiChat"插件，点击"启用"按钮激活插件。配置插件1. 基本配置激活插件后，点击"设置"按钮进入插件配置页面：在配置页面中，您需要填写以下信息：AI API接口地址：默认为https://api.deepseek.com/v1/chat/completions，您也可以使用其他兼容的API接口AI API Key：您的API密钥，可以从相应的AI服务提供商获取AI 模型：默认为deepseek-chat，也可以根据您的需求选择其他模型温度参数：控制AI回复的随机性，值越高回复越多样，默认为0.7最大回复长度：控制AI回复的最大token数量，默认为2000填写完成后，点击"保存设置"按钮。2. 创建聊天页面配置完插件后，您需要创建一个独立页面来展示AI聊天界面：进入Typecho后台的"管理 > 创建 > 创建页面"填写页面标题，如"AI聊天"在"自定义模板"下拉菜单中选择"AI聊天"点击"发布页面"按钮使用体验1. 界面展示创建完成后，访问您刚才创建的页面，您将看到一个精美的AI聊天界面：界面主要包括以下部分：顶部提示区：显示使用说明和当前登录状态聊天区域：展示对话内容输入区域：输入您想问的问题功能按钮：包括发送按钮、清空对话按钮和查看历史记录按钮（仅登录用户可见）2. 功能特点2.1 一键复制当您将鼠标悬停在任何一条消息上时，会出现一个复制按钮。点击该按钮可以一键复制该条消息的内容，非常方便引用或分享。2.2 聊天记录同步游客用户：聊天记录会保存在浏览器的本地存储中，清除浏览器数据后将丢失注册用户：聊天记录会同时保存在服务器，可以在任何设备登录账号后通过"查看历史记录"按钮加载2.3 清空对话如果您想开始新的对话，可以点击右上角的清空按钮，系统会询问确认后清空当前的对话记录。3. 深色模式适配AiChat插件完美适配深色模式，当您的博客切换到深色模式时，聊天界面也会自动切换到相应的深色样式。常见问题解答1. 为什么我看不到"AI Chat Plugin"模板选项？如果您在创建页面时没有看到"AI Chat Plugin"模板选项，可能是因为：插件没有正确安装或激活您的主题不完全兼容解决方法：尝试禁用后重新启用插件，或者联系插件作者获取支持。2. 为什么AI回复很慢或者失败？可能的原因包括：API密钥配置错误网络连接问题API服务商限流或额度用尽解决方法：检查API配置，确保网络连接稳定，或者考虑更换API服务商。3. 如何自定义聊天界面样式？如果您想进一步自定义聊天界面的样式，可以通过修改主题的CSS文件或使用自定义CSS插件来实现。主要的CSS类名包括：.ai-chat-container：整个聊天容器.ai-chat-messages-area：消息显示区域.ai-chat-input-area：输入区域.message.user：用户消息.message.bot：AI回复消息总结通过AiChat插件，您可以轻松地为Typecho博客添加一个功能强大、界面美观的AI聊天功能。无论是用于回答访客问题，还是作为博客的特色功能，它都能为您的网站增添不少亮点。希望本教程对您有所帮助！如果您在使用过程中遇到任何问题，欢迎在评论区留言或者联系插件作者获取支持。