最近把博客(Halo)的邮件通知好好收拾了一下。
说起来有点惭愧——作为一个纯小白,我对「前端」「Thymeleaf」「响应式邮件」这些词的认识,基本停留在"听起来很厉害"的层面。但架不住博客默认的那些通知邮件实在有点……不太好看,而且每封长得都不一样:安全提醒、评论通知、验证码各搞各的,完全没有品牌感。
所以这次我干脆让 AI 帮我从头写了一套:统一风格、统一结构、自适应所有设备。今天把成果和折腾过程一起分享出来。
一、先说说我折腾 AI 工具的经历
这事得从"我想偷懒"说起。
一开始我试了两个国外很火的工具:OpenClaw 和 Hermes。说实话,它们确实好用,交互顺滑、能力强,折腾起来也很有意思。
但问题就出在"国外的"这三个字上:
部署阶段就卡了好几次——镜像拉不下来、依赖装一半网络就超时;
平时用也不稳——连接时好时坏、响应慢、时不时掉线;
最要命的是排错难——作为纯小白,一旦出问题,我连"该搜什么关键词"都不知道,文档还是英文的,看着头大。
我想要的其实很简单:能稳定用、中文友好、出错了有人(或者至少有提示)告诉我怎么办。
试来试去,最后选了 WorkBuddy——一个国产的 AI 助手。它不一定像那两个国外工具那么"炫",但对我们这种纯小白来说,胜在稳、在国内网络下基本不抽风、中文沟通无障碍。用它来写这套邮件模板,全程就是"我说需求、它出活",体验意外地顺。
二、我是怎么让 AI 写出这套模板的
我一行 HTML 都没自己敲(敲也敲不利索)。做法特别简单:
先把 Halo 里各种邮件通知需要的变量整理成一张表(比如新设备登录需要 os、browser、ipAddress、loginTime……);
告诉 WorkBuddy:我要一套风格统一、大气、带点苹果设计感、能自适应所有设备的邮件模板;
它先搭了一套"设计系统"(统一颜色、圆角、字体、组件),然后一套一套地把 7 种通知邮件都写了出来。
整个过程我基本只做了"提需求"和"看效果",真正写代码的全是 AI。

三、这套模板包含什么
一共 7 套,覆盖 Halo 常见的通知场景,全部复用同一套视觉规范:

四、设计上的几个小小心思
苹果风:浅灰底(#f5f5f7)+ 纯白卡片 + Apple 蓝(#0071e3),大圆角、淡阴影、大量留白,克制不花哨;
自适应所有设备:容器 max-width:600px 自动缩放,手机端用媒体查询收窄间距字号,不会出横向滚动条;
统一结构:顶部居中 LOGO → 图标+标题同行 → 内容 → 主按钮/说明 → 版权+退订页脚,每封都长一个样;
评论类统一:瞬间 / 页面 / 文章 / 回复四类评论,共用"引用块 + 评论气泡"两个组件,改一处全站生效;
年份自动:页脚年份用表达式动态生成,不用每年手改。
五、完整代码
我发布的瞬间收到新评论
<!--
邮件模板 · 我发布的瞬间收到新评论(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 momentName / momentCreatedAt / momentRawContent / momentHtmlContent / momentUrl / commenter / commentName / content。
· 说明:momentRawContent(瞬间原文)用于引用块;commenter(评论者显示名)+ content(评论内容)用于评论气泡;momentUrl 为主按钮。
momentName / momentCreatedAt / momentHtmlContent / commentName 当前未直接展示,预留给主题需要时可加。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
/* 引用块:被评论的瞬间原文 */
.ae-quote { background: #f5f5f7; border-left: 3px solid #0071e3; border-radius: 0 12px 12px 0; padding: 14px 18px; margin: 0 0 20px; }
.ae-quote-label { font-size: 13px; color: #6e6e73; margin-bottom: 6px; letter-spacing: 0.01em; }
.ae-quote-text { font-size: 15px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
/* 评论气泡:新评论内容 */
.ae-comment { background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; margin: 0 0 28px; }
.ae-comment-author { font-size: 15px; font-weight: 600; color: #1d1d1f; margin-bottom: 6px; }
.ae-comment-body { font-size: 16px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-quote { padding: 12px 14px !important; }
.ae-comment { padding: 14px 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">💬</div>
<h1 class="ae-title">你的瞬间收到新评论</h1>
</div>
<p class="ae-lead">
<span th:text="${subscriber.displayName}"></span>,有人在你的瞬间下发表了新评论。
</p>
<!-- 被评论的瞬间(引用块) -->
<div class="ae-quote">
<div class="ae-quote-label">你的瞬间</div>
<div class="ae-quote-text" th:text="${momentRawContent}"></div>
</div>
<!-- 新评论(气泡) -->
<div class="ae-comment">
<div class="ae-comment-author"><span th:text="${commenter}"></span> 评论道</div>
<div class="ae-comment-body" th:text="${content}"></div>
</div>
<!-- 主按钮:查看这条瞬间 -->
<a class="ae-btn" th:href="${momentUrl}" th:text="|查看瞬间|">查看瞬间</a>
<!-- 说明条 -->
<p class="ae-note">
如果你不想再收到此类通知,可在账户设置中调整通知偏好,或点击页脚「取消订阅」。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
新设备登录
<!--
邮件模板 · 新设备登录提醒(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;模板变量 os / browser / ipAddress / loginTime / principalName。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(无页眉时的 Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
.ae-info { background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 16px; padding: 8px 20px; margin-bottom: 28px; }
.ae-info-row { padding: 16px 0; border-bottom: 1px solid #ececf0; }
.ae-info-row:last-child { border-bottom: none; }
.ae-info-label { font-size: 13px; color: #6e6e73; margin-bottom: 4px; letter-spacing: 0.01em; }
.ae-info-value { font-size: 17px; color: #1d1d1f; font-weight: 500; word-break: break-word; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-info { padding: 4px 16px !important; }
.ae-info-row { padding: 14px 0 !important; }
.ae-info-value { font-size: 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">🔐</div>
<h1 class="ae-title">新设备登录提醒</h1>
</div>
<p class="ae-lead">
<span th:text="${subscriber.displayName}"></span>,你的账户刚刚在一台新设备上登录。若这是你本人操作,可忽略此邮件;否则请尽快保护你的账户安全。
</p>
<!-- 设备信息明细 -->
<div class="ae-info">
<div class="ae-info-row">
<div class="ae-info-label">操作系统</div>
<div class="ae-info-value" th:text="${os}"></div>
</div>
<div class="ae-info-row">
<div class="ae-info-label">浏览器</div>
<div class="ae-info-value" th:text="${browser}"></div>
</div>
<div class="ae-info-row">
<div class="ae-info-label">登录账户</div>
<div class="ae-info-value" th:text="${principalName}"></div>
</div>
<div class="ae-info-row">
<div class="ae-info-label">IP 地址</div>
<div class="ae-info-value" th:text="${ipAddress}"></div>
</div>
<div class="ae-info-row">
<div class="ae-info-label">登录时间</div>
<div class="ae-info-value" th:text="${loginTime}"></div>
</div>
</div>
<!-- 主按钮:前往站点 / 账户安全页(如有独立密码修改页,替换为对应 URL 变量即可) -->
<a class="ae-btn" th:href="${site.url}" th:text="|前往 ${site.title}|"></a>
<!-- 安全提示 -->
<p class="ae-note">
如果你并未进行此操作,或怀疑账户被盗,请立即修改密码并退出所有设备。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
邮箱验证邮箱验证邮箱验证邮箱验证邮箱验证
<!--
邮件模板 · 邮箱验证(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 username / code / expirationAtMinutes。
· 说明:code 为大字验证码块(.ae-code);expirationAtMinutes 为有效期分钟数;username 用于问候。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
/* 验证码大字块 */
.ae-code { margin: 0 0 24px; text-align: center; font-family: "SF Mono", "Menlo", "Consolas", "Courier New", monospace; font-size: 36px; font-weight: 700; letter-spacing: 0.25em; text-indent: 0.25em; color: #1d1d1f; background: #f5f5f7; border: 1px solid #e8e8ed; border-radius: 14px; padding: 22px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-code { font-size: 30px !important; letter-spacing: 0.2em !important; text-indent: 0.2em !important; padding: 18px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">✉️</div>
<h1 class="ae-title">验证你的邮箱</h1>
</div>
<p class="ae-lead">
<span th:text="${username}"></span>,欢迎!请使用下方验证码完成邮箱验证。
</p>
<!-- 验证码大字块 -->
<div class="ae-code" th:text="${code}"></div>
<!-- 说明条 -->
<p class="ae-note">
验证码将在 <span th:text="${expirationAtMinutes}"></span> 分钟内有效。为了账户安全,请勿将验证码告知他人或转发此邮件。<br/>
若你并未申请验证,请忽略此邮件,你的账户不会受到任何影响。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
有人回复了我有人回复了我
<!--
邮件模板 · 有人回复了我(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 commentName / commentSubjectTitle / commentSubjectUrl / quoteContent / isQuoteReply /
commentContent / repliedOwner / replyOwner / replier / replyName / content。
· 说明:replier=回复者显示名,content=对方回复正文,quoteContent=你被回复的原文(有内容才显示引用块),
commentSubjectUrl=查看回复的主题链接。其余字段语义与已用字段重叠,预留按需补充。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
/* 引用块:你被回复的原文 */
.ae-quote { background: #f5f5f7; border-left: 3px solid #0071e3; border-radius: 0 12px 12px 0; padding: 14px 18px; margin: 0 0 20px; }
.ae-quote-label { font-size: 13px; color: #6e6e73; margin-bottom: 6px; letter-spacing: 0.01em; }
.ae-quote-text { font-size: 15px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
/* 评论气泡:新回复内容 */
.ae-comment { background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; margin: 0 0 28px; }
.ae-comment-author { font-size: 15px; font-weight: 600; color: #1d1d1f; margin-bottom: 6px; }
.ae-comment-body { font-size: 16px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-quote { padding: 12px 14px !important; }
.ae-comment { padding: 14px 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">💬</div>
<h1 class="ae-title">有人回复了你的评论</h1>
</div>
<p class="ae-lead">
<span th:text="${subscriber.displayName}"></span>,<span th:text="${replier}"></span> 回复了你的评论。
</p>
<!-- 你被回复的原文(有内容才显示引用块) -->
<div class="ae-quote" th:if="${quoteContent}">
<div class="ae-quote-label">你之前的评论</div>
<div class="ae-quote-text" th:text="${quoteContent}"></div>
</div>
<!-- 对方的新回复 -->
<div class="ae-comment">
<div class="ae-comment-author"><span th:text="${replier}"></span> 回复道</div>
<div class="ae-comment-body" th:text="${content}"></div>
</div>
<!-- 主按钮:查看回复 -->
<a class="ae-btn" th:href="${commentSubjectUrl}" th:text="|查看回复|">查看回复</a>
<!-- 说明条 -->
<p class="ae-note">
如果你不想再收到此类通知,可在账户设置中调整通知偏好,或点击页脚「取消订阅」。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
我的自定义页面收到新评论我的自定义页面收到新评论
<!--
邮件模板 · 我的自定义页面收到新评论(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 pageName / pageOwner / pageTitle / pageUrl / commenter / commentName / content。
· 说明:引用块展示被评论的页面(优先 pageTitle,缺省回退 pageName);
commenter(评论者显示名)+ content(评论内容)用于评论气泡;pageUrl 为主按钮。
pageOwner / commentName 当前未直接展示,预留给主题需要时可加。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
/* 引用块:被评论的页面 */
.ae-quote { background: #f5f5f7; border-left: 3px solid #0071e3; border-radius: 0 12px 12px 0; padding: 14px 18px; margin: 0 0 20px; }
.ae-quote-label { font-size: 13px; color: #6e6e73; margin-bottom: 6px; letter-spacing: 0.01em; }
.ae-quote-text { font-size: 15px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
/* 评论气泡:新评论内容 */
.ae-comment { background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; margin: 0 0 28px; }
.ae-comment-author { font-size: 15px; font-weight: 600; color: #1d1d1f; margin-bottom: 6px; }
.ae-comment-body { font-size: 16px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-quote { padding: 12px 14px !important; }
.ae-comment { padding: 14px 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">💬</div>
<h1 class="ae-title">你的页面收到新评论</h1>
</div>
<p class="ae-lead">
<span th:text="${subscriber.displayName}"></span>,有人在你的页面下发表了新评论。
</p>
<!-- 被评论的页面(引用块) -->
<div class="ae-quote">
<div class="ae-quote-label">你发布的页面</div>
<div class="ae-quote-text" th:text="${pageTitle ?: pageName}"></div>
</div>
<!-- 新评论(气泡) -->
<div class="ae-comment">
<div class="ae-comment-author"><span th:text="${commenter}"></span> 评论道</div>
<div class="ae-comment-body" th:text="${content}"></div>
</div>
<!-- 主按钮:查看这个页面 -->
<a class="ae-btn" th:href="${pageUrl}" th:text="|查看页面|">查看页面</a>
<!-- 说明条 -->
<p class="ae-note">
如果你不想再收到此类通知,可在账户设置中调整通知偏好,或点击页脚「取消订阅」。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
我的文章收到新评论
<!--
邮件模板 · 我的文章收到新评论(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 postName / postOwner / postTitle / postUrl / commenter / commentName / content。
· 说明:引用块展示被评论的文章(优先 postTitle,缺省回退 postName);
commenter(评论者显示名)+ content(评论内容)用于评论气泡;postUrl 为主按钮。
postOwner / commentName 当前未直接展示,预留给主题需要时可加。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
/* 引用块:被评论的文章 */
.ae-quote { background: #f5f5f7; border-left: 3px solid #0071e3; border-radius: 0 12px 12px 0; padding: 14px 18px; margin: 0 0 20px; }
.ae-quote-label { font-size: 13px; color: #6e6e73; margin-bottom: 6px; letter-spacing: 0.01em; }
.ae-quote-text { font-size: 15px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
/* 评论气泡:新评论内容 */
.ae-comment { background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; margin: 0 0 28px; }
.ae-comment-author { font-size: 15px; font-weight: 600; color: #1d1d1f; margin-bottom: 6px; }
.ae-comment-body { font-size: 16px; line-height: 1.6; color: #1d1d1f; word-break: break-word; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-quote { padding: 12px 14px !important; }
.ae-comment { padding: 14px 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">💬</div>
<h1 class="ae-title">你的文章收到新评论</h1>
</div>
<p class="ae-lead">
<span th:text="${subscriber.displayName}"></span>,有人在你的文章下发表了新评论。
</p>
<!-- 被评论的文章(引用块) -->
<div class="ae-quote">
<div class="ae-quote-label">你发布的文章</div>
<div class="ae-quote-text" th:text="${postTitle ?: postName}"></div>
</div>
<!-- 新评论(气泡) -->
<div class="ae-comment">
<div class="ae-comment-author"><span th:text="${commenter}"></span> 评论道</div>
<div class="ae-comment-body" th:text="${content}"></div>
</div>
<!-- 主按钮:查看这篇文章 -->
<a class="ae-btn" th:href="${postUrl}" th:text="|查看文章|">查看文章</a>
<!-- 说明条 -->
<p class="ae-note">
如果你不想再收到此类通知,可在账户设置中调整通知偏好,或点击页脚「取消订阅」。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
根据邮件地址重置密码
<!--
邮件模板 · 根据邮件地址重置密码(HALO 纯片段)
────────────────────────────────────────────
· 本片段不含 <!DOCTYPE>/<html>/<head>/<body>,作为 Thymeleaf 片段嵌入 HALO 邮件 layout。
· 顶部 <style> 为「邮件设计系统(Apple 风格)」单一真源见 email-design-system.html。
若你的 HALO 主题已有公共邮件 layout 自带 <head>,建议把 <style> 整段合并进 layout 的 <head>,
本片段只保留下方结构即可。
· 变量:系统变量 site.* / subscriber.* / unsubscribeUrl;
本模板变量 username / link / expirationAtMinutes。
· 说明:username(用户名/问候)、link(重置密码链接,作主按钮)、expirationAtMinutes(链接有效期分钟数,进说明条)。
-->
<style>
/* —— 邮件设计系统(Apple 风格,单一真源见 email-design-system.html)—— */
*, *::before, *::after { box-sizing: border-box; }
.ae-wrapper { width: 100%; padding: 40px 16px; background-color: #f5f5f7; }
.ae-card {
width: 100%; max-width: 600px; margin: 0 auto; background: #ffffff;
border-radius: 20px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
color: #1d1d1f; -webkit-font-smoothing: antialiased;
}
.ae-logo-bar { padding: 28px 40px 0; text-align: center; }
.ae-logo { height: 44px; width: auto; border-radius: 9px; display: inline-block; }
.ae-body { padding: 32px 40px 8px; }
.ae-icon { width: 56px; height: 56px; border-radius: 14px; background: #e8f1fd; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px; }
.ae-title { margin: 0 0 12px; font-size: 28px; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; color: #1d1d1f; }
.ae-lead { margin: 0 0 28px; font-size: 17px; line-height: 1.6; color: #6e6e73; }
/* 图标 + 标题同行(Hero 区) */
.ae-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.ae-hero .ae-icon { width: 48px; height: 48px; border-radius: 12px; font-size: 24px; margin-bottom: 0; flex-shrink: 0; }
.ae-hero .ae-title { margin: 0; }
.ae-btn { display: block; text-align: center; text-decoration: none; background: #0071e3; color: #ffffff; font-size: 17px; font-weight: 600; padding: 15px 24px; border-radius: 12px; margin: 0 0 24px; }
.ae-note { margin: 0; font-size: 15px; line-height: 1.6; color: #6e6e73; background: #fbfbfd; border: 1px solid #e8e8ed; border-radius: 14px; padding: 16px 18px; }
.ae-footer { padding: 24px 40px 32px; border-top: 1px solid #ececf0; text-align: center; }
.ae-footer p { margin: 0 0 8px; font-size: 13px; color: #86868b; line-height: 1.5; }
.ae-footer a { color: #0071e3; text-decoration: none; font-size: 13px; }
.ae-footer .ae-unsub { margin-top: 4px; }
@media only screen and (max-width: 600px) {
.ae-wrapper { padding: 16px 10px !important; }
.ae-logo-bar { padding: 22px 24px 0 !important; }
.ae-body { padding: 24px 24px 4px !important; }
.ae-title { font-size: 24px !important; }
.ae-hero { gap: 12px !important; }
.ae-lead { font-size: 16px !important; }
.ae-btn { font-size: 16px !important; padding: 14px 20px !important; }
.ae-footer { padding: 20px 24px 28px !important; }
}
</style>
<!-- 外层画布 -->
<div class="ae-wrapper">
<div class="ae-card">
<!-- 顶部居中 LOGO -->
<div class="ae-logo-bar">
<img class="ae-logo" th:if="${site.logo}" th:src="${site.logo}" th:alt="${site.title}" />
</div>
<!-- 内容区 -->
<div class="ae-body">
<div class="ae-hero">
<div class="ae-icon">🔑</div>
<h1 class="ae-title">重置你的密码</h1>
</div>
<p class="ae-lead">
你好 <span th:text="${username}"></span>,我们收到了重置你账户密码的请求。点击下方按钮即可设置新密码。
</p>
<!-- 主按钮:前往重置密码 -->
<a class="ae-btn" th:href="${link}" th:text="|重置密码|">重置密码</a>
<!-- 说明条 -->
<p class="ae-note">
出于安全考虑,该链接将在 <span th:text="${expirationAtMinutes}"></span> 分钟后失效。如果你没有请求重置密码,请忽略此邮件,你的密码不会被更改。此邮件由系统自动发送,请勿直接回复。
</p>
</div>
<!-- 页脚:版权 + 退订 -->
<div class="ae-footer">
<p>© <span th:text="${T(java.time.Year).now().getValue()}"></span> <span th:text="${site.title}"></span> · <span th:text="${site.subtitle}"></span></p>
<p class="ae-unsub"><a th:href="${unsubscribeUrl}">取消订阅</a></p>
</div>
</div>
</div>
六、怎么用六、怎么用
代码适配HALO应用邮件模板管理,安装插件后在后台-工具-邮件模板管理粘贴对应模板代码
评论