[{"data":1,"prerenderedAt":341},["ShallowReactive",2],{"content:\u002Fposts\u002Fquote-of-the-day":3,"surround:\u002Fposts\u002Fquote-of-the-day":331},{"id":4,"title":5,"body":6,"categories":304,"date":306,"description":307,"draft":308,"extension":309,"image":310,"meta":311,"navigation":313,"path":314,"permalink":315,"pinned":308,"published":315,"readingTime":316,"recommend":315,"references":315,"seo":321,"sitemap":322,"stem":323,"tags":324,"type":328,"updated":329,"__hash__":330},"content\u002Fposts\u002Fposts\u002Fquote-of-the-day.md","Firefly 魔改：每日一言侧边栏组件",{"type":7,"value":8,"toc":291},"minimark",[9,19,22,25,30,37,45,68,72,76,79,90],[10,11,15],"alert",{"title":12,"type":13,"icon":14},"AI 迁移提示","info","tabler:robot",[16,17,18],"p",{},"本文由 AI 协助从旧站迁移，尚未完成逐篇人工审校；内容如有疏漏，将在复核后修订。",[10,20],{"title":21,"type":13},"> 本文部分内容由 AI 辅助整理，已结合实际操作进行校验，请根据自身环境谨慎使用。",[16,23,24],{},"在侧边栏展示一句名言，每天刷新。如何做到\"同一天显示相同的名言，且不需要数据库\"？答案是用日期作为随机种子。",[26,27,29],"h2",{"id":28},"一设计思路","一、设计思路",[16,31,32],{},[33,34],"img",{"alt":35,"src":36},"今日一言效果图","https:\u002F\u002Fimg.olinl.com\u002Ffile\u002Fpost-img\u002Fquote-of-the-day\u002F0001.webp",[16,38,39,40,44],{},"名言数据存储在 ",[41,42,43],"code",{"code":43},"content\u002Fziyuan\u002Fquote.md"," 中，构建时读取。每次页面加载时通过日期字符串计算出伪随机索引，确保：",[46,47,48,56,62],"ul",{},[49,50,51,55],"li",{},[52,53,54],"strong",{},"同一天"," → 显示同一条名言",[49,57,58,61],{},[52,59,60],{},"不同天"," → 自动切换",[49,63,64,67],{},[52,65,66],{},"无需数据库"," → 纯静态，构建时确定",[26,69,71],{"id":70},"二核心实现","二、核心实现",[73,74,75],"h3",{"id":75},"日期种子算法",[16,77,78],{},"组件完整源码：",[80,81,87],"pre",{"className":82,"code":84,"language":85,"meta":86},[83],"language-astro","---\nimport { getCollection } from \"astro:content\";\nimport WidgetLayout from \"@\u002Fcomponents\u002Fcommon\u002FWidgetLayout.astro\";\n\ninterface QuoteItem {\n    text: string;\n    author: string;\n}\n\ninterface QuoteData {\n    title: string;\n    quotes?: QuoteItem[];\n}\n\n\u002F\u002F 从 content\u002Fziyuan\u002Fquote.md 读取名言数据\nconst ziyuanEntries = await getCollection(\"ziyuan\");\nconst quoteEntry = ziyuanEntries.find((e) => e.id === \"quote\");\nconst quoteData = quoteEntry?.data as QuoteData | undefined;\nconst quotes = quoteData?.quotes || [];\n\ninterface Props {\n    class?: string;\n    style?: string;\n}\n\nconst { class: className, style } = Astro.props;\n\n\u002F\u002F 使用日期作为随机种子，确保同一天显示相同的名言\nconst today = new Date();\nconst dateStr = today.toISOString().split(\"T\")[0];\nconst seed = dateStr\n    .split(\"\")\n    .reduce((acc, char) => acc + char.charCodeAt(0), 0);\nconst randomIndex = seed % quotes.length;\nconst quote = quotes[randomIndex] || { text: \"暂无名言\", author: \"\" };\n---\n\n\u003CWidgetLayout name=\"✨ 今日一言\" id=\"quote-of-the-day\" class={className} style={style}>\n    \u003Cdiv class=\"quote-content\">\n        \u003Cblockquote class=\"quote-text\">\n            \u003Cspan class=\"quote-mark\">\"\u003C\u002Fspan>\n            {quote.text}\n            \u003Cspan class=\"quote-mark\">\"\u003C\u002Fspan>\n        \u003C\u002Fblockquote>\n        \u003Ccite class=\"quote-author\">—— {quote.author}\u003C\u002Fcite>\n    \u003C\u002Fdiv>\n\u003C\u002FWidgetLayout>\n\n\u003Cstyle>\n    .quote-content { display: flex; flex-direction: column; gap: 0.75rem; padding: 0.5rem 0; }\n    .quote-text { position: relative; font-style: italic; font-size: 0.9rem; line-height: 1.6; padding: 0 0.5rem; margin: 0; }\n    .quote-mark { font-size: 1.5rem; line-height: 0; vertical-align: middle; color: var(--primary); opacity: 0.6; font-family: Georgia, serif; }\n    .quote-author { display: block; text-align: right; font-size: 0.75rem; color: var(--content-meta); font-style: normal; margin-top: 0.25rem; }\n    :global(html.dark) .quote-text { color: #fff; }\n    :global(.quote-content:hover .quote-text) { color: var(--primary); transition: color 0.2s ease; }\n\u003C\u002Fstyle>\n","astro","title=\"src\u002Fcomponents\u002Fwidget\u002FQuoteOfTheDay.astro\"",[41,88,84],{"__ignoreMap":89},"",[10,91,94,104,107,114,123,127,134,145,152,160,164,170,182,186,192,199,205,209,216,238,249,256,260,268,276,284,288],{"title":92,"type":93},"为什么会这样工作？","tip",[16,95,96,99,100,103],{},[41,97,98],{"code":98},"toISOString().split(\"T\")[0]"," 返回 ",[41,101,102],{"code":102},"\"YYYY-MM-DD\""," 格式字符串。将其每个字符的 charCode 累加，得到的数字在不同日期不同，同一天完全一致。再取模就得到了\"每日固定的随机\"索引。",[73,105,106],{"id":106},"数据源",[16,108,109,110,113],{},"名言存放在资源集合 ",[41,111,112],{"code":112},"src\u002Fcontent\u002Fziyuan\u002Fquote.md"," 的 frontmatter 中：",[80,115,121],{"className":116,"code":118,"language":119,"meta":120},[117],"language-yaml","---\ntitle: 名言集\nquotes:\n  - text: 知行合一\n    author: 王阳明\n  - text: 学而不思则罔，思而不学则殆\n    author: 孔子\n---\n","yaml","title=\"src\u002Fcontent\u002Fziyuan\u002Fquote.md\"",[41,122,118],{"__ignoreMap":89},[26,124,126],{"id":125},"三注册组件","三、注册组件",[16,128,129,130,133],{},"在 ",[41,131,132],{"code":132},"src\u002Fcomponents\u002Flayout\u002FSideBar.astro"," 中导入：",[80,135,143],{"className":136,"code":138,"highlights":139,"language":141,"meta":142},[137],"language-js","import Advertisement from \"@\u002Fcomponents\u002Fwidget\u002FAdvertisement.astro\";\nimport Announcement from \"@\u002Fcomponents\u002Fwidget\u002FAnnouncement.astro\";\nimport Calendar from \"@\u002Fcomponents\u002Fwidget\u002FCalendar.astro\";\nimport Categories from \"@\u002Fcomponents\u002Fwidget\u002FCategories.astro\";\nimport Dynamic from \"@\u002Fcomponents\u002Fwidget\u002FDynamic.astro\";\nimport GitHubHeatmap from \"@\u002Fcomponents\u002Fwidget\u002FGitHubHeatmap.astro\";\nimport Music from \"@\u002Fcomponents\u002Fwidget\u002FMusic.astro\";\nimport Profile from \"@\u002Fcomponents\u002Fwidget\u002FProfile.astro\";\nimport QuoteOfTheDay from \"@\u002Fcomponents\u002Fwidget\u002FQuoteOfTheDay.astro\";\nimport Schedule from \"@\u002Fcomponents\u002Fwidget\u002FSchedule.astro\";\nimport SidebarTOC from \"@\u002Fcomponents\u002Fwidget\u002FSidebarTOC.astro\";\nimport SiteInfo from \"@\u002Fcomponents\u002Fwidget\u002FSiteInfo.astro\";\nimport SiteStats from \"@\u002Fcomponents\u002Fwidget\u002FSiteStats.astro\";\nimport Tags from \"@\u002Fcomponents\u002Fwidget\u002FTags.astro\";\nimport TimeGreeting from \"@\u002Fcomponents\u002Fwidget\u002FTimeGreeting.astro\";\n",[140],9,"js","title=\"src\u002Fcomponents\u002Flayout\u002FSideBar.astro\" ins=",[41,144,138],{"__ignoreMap":89},[16,146,147,148,151],{},"同时在 ",[41,149,150],{"code":150},"componentMap"," 中注册：",[80,153,158],{"className":154,"code":155,"highlights":156,"language":141,"meta":142},[137],"const componentMap = {\n    profile: Profile,\n    announcement: Announcement,\n    categories: Categories,\n    tags: Tags,\n    sidebarToc: SidebarTOC,\n    advertisement: Advertisement,\n    stats: SiteStats,\n    calendar: Calendar,\n    music: Music,\n    siteInfo: SiteInfo,\n    githubHeatmap: GitHubHeatmap,\n    timeGreeting: TimeGreeting,\n    dynamic: Dynamic,\n    schedule: Schedule,\n    quoteOfTheDay: QuoteOfTheDay,\n};\n",[157],16,[41,159,155],{"__ignoreMap":89},[26,161,163],{"id":162},"四注册类型","四、注册类型",[16,165,129,166,169],{},[41,167,168],{"code":168},"src\u002Ftypes\u002FsidebarConfig.ts"," 的类型联合中添加组件名：",[80,171,180],{"className":172,"code":174,"highlights":175,"language":178,"meta":179},[173],"language-typescript","export type WidgetComponentType =\n    | \"githubHeatmap\"\n    | \"timeGreeting\"\n    | \"dynamic\"\n    | \"schedule\"\n    | \"quoteOfTheDay\";\n",[176,177],5,6,"typescript","title=\"src\u002Ftypes\u002FsidebarConfig.ts\" ins=",[41,181,174],{"__ignoreMap":89},[26,183,185],{"id":184},"五配置方式","五、配置方式",[16,187,129,188,191],{},[41,189,190],{"code":190},"sidebarConfig.ts"," 中配置：",[80,193,197],{"className":194,"code":195,"language":178,"meta":196},[173],"{\n  type: \"quoteOfTheDay\",\n  enable: true,\n  position: \"top\",\n  showOnPostPage: false,\n}\n","title=\"src\u002Fconfig\u002FsidebarConfig.ts\"",[41,198,195],{"__ignoreMap":89},[16,200,201,202,204],{},"添加新名言只需编辑 ",[41,203,112],{"code":112}," 中的数组即可。",[26,206,208],{"id":207},"六外部-api-方式可选","六、外部 API 方式（可选）",[16,210,211,212,215],{},"如果不想维护本地名言库，也可以用客户端脚本在页面加载后实时拉取外部 API。在组件末尾添加 ",[41,213,214],{"code":214},"\u003Cscript>"," 块：",[80,217,236],{"className":218,"code":219,"highlights":220,"language":85,"meta":235},[83],"\u003Cscript>\n  (async () => {\n    try {\n      const res = await fetch(\"https:\u002F\u002Fv1.hitokoto.cn\u002F\");\n      const data = await res.json();\n      const text = document.querySelector(\".quote-text\");\n      const author = document.querySelector(\".quote-author\");\n      if (text) text.textContent = `\"${data.hitokoto}\"`;\n      if (author) author.textContent = `—— ${data.from || \"\"}`;\n    } catch {\n      \u002F\u002F 保留构建时渲染的默认名言\n    }\n  })();\n\u003C\u002Fscript>\n",[221,222,223,224,176,177,225,226,140,227,228,229,230,231,232,157,233,234],1,2,3,4,7,8,10,11,12,13,14,15,17,18,"title=\"src\u002Fcomponents\u002Fwidget\u002FQuoteOfTheDay.astro\" ins=",[41,237,219],{"__ignoreMap":89},[16,239,240,241,248],{},"这样每次页面加载都会从",[242,243,247],"a",{"href":244,"rel":245},"https:\u002F\u002Fhitokoto.cn",[246],"nofollow","一言 API"," 实时获取句子，覆盖组件原本渲染的本地名言。优点是名言库无限大，缺点是无法保持\"同一天同内容\"的确定性。",[16,250,251,252,255],{},"如果想保留每日确定性，也可以封装一个服务端 API 代理（类似 ",[41,253,254],{"code":254},"src\u002Fpages\u002Fapi\u002Fdynamic.json.ts","），在构建时缓存每日结果。",[26,257,259],{"id":258},"七相关文件","七、相关文件",[16,261,262,263],{},"组件：",[242,264,267],{"href":265,"rel":266},"https:\u002F\u002Fgithub.com\u002Folinll\u002Ffirefly-blog\u002Fblob\u002Fmaster\u002Fsrc\u002Fcomponents\u002Fwidget\u002FQuoteOfTheDay.astro",[246],"\u002Fsrc\u002Fcomponents\u002Fwidget\u002FQuoteOfTheDay.astro",[16,269,270,271],{},"数据源：",[242,272,275],{"href":273,"rel":274},"https:\u002F\u002Fgithub.com\u002Folinll\u002Ffirefly-blog\u002Fblob\u002Fmaster\u002Fsrc\u002Fcontent\u002Fziyuan\u002Fquote.md",[246],"\u002Fsrc\u002Fcontent\u002Fziyuan\u002Fquote.md",[16,277,278,279],{},"相关源码：",[242,280,283],{"href":281,"rel":282},"https:\u002F\u002Fgithub.com\u002Folinll\u002Ffirefly-blog",[246],"olinll\u002Ffirefly-blog",[26,285,287],{"id":286},"最后","🔗 最后",[16,289,290],{},"这个组件的巧妙之处在于用最简单的算法实现了\"每日刷新\"的效果，零运行时依赖。名言数据源和展示逻辑完全分离，你只需要维护名言列表即可。",{"title":89,"searchDepth":224,"depth":224,"links":292},[293,294,298,299,300,301,302,303],{"id":28,"depth":222,"text":29},{"id":70,"depth":222,"text":71,"children":295},[296,297],{"id":75,"depth":223,"text":75},{"id":106,"depth":223,"text":106},{"id":125,"depth":222,"text":126},{"id":162,"depth":222,"text":163},{"id":184,"depth":222,"text":185},{"id":207,"depth":222,"text":208},{"id":258,"depth":222,"text":259},{"id":286,"depth":222,"text":287},[305],"Firefly","2026-07-21 19:41:29","侧边栏每日一句名言，以日期为种子的伪随机算法确保同一天显示相同内容。",false,"md","https:\u002F\u002Fimg.olinl.com\u002Ffile\u002Fpost-img\u002Fquote-of-the-day\u002Fcover.webp",{"slots":312},{},true,"\u002Fposts\u002Fquote-of-the-day",null,{"text":317,"minutes":318,"time":319,"words":320},"5 min read",4.835,290100,967,{"title":5,"description":307},{"loc":314},"posts\u002Fposts\u002Fquote-of-the-day",[305,325,326,327],"博客","二开","小部件","tech","2026-07-22 19:30:00","BwIU44wxmL7SUhTmw0cWyc2w11rb7nAtcUqXtT_vgUw",[332,337],{"title":333,"path":334,"stem":335,"date":336,"type":328,"children":-1},"Jenkins 部署 Vue+Java 完整指南","\u002Fposts\u002Fjenkins-devops","posts\u002Fposts\u002Fjenkins-devops","2026-07-19 18:02:26",{"title":338,"path":339,"stem":340,"date":306,"type":328,"children":-1},"Firefly 魔改：时间进度与节假日倒计时组件","\u002Fposts\u002Fschedule-progress","posts\u002Fposts\u002Fschedule-progress",1788712221314]