feat: 全局 UI 优化与邮件模板系统

- 新增动画系统 CSS 变量(时长、缓动函数、状态层透明度)
- 新增 prefers-reduced-motion 媒体查询支持
- 优化按钮组件样式,移除渐变背景,添加涟漪效果
- 优化分享按钮错落有致的展开动画
- 优化评论区展开动画效果
- 新增设置面板 Material 3 风格分段控件
- 新增玻璃拟态(Glassmorphism)主题变体
- 新增新拟态(Neumorphism)主题变体
- 新增邮件模板系统(base.php、comment-notify.php、reply-notify.php)
- 新增邮件模板后台设置(主题色、Logo、社交链接、预览功能)
- 集成邮件模板到评论回复通知
- 版本更新至 1.5.0
This commit is contained in:
2026-01-11 22:13:59 +08:00
parent 4fe10c84d7
commit f8d7c79b86
13 changed files with 1180 additions and 242 deletions

View File

@@ -29,8 +29,14 @@
```
style.css # 主样式文件(修改)
argontheme.js # 主脚本文件(修改)
functions.php # 主函数文件(修改 - 添加邮件模板函数)
settings.php # 设置页面(修改 - 添加邮件模板设置)
template-parts/
└── post-actions.php # 文章操作按钮模板(修改)
email-templates/
└── base.php # 邮件基础模板(新增)
└── comment-notify.php # 评论通知模板(新增)
└── reply-notify.php # 回复通知模板(新增)
```
## Components and Interfaces
@@ -139,6 +145,38 @@ template-parts/
}
```
#### 分组卡片布局
设计决策:使用分组卡片将相关设置项组织在一起,提升视觉层次和可读性。
```css
.settings-group {
background: var(--color-background);
border-radius: 12px;
padding: 12px 16px;
margin-bottom: 12px;
}
.settings-group-title {
font-size: 12px;
font-weight: 600;
color: var(--color-text-light);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.settings-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.settings-item + .settings-item {
border-top: 1px solid var(--color-border-light);
}
```
#### 分段控件 (Segmented Control)
```css
.segmented-control {
@@ -235,6 +273,22 @@ template-parts/
}
```
#### 点击外部收起逻辑
设计决策:使用事件委托监听 document 点击事件,判断点击目标是否在分享容器外部。
```javascript
// 点击外部收起分享面板
document.addEventListener('click', function(e) {
const shareContainer = document.getElementById('share_container');
if (shareContainer && shareContainer.classList.contains('opened')) {
// 检查点击目标是否在分享容器外部
if (!shareContainer.contains(e.target)) {
shareContainer.classList.remove('opened');
}
}
});
```
### 5. 评论展开动画设计
#### 评论区容器
@@ -278,8 +332,49 @@ template-parts/
}
```
#### 折叠反向动画
设计决策折叠动画使用与展开相同的过渡属性CSS transition 会自动处理反向动画。通过调整缓动函数实现自然的收起效果。
```css
/* 折叠状态 - 使用 accelerate 缓动使收起更自然 */
#comments.comments-collapsed,
#post_comment.comments-collapsed {
transition:
max-height var(--animation-slow) var(--ease-emphasized-accelerate),
opacity var(--animation-fast) var(--ease-standard-accelerate),
margin var(--animation-slow) var(--ease-standard),
padding var(--animation-slow) var(--ease-standard);
}
```
### 6. 主题变体设计
#### 悬停效果增强
设计决策:为所有可交互元素添加统一的悬停效果,使用微妙的缩放和阴影变化提升交互反馈。
```css
/* 通用悬停效果 */
.card:hover,
.btn:hover {
transform: translateY(-1px);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.1),
0 2px 4px rgba(0, 0, 0, 0.06);
transition:
transform var(--animation-fast) var(--ease-standard),
box-shadow var(--animation-fast) var(--ease-standard);
}
/* 可点击元素的微缩放效果 */
.clickable:hover {
transform: scale(1.02);
}
.clickable:active {
transform: scale(0.98);
}
```
#### 玻璃拟态 (Glassmorphism)
```css
html.style-glass .card,
@@ -314,6 +409,67 @@ html.darkmode.style-neumorphism .card {
}
```
#### Material 3 动态色彩系统
设计决策:实现 Material 3 的动态色彩系统,根据主题色自动生成协调的色彩方案。使用 CSS 变量和 HSL 色彩空间实现动态计算。
```css
:root {
/* Material 3 色彩角色 - 基于主题色动态生成 */
--md-primary: var(--themecolor);
--md-on-primary: #ffffff;
--md-primary-container: var(--themecolor-light);
--md-on-primary-container: var(--themecolor-dark2);
/* 表面色彩 */
--md-surface: var(--color-background);
--md-surface-variant: var(--color-foreground);
--md-on-surface: var(--color-text);
--md-on-surface-variant: var(--color-text-light);
/* 轮廓色彩 */
--md-outline: var(--color-border);
--md-outline-variant: var(--color-border-light);
}
/* Material 3 风格组件 */
html.style-material3 .card {
background: var(--md-surface);
border-radius: 16px;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
}
html.style-material3 .btn-primary {
background: var(--md-primary);
color: var(--md-on-primary);
border-radius: 20px;
font-weight: 500;
letter-spacing: 0.1px;
}
html.style-material3 .btn-primary:hover {
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.14),
0 3px 4px rgba(0, 0, 0, 0.12);
}
```
```javascript
// Material 3 动态色彩生成函数
function generateMaterial3Colors(primaryColor) {
// 将主题色转换为 HSL
const hsl = hexToHSL(primaryColor);
return {
primary: primaryColor,
primaryContainer: hslToHex(hsl.h, Math.max(hsl.s - 20, 0), Math.min(hsl.l + 30, 95)),
onPrimary: hsl.l > 50 ? '#000000' : '#ffffff',
onPrimaryContainer: hslToHex(hsl.h, hsl.s, Math.max(hsl.l - 40, 10))
};
}
```
## Data Models
### 设置存储结构
@@ -321,7 +477,7 @@ html.darkmode.style-neumorphism .card {
```javascript
// localStorage 存储的设置项
{
'Argon_UI_Style': 'default' | 'glass' | 'neumorphism',
'Argon_UI_Style': 'default' | 'glass' | 'neumorphism' | 'material3',
'Argon_Animation_Reduced': 'true' | 'false',
'Argon_Use_Serif': 'true' | 'false',
'Argon_Use_Big_Shadow': 'true' | 'false',
@@ -329,6 +485,216 @@ html.darkmode.style-neumorphism .card {
}
```
### 邮件模板设置结构
```php
// WordPress 选项存储的邮件模板设置
[
'argon_email_theme_color' => '#5e72e4', // 主题色
'argon_email_logo_url' => '', // Logo 图片 URL
'argon_email_blog_name' => get_bloginfo('name'), // 博客名称
'argon_email_footer_text' => '', // 页脚版权信息
'argon_email_social_links' => [ // 社交链接
'twitter' => '',
'github' => '',
'weibo' => ''
]
]
```
### 7. 邮件模板系统设计
#### 设计决策
- 使用内联 CSS 确保邮件客户端兼容性(邮件客户端不支持外部样式表)
- 采用表格布局保证跨客户端一致性Outlook 等客户端对 div 布局支持有限)
- 提供简洁的后台设置界面,仅暴露必要的自定义选项
#### 模板基础结构
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="margin: 0; padding: 0; background-color: #f4f5f7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f4f5f7; padding: 40px 20px;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="max-width: 600px; width: 100%;">
<!-- 页眉 -->
<tr>
<td style="padding: 24px; text-align: center;">
{{#if logo_url}}
<img src="{{logo_url}}" alt="{{blog_name}}" style="max-height: 48px; max-width: 200px;">
{{else}}
<h1 style="margin: 0; font-size: 24px; color: {{theme_color}};">{{blog_name}}</h1>
{{/if}}
</td>
</tr>
<!-- 内容区 -->
<tr>
<td style="background: #ffffff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08);">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td style="padding: 32px;">
{{content}}
</td>
</tr>
</table>
</td>
</tr>
<!-- 页脚 -->
<tr>
<td style="padding: 24px; text-align: center;">
{{#if social_links}}
<p style="margin: 0 0 12px 0;">
{{#each social_links}}
<a href="{{this.url}}" style="display: inline-block; margin: 0 8px; color: #8898aa; text-decoration: none;">{{this.name}}</a>
{{/each}}
</p>
{{/if}}
<p style="margin: 0; font-size: 12px; color: #8898aa;">
{{footer_text}}
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
```
#### 评论通知邮件内容模板
```html
<h2 style="margin: 0 0 16px 0; font-size: 20px; color: #32325d;">
您的文章收到了新评论
</h2>
<p style="margin: 0 0 24px 0; color: #525f7f; line-height: 1.6;">
<strong>{{commenter_name}}</strong> 在《<a href="{{post_url}}" style="color: {{theme_color}};">{{post_title}}</a>》中发表了评论:
</p>
<div style="background: #f6f9fc; border-left: 4px solid {{theme_color}}; padding: 16px; border-radius: 4px; margin: 0 0 24px 0;">
<p style="margin: 0; color: #525f7f; line-height: 1.6;">{{comment_content}}</p>
</div>
<a href="{{comment_url}}" style="display: inline-block; background: {{theme_color}}; color: #ffffff; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 500;">
查看评论
</a>
```
#### 评论回复通知邮件内容模板
```html
<h2 style="margin: 0 0 16px 0; font-size: 20px; color: #32325d;">
您的评论收到了回复
</h2>
<p style="margin: 0 0 16px 0; color: #8898aa; font-size: 14px;">
您在《<a href="{{post_url}}" style="color: {{theme_color}};">{{post_title}}</a>》的评论:
</p>
<div style="background: #f6f9fc; padding: 12px 16px; border-radius: 4px; margin: 0 0 16px 0;">
<p style="margin: 0; color: #8898aa; font-size: 14px;">{{original_comment}}</p>
</div>
<p style="margin: 0 0 16px 0; color: #525f7f;">
<strong>{{replier_name}}</strong> 回复了您:
</p>
<div style="background: #f6f9fc; border-left: 4px solid {{theme_color}}; padding: 16px; border-radius: 4px; margin: 0 0 24px 0;">
<p style="margin: 0; color: #525f7f; line-height: 1.6;">{{reply_content}}</p>
</div>
<a href="{{comment_url}}" style="display: inline-block; background: {{theme_color}}; color: #ffffff; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 500;">
查看回复
</a>
```
#### 后台设置界面
```php
// 邮件模板设置区域 - 添加到 settings.php
function argon_email_settings_section() {
// 主题色选择器
add_settings_field('argon_email_theme_color', '邮件主题色', ...);
// Logo 上传
add_settings_field('argon_email_logo_url', '邮件 Logo', ...);
// 博客名称(默认使用站点名称)
add_settings_field('argon_email_blog_name', '邮件显示名称', ...);
// 页脚文本
add_settings_field('argon_email_footer_text', '页脚版权信息', ...);
// 社交链接
add_settings_field('argon_email_social_links', '社交链接', ...);
// 预览按钮
add_settings_field('argon_email_preview', '邮件预览', ...);
}
```
#### 邮件预览功能
```javascript
// AJAX 预览邮件
function previewEmail(type) {
fetch(ajaxurl, {
method: 'POST',
body: new URLSearchParams({
action: 'argon_preview_email',
type: type, // 'comment' | 'reply'
nonce: argon_email_nonce
})
})
.then(res => res.text())
.then(html => {
// 在模态框中显示预览
openPreviewModal(html);
});
}
```
#### PHP 邮件发送函数
```php
/**
* 发送统一格式的邮件
* @param string $to 收件人
* @param string $subject 主题
* @param string $content 内容 HTML
* @param string $type 邮件类型
*/
function argon_send_email($to, $subject, $content, $type = 'general') {
$template = argon_get_email_template();
$settings = argon_get_email_settings();
// 替换模板变量
$html = str_replace([
'{{theme_color}}',
'{{logo_url}}',
'{{blog_name}}',
'{{footer_text}}',
'{{content}}'
], [
$settings['theme_color'],
$settings['logo_url'],
$settings['blog_name'],
$settings['footer_text'],
$content
], $template);
// 设置邮件头
$headers = [
'Content-Type: text/html; charset=UTF-8',
'From: ' . $settings['blog_name'] . ' <' . get_option('admin_email') . '>'
];
return wp_mail($to, $subject, $html, $headers);
}
```
## Correctness Properties
@@ -377,6 +743,42 @@ html.darkmode.style-neumorphism .card {
**Validates: Requirements 6.4**
### Property 8: 点击外部收起分享面板
*For any* 处于展开状态的分享面板,当用户点击分享容器外部区域时,分享面板应该收起(移除 .opened 类)。
**Validates: Requirements 3.5**
### Property 9: 悬停效果使用 transform 和 box-shadow
*For any* 可交互元素(.card, .btn的悬停状态应该使用 transform如 translateY 或 scale和 box-shadow 属性实现视觉反馈,而非改变元素尺寸或位置属性。
**Validates: Requirements 5.5**
### Property 10: Material 3 色彩变量定义
*For any* 启用 Material 3 样式的页面,:root 选择器中应该定义 --md-primary, --md-surface, --md-on-primary 等 Material 3 色彩角色变量。
**Validates: Requirements 6.3**
### Property 11: 邮件模板统一结构
*For any* 发送的邮件(评论通知、回复通知等),其 HTML 结构应该包含页眉logo/博客名)、内容区、页脚(版权信息)三个区域,且使用相同的基础模板。
**Validates: Requirements 7.1**
### Property 12: 邮件模板主题色应用
*For any* 邮件模板中的主题色元素(按钮背景、链接颜色、边框强调色),应该使用后台设置的 argon_email_theme_color 值,而非硬编码颜色。
**Validates: Requirements 7.2**
### Property 13: 邮件模板内联样式
*For any* 邮件模板的 HTML 输出所有样式应该以内联方式style 属性)应用,不依赖外部 CSS 文件或 `<style>` 标签,以确保邮件客户端兼容性。
**Validates: Requirements 7.7**
## Error Handling
### CSS 兼容性处理
@@ -403,6 +805,21 @@ html.darkmode.style-neumorphism .card {
- 检测 Web Animations API 支持
- 降级为 CSS transition
### 邮件发送错误处理
1. **wp_mail 发送失败**
- 记录错误日志
- 返回友好的错误提示
2. **模板变量缺失**
- 提供默认值回退
- 使用站点名称作为博客名称默认值
- 使用主题色作为邮件主题色默认值
3. **图片加载失败**
- Logo 图片加载失败时显示文字博客名称
- 使用 alt 属性提供替代文本
## Testing Strategy
### 单元测试
@@ -436,3 +853,19 @@ html.darkmode.style-neumorphism .card {
1. 使用截图对比工具验证样式变更
2. 在不同浏览器中验证兼容性
### 邮件模板测试
1. **模板渲染测试**
- 验证所有模板变量正确替换
- 验证 HTML 结构完整性
2. **邮件客户端兼容性测试**
- 在 Gmail Web 端验证显示效果
- 在 Outlook 验证表格布局
- 在 Apple Mail 验证样式渲染
3. **设置功能测试**
- 验证主题色修改后邮件颜色更新
- 验证 Logo 上传和显示
- 验证预览功能正确渲染当前设置

View File

@@ -12,6 +12,7 @@
- **Comments_Toggle**: 评论区展开/折叠切换按钮和动画系统
- **Button_Component**: 主题中的通用按钮组件
- **Animation_System**: 主题的动画效果系统
- **Email_Template_System**: 邮件通知模板系统,负责生成和发送各类邮件通知
## Requirements
@@ -89,3 +90,18 @@
3. THE Theme_System SHALL 提供 Material 3 动态色彩系统支持
4. WHEN 用户切换样式时, THE Theme_System SHALL 使用平滑过渡动画
5. THE Theme_System SHALL 样式选项在设置面板中可预览
### Requirement 7: 邮件模板优化
**User Story:** As a 博客管理员, I want 所有邮件通知使用统一且可配置的模板, so that 邮件外观专业一致,且能轻松自定义品牌风格。
#### Acceptance Criteria
1. THE Email_Template_System SHALL 提供统一的邮件模板基础结构,包含页眉、内容区、页脚三个区域
2. THE Email_Template_System SHALL 支持在后台设置邮件模板的主题色,自动应用到所有邮件类型
3. THE Email_Template_System SHALL 支持自定义邮件页眉的 Logo 图片和博客名称
4. THE Email_Template_System SHALL 支持自定义邮件页脚的版权信息和社交链接
5. WHEN 发送评论通知邮件, THE Email_Template_System SHALL 使用统一模板并包含评论内容、文章链接和回复按钮
6. WHEN 发送评论回复通知邮件, THE Email_Template_System SHALL 使用统一模板并包含原评论、回复内容和查看按钮
7. THE Email_Template_System SHALL 邮件模板兼容主流邮件客户端Gmail、Outlook、Apple Mail
8. THE Email_Template_System SHALL 提供邮件预览功能,管理员可在保存前预览邮件效果

View File

@@ -2,7 +2,7 @@
## Overview
本实现计划将 Argon 主题的 UI 优化分为 6 个主要任务,按照依赖关系顺序执行。首先建立动画系统基础,然后逐步优化各个组件,最后添加新的主题变体。
本实现计划将 Argon 主题的 UI 优化分为个主要任务,按照依赖关系顺序执行。首先建立动画系统基础,然后逐步优化各个组件,添加新的主题变体,最后实现统一的邮件模板系统
## Tasks
@@ -95,17 +95,76 @@
- 绑定点击事件切换样式
- _Requirements: 6.5_
- [ ] 7. Checkpoint - 验证所有功能
- [x] 7. Checkpoint - 验证 UI 优化功能
- 确保所有样式修改正确应用
- 测试各种浏览器兼容性
- 验证暗色模式下的显示效果
- 确保动画流畅无卡顿
- 如有问题请询问用户
- [x] 8. 实现邮件模板基础系统
- [x] 8.1 创建邮件模板目录和基础模板文件
- 创建 email-templates/ 目录
- 创建 email-templates/base.php 基础模板
- 实现页眉、内容区、页脚三段式结构
- 使用表格布局确保邮件客户端兼容性
- _Requirements: 7.1, 7.7_
- [x] 8.2 实现邮件模板渲染函数
- 在 functions.php 中添加 argon_get_email_template() 函数
- 添加 argon_render_email() 模板变量替换函数
- 添加 argon_send_email() 统一邮件发送函数
- _Requirements: 7.1_
- [x] 9. 实现邮件模板后台设置
- [x] 9.1 添加邮件模板设置选项
- 在 settings.php 中添加"邮件模板"设置区域
- 添加主题色选择器(颜色选择器控件)
- 添加 Logo 图片上传控件
- 添加博客显示名称输入框
- _Requirements: 7.2, 7.3_
- [x] 9.2 添加页脚和社交链接设置
- 添加页脚版权信息文本框
- 添加社交链接输入组Twitter、GitHub、微博等
- 实现设置保存和读取逻辑
- _Requirements: 7.4_
- [x] 9.3 实现邮件预览功能
- 添加 AJAX 预览接口 argon_preview_email
- 创建预览模态框 UI
- 支持预览评论通知和回复通知两种类型
- _Requirements: 7.8_
- [x] 10. 实现具体邮件类型模板
- [x] 10.1 创建评论通知邮件模板
- 创建 email-templates/comment-notify.php
- 包含评论者名称、文章标题、评论内容
- 添加"查看评论"按钮链接
- _Requirements: 7.5_
- [x] 10.2 创建评论回复通知邮件模板
- 创建 email-templates/reply-notify.php
- 包含原评论内容、回复者名称、回复内容
- 添加"查看回复"按钮链接
- _Requirements: 7.6_
- [x] 10.3 集成邮件模板到现有通知逻辑
- 修改 functions.php 中的评论通知发送逻辑
- 替换原有邮件内容为新模板渲染结果
- 确保向后兼容(设置为空时使用默认值)
- _Requirements: 7.5, 7.6_
- [x] 11. Checkpoint - 验证邮件模板功能
- 测试邮件模板设置保存和读取
- 测试邮件预览功能
- 发送测试邮件验证实际效果
- 在 Gmail、Outlook 中验证显示效果
- 如有问题请询问用户
## Notes
- 所有 CSS 修改应在 style.css 文件中进行
- JavaScript 修改应在 argontheme.js 文件中进行
- PHP 模板修改应在 template-parts/post-actions.php 中进行
- 邮件模板文件放置在 email-templates/ 目录
- 邮件模板设置添加到 settings.php
- 邮件模板函数添加到 functions.php
- 优先使用 CSS 变量以保持一致性和可维护性
- 动画应使用 GPU 加速属性transform, opacity以确保性能
- 邮件模板必须使用内联样式和表格布局以确保客户端兼容性

View File

@@ -0,0 +1,106 @@
# Requirements Document
## Introduction
本文档定义了 ArgonAve 主题隐私政策页面及相关移动端 UI 问题的修复需求。主要涉及侧边抽屉栏功能缺失、转发按钮动画异常、以及首页文章特色图片显示问题。
## Glossary
- **Sidebar_Drawer**: 移动端侧边折叠抽屉组件,包含文章目录、多邻国连胜记录等功能
- **TOC_Generator**: 文章目录生成器,根据文章标题自动生成导航目录
- **Duolingo_Streak**: 多邻国连胜记录显示组件
- **Ripple_Effect**: 涟漪点击效果动画
- **Theme_Switcher**: 暗黑/明亮模式切换组件
- **Share_Panel**: 文章分享面板组件
- **Featured_Image**: 文章特色图片/缩略图组件
## Requirements
### Requirement 1: 侧边抽屉文章目录生成
**User Story:** As a 移动端用户, I want 在侧边抽屉中看到正常的文章目录, so that 我可以快速导航到文章的各个章节。
#### Acceptance Criteria
1. WHEN 用户在隐私政策页面打开侧边抽屉, THE TOC_Generator SHALL 正确解析页面中的标题元素h1-h6并生成目录
2. WHEN 页面包含多级标题, THE TOC_Generator SHALL 以层级缩进方式显示目录结构
3. WHEN 用户点击目录项, THE Sidebar_Drawer SHALL 平滑滚动到对应的标题位置
4. IF 页面没有可识别的标题元素, THEN THE TOC_Generator SHALL 显示"无目录"提示而非空白
### Requirement 2: 多邻国连胜记录显示
**User Story:** As a 用户, I want 在侧边抽屉中看到多邻国连胜记录, so that 我可以了解博主的学习进度。
#### Acceptance Criteria
1. WHEN 用户打开侧边抽屉且多邻国功能已启用, THE Duolingo_Streak SHALL 正确显示连胜天数
2. WHEN 多邻国 API 请求成功, THE Duolingo_Streak SHALL 显示用户头像、用户名和连胜火焰图标
3. IF 多邻国 API 请求失败, THEN THE Duolingo_Streak SHALL 显示友好的错误提示或隐藏该组件
4. THE Duolingo_Streak SHALL 在隐私政策页面与其他页面保持一致的显示效果
### Requirement 3: 涟漪点击效果优化
**User Story:** As a 用户, I want 点击按钮时看到自然的涟漪效果, so that 交互反馈更加流畅自然。
#### Acceptance Criteria
1. WHEN 用户点击侧边抽屉中的按钮, THE Ripple_Effect SHALL 从点击位置向外扩散
2. WHEN 涟漪动画结束, THE Ripple_Effect SHALL 以淡出方式消失,而非倒放收缩
3. THE Ripple_Effect SHALL 使用 opacity 淡出而非 scale 收缩作为结束动画
4. THE Ripple_Effect SHALL 动画时长控制在 300-400ms 范围内
### Requirement 4: 暗黑/明亮模式切换流畅性
**User Story:** As a 用户, I want 暗黑和明亮模式之间的切换更加流畅, so that 视觉体验不会突兀。
#### Acceptance Criteria
1. WHEN 用户切换主题模式, THE Theme_Switcher SHALL 使用平滑的颜色过渡动画
2. THE Theme_Switcher SHALL 所有颜色变化使用 CSS transition时长为 200-300ms
3. THE Theme_Switcher SHALL 背景色、文字色、边框色同步过渡
4. THE Theme_Switcher SHALL 避免闪烁或跳变效果
### Requirement 5: 转发按钮退出动画优化
**User Story:** As a 用户, I want 转发面板关闭时有正确的退出动画, so that 交互体验更加自然。
#### Acceptance Criteria
1. WHEN 用户关闭转发面板, THE Share_Panel SHALL 使用向上滑出的退出动画
2. THE Share_Panel SHALL 退出动画方向与进入动画方向一致(上滑进入则上滑退出)
3. THE Share_Panel SHALL 退出动画不应出现歪斜或偏移
4. THE Share_Panel SHALL 退出动画时长与进入动画时长一致
### Requirement 6: 转发选项溢出修复
**User Story:** As a 用户, I want 转发选项完整显示在窗口内, so that 我可以看到并点击所有分享选项。
#### Acceptance Criteria
1. THE Share_Panel SHALL 所有分享选项完整显示在视口范围内
2. WHEN 分享选项数量较多, THE Share_Panel SHALL 使用滚动或换行方式适配
3. THE Share_Panel SHALL 在各种屏幕尺寸下保持选项可见性
4. THE Share_Panel SHALL 与屏幕边缘保持适当的安全边距
### Requirement 7: 转发动画流畅性优化
**User Story:** As a 用户, I want 转发面板的动画更加流畅顺滑, so that 整体交互体验更好。
#### Acceptance Criteria
1. THE Share_Panel SHALL 使用 GPU 加速属性transform, opacity进行动画
2. THE Share_Panel SHALL 动画使用合适的缓动函数ease-out 或 cubic-bezier
3. THE Share_Panel SHALL 避免动画过程中出现抖动或卡顿
4. THE Share_Panel SHALL 分享图标按钮使用错落有致的出现动画
### Requirement 8: 首页文章特色图片显示
**User Story:** As a 用户, I want 在首页看到文章的特色图片, so that 我可以通过视觉预览了解文章内容。
#### Acceptance Criteria
1. WHEN 文章设置了特色图片, THE Featured_Image SHALL 在首页文章列表中正确显示
2. THE Featured_Image SHALL 图片尺寸和比例与卡片布局适配
3. IF 文章没有设置特色图片, THEN THE Featured_Image SHALL 显示默认占位图或隐藏图片区域
4. THE Featured_Image SHALL 支持懒加载以优化页面性能

279
email-templates/base.php Normal file
View File

@@ -0,0 +1,279 @@
<?php
/**
* Argon 邮件基础模板
*
* 使用表格布局确保邮件客户端兼容性
* 所有样式使用内联方式
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 获取邮件模板设置
*/
function argon_get_email_settings() {
$theme_color = get_option('argon_email_theme_color', '#5e72e4');
$logo_url = get_option('argon_email_logo_url', '');
$blog_name = get_option('argon_email_blog_name', '');
$footer_text = get_option('argon_email_footer_text', '');
$social_links = get_option('argon_email_social_links', array());
// 使用默认值
if (empty($blog_name)) {
$blog_name = get_bloginfo('name');
}
if (empty($footer_text)) {
$footer_text = '© ' . date('Y') . ' ' . $blog_name . '. All rights reserved.';
}
return array(
'theme_color' => $theme_color,
'logo_url' => $logo_url,
'blog_name' => $blog_name,
'footer_text' => $footer_text,
'social_links' => $social_links
);
}
/**
* 获取邮件基础模板 HTML
*/
function argon_get_email_template() {
$settings = argon_get_email_settings();
// 页眉部分
$header_html = '';
if (!empty($settings['logo_url'])) {
$header_html = '<img src="' . esc_url($settings['logo_url']) . '" alt="' . esc_attr($settings['blog_name']) . '" style="max-height: 48px; max-width: 200px;">';
} else {
$header_html = '<h1 style="margin: 0; font-size: 24px; font-weight: 600; color: ' . esc_attr($settings['theme_color']) . ';">' . esc_html($settings['blog_name']) . '</h1>';
}
// 社交链接部分
$social_html = '';
if (!empty($settings['social_links']) && is_array($settings['social_links'])) {
$social_items = array();
$social_names = array(
'twitter' => 'Twitter',
'github' => 'GitHub',
'weibo' => '微博',
'facebook' => 'Facebook',
'instagram' => 'Instagram'
);
foreach ($settings['social_links'] as $key => $url) {
if (!empty($url)) {
$name = isset($social_names[$key]) ? $social_names[$key] : ucfirst($key);
$social_items[] = '<a href="' . esc_url($url) . '" style="display: inline-block; margin: 0 8px; color: #8898aa; text-decoration: none;">' . esc_html($name) . '</a>';
}
}
if (!empty($social_items)) {
$social_html = '<p style="margin: 0 0 12px 0;">' . implode('', $social_items) . '</p>';
}
}
$template = '<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{subject}}</title>
</head>
<body style="margin: 0; padding: 0; background-color: #f4f5f7; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, \'Helvetica Neue\', Arial, sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f4f5f7; padding: 40px 20px;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" border="0" style="max-width: 600px; width: 100%;">
<!-- 页眉 -->
<tr>
<td style="padding: 24px; text-align: center;">
' . $header_html . '
</td>
</tr>
<!-- 内容区 -->
<tr>
<td style="background: #ffffff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08);">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 32px;">
{{content}}
</td>
</tr>
</table>
</td>
</tr>
<!-- 页脚 -->
<tr>
<td style="padding: 24px; text-align: center;">
' . $social_html . '
<p style="margin: 0; font-size: 12px; color: #8898aa;">
' . esc_html($settings['footer_text']) . '
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>';
return $template;
}
/**
* 渲染邮件模板
*
* @param string $content 邮件内容 HTML
* @param array $vars 额外的模板变量
* @return string 渲染后的完整邮件 HTML
*/
function argon_render_email($content, $vars = array()) {
$settings = argon_get_email_settings();
$template = argon_get_email_template();
// 替换内容占位符
$html = str_replace('{{content}}', $content, $template);
// 替换主题色
$html = str_replace('{{theme_color}}', esc_attr($settings['theme_color']), $html);
// 替换其他变量
if (!empty($vars)) {
foreach ($vars as $key => $value) {
$html = str_replace('{{' . $key . '}}', $value, $html);
}
}
return $html;
}
/**
* 发送统一格式的邮件
*
* @param string $to 收件人邮箱
* @param string $subject 邮件主题
* @param string $content 邮件内容 HTML
* @param string $type 邮件类型 (comment, reply, general)
* @return bool 发送是否成功
*/
function argon_send_email($to, $subject, $content, $type = 'general') {
$settings = argon_get_email_settings();
// 渲染完整邮件
$html = argon_render_email($content, array('subject' => $subject));
// 设置邮件头
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: ' . $settings['blog_name'] . ' <' . get_option('admin_email') . '>'
);
// 发送邮件
$result = wp_mail($to, $subject, $html, $headers);
// 记录错误日志
if (!$result) {
error_log('Argon Email: Failed to send ' . $type . ' email to ' . $to);
}
return $result;
}
/**
* AJAX 邮件预览接口
*/
add_action('wp_ajax_argon_preview_email', 'argon_preview_email_handler');
function argon_preview_email_handler() {
// 验证 nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'argon_preview_email')) {
wp_die('Invalid nonce');
}
// 检查权限
if (!current_user_can('manage_options')) {
wp_die('Permission denied');
}
$type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'comment';
$settings = argon_get_email_settings();
// 生成示例内容
if ($type === 'reply') {
$content = argon_get_reply_notify_content(array(
'post_title' => '示例文章标题',
'post_url' => home_url('/sample-post/'),
'original_comment' => '这是原始评论的内容,用于展示邮件模板效果。',
'replier_name' => '回复者',
'reply_content' => '这是回复内容的示例文本,展示了邮件模板中回复通知的样式效果。感谢您的评论!',
'comment_url' => home_url('/sample-post/#comment-1'),
'theme_color' => $settings['theme_color']
));
} else {
$content = argon_get_comment_notify_content(array(
'commenter_name' => '评论者',
'post_title' => '示例文章标题',
'post_url' => home_url('/sample-post/'),
'comment_content' => '这是评论内容的示例文本,展示了邮件模板中评论通知的样式效果。非常感谢您的精彩文章!',
'comment_url' => home_url('/sample-post/#comment-1'),
'theme_color' => $settings['theme_color']
));
}
// 渲染完整邮件
$html = argon_render_email($content, array('subject' => '邮件预览'));
echo $html;
wp_die();
}
/**
* 生成评论通知邮件内容
*/
function argon_get_comment_notify_content($vars) {
$theme_color = isset($vars['theme_color']) ? $vars['theme_color'] : '#5e72e4';
return '<h2 style="margin: 0 0 16px 0; font-size: 20px; font-weight: 600; color: #32325d;">
您的文章收到了新评论
</h2>
<p style="margin: 0 0 24px 0; color: #525f7f; line-height: 1.6;">
<strong>' . esc_html($vars['commenter_name']) . '</strong> 在《<a href="' . esc_url($vars['post_url']) . '" style="color: ' . esc_attr($theme_color) . '; text-decoration: none;">' . esc_html($vars['post_title']) . '</a>》中发表了评论:
</p>
<div style="background: #f6f9fc; border-left: 4px solid ' . esc_attr($theme_color) . '; padding: 16px; border-radius: 4px; margin: 0 0 24px 0;">
<p style="margin: 0; color: #525f7f; line-height: 1.6;">' . esc_html($vars['comment_content']) . '</p>
</div>
<a href="' . esc_url($vars['comment_url']) . '" style="display: inline-block; background: ' . esc_attr($theme_color) . '; color: #ffffff; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 500;">
查看评论
</a>';
}
/**
* 生成评论回复通知邮件内容
*/
function argon_get_reply_notify_content($vars) {
$theme_color = isset($vars['theme_color']) ? $vars['theme_color'] : '#5e72e4';
return '<h2 style="margin: 0 0 16px 0; font-size: 20px; font-weight: 600; color: #32325d;">
您的评论收到了回复
</h2>
<p style="margin: 0 0 16px 0; color: #8898aa; font-size: 14px;">
您在《<a href="' . esc_url($vars['post_url']) . '" style="color: ' . esc_attr($theme_color) . '; text-decoration: none;">' . esc_html($vars['post_title']) . '</a>》的评论:
</p>
<div style="background: #f6f9fc; padding: 12px 16px; border-radius: 4px; margin: 0 0 16px 0;">
<p style="margin: 0; color: #8898aa; font-size: 14px;">' . esc_html($vars['original_comment']) . '</p>
</div>
<p style="margin: 0 0 16px 0; color: #525f7f;">
<strong>' . esc_html($vars['replier_name']) . '</strong> 回复了您:
</p>
<div style="background: #f6f9fc; border-left: 4px solid ' . esc_attr($theme_color) . '; padding: 16px; border-radius: 4px; margin: 0 0 24px 0;">
<p style="margin: 0; color: #525f7f; line-height: 1.6;">' . esc_html($vars['reply_content']) . '</p>
</div>
<a href="' . esc_url($vars['comment_url']) . '" style="display: inline-block; background: ' . esc_attr($theme_color) . '; color: #ffffff; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 500;">
查看回复
</a>';
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Argon 评论通知邮件模板
*
* 当博客收到新评论时发送给管理员的通知邮件
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 发送评论通知邮件给管理员
*
* @param WP_Comment $comment 评论对象
* @return bool 发送是否成功
*/
function argon_send_comment_notify_email($comment) {
// 获取文章信息
$post = get_post($comment->comment_post_ID);
if (!$post) {
return false;
}
// 获取管理员邮箱
$admin_email = get_option('admin_email');
if (empty($admin_email)) {
return false;
}
// 不给自己发通知
$comment_author_email = strtolower($comment->comment_author_email);
if ($comment_author_email === strtolower($admin_email)) {
return false;
}
$settings = argon_get_email_settings();
// 准备邮件内容
$content = argon_get_comment_notify_content(array(
'commenter_name' => $comment->comment_author,
'post_title' => $post->post_title,
'post_url' => get_permalink($post->ID),
'comment_content' => $comment->comment_content,
'comment_url' => get_comment_link($comment),
'theme_color' => $settings['theme_color']
));
// 邮件主题
$subject = sprintf(
'[%s] 新评论:%s',
$settings['blog_name'],
$post->post_title
);
// 发送邮件
return argon_send_email($admin_email, $subject, $content, 'comment');
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Argon 评论回复通知邮件模板
*
* 当评论收到回复时发送给原评论者的通知邮件
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 发送评论回复通知邮件
*
* @param WP_Comment $reply 回复评论对象
* @param WP_Comment $parent 被回复的评论对象
* @return bool 发送是否成功
*/
function argon_send_reply_notify_email($reply, $parent) {
// 检查父评论是否存在
if (!$parent || empty($parent->comment_author_email)) {
return false;
}
// 获取文章信息
$post = get_post($reply->comment_post_ID);
if (!$post) {
return false;
}
// 不给自己发通知
$reply_author_email = strtolower($reply->comment_author_email);
$parent_author_email = strtolower($parent->comment_author_email);
if ($reply_author_email === $parent_author_email) {
return false;
}
$settings = argon_get_email_settings();
// 准备邮件内容
$content = argon_get_reply_notify_content(array(
'post_title' => $post->post_title,
'post_url' => get_permalink($post->ID),
'original_comment' => wp_trim_words($parent->comment_content, 50, '...'),
'replier_name' => $reply->comment_author,
'reply_content' => $reply->comment_content,
'comment_url' => get_comment_link($reply),
'theme_color' => $settings['theme_color']
));
// 邮件主题
$subject = sprintf(
'[%s] 您的评论收到了回复',
$settings['blog_name']
);
// 发送邮件
return argon_send_email($parent->comment_author_email, $subject, $content, 'reply');
}

View File

@@ -22,135 +22,8 @@
</div>
<!-- Argon 修复补丁 - 在所有脚本加载后重新注册插件 -->
<script>
(function() {
if (typeof jQuery !== 'undefined') {
var $ = jQuery;
// 强制重新注册 headIndex 插件
$.fn.headIndex = function(options) {
var defaults = {
articleWrapSelector: '#post_content',
indexBoxSelector: '#leftbar_catalog',
subItemBoxClass: 'index-subItem-box',
scrollOffset: 80,
activeClass: 'active'
};
var settings = $.extend({}, defaults, options);
var $articleWrap = $(settings.articleWrapSelector);
var $indexBox = $(settings.indexBoxSelector);
if (!$articleWrap.length || !$indexBox.length) {
return this;
}
var $headings = $articleWrap.find('h1, h2, h3, h4, h5, h6');
if ($headings.length === 0) {
$indexBox.html('<div class="no-catalog">暂无目录</div>');
return this;
}
// 生成目录 HTML不带序号
var catalogHtml = '<ul class="catalog-list">';
var minLevel = 6;
$headings.each(function() {
var level = parseInt(this.tagName.substring(1));
if (level < minLevel) minLevel = level;
});
$headings.each(function(index) {
var $heading = $(this);
var level = parseInt(this.tagName.substring(1));
var text = $heading.text().trim();
var id = $heading.attr('id') || 'heading-' + index;
if (!$heading.attr('id')) {
$heading.attr('id', id);
}
var indent = (level - minLevel) * 15;
catalogHtml += '<li style="padding-left: ' + indent + 'px;">' +
'<a href="#' + id + '" class="catalog-link" data-target="' + id + '">' +
text + '</a></li>';
});
catalogHtml += '</ul>';
$indexBox.html(catalogHtml);
// 点击事件
$indexBox.off('click.headIndex').on('click.headIndex', '.catalog-link', function(e) {
e.preventDefault();
var targetId = $(this).data('target');
var $target = $('#' + targetId);
if ($target.length) {
$('html, body').animate({
scrollTop: $target.offset().top - settings.scrollOffset
}, 500);
$indexBox.find('.catalog-link').removeClass(settings.activeClass);
$(this).addClass(settings.activeClass);
}
});
// 滚动高亮
var throttleTimer = null;
$(window).off('scroll.headIndex').on('scroll.headIndex', function() {
if (throttleTimer) clearTimeout(throttleTimer);
throttleTimer = setTimeout(function() {
var scrollTop = $(window).scrollTop();
var currentHeading = null;
$headings.each(function() {
var headingTop = $(this).offset().top - settings.scrollOffset - 50;
if (scrollTop >= headingTop) {
currentHeading = $(this).attr('id');
}
});
if (currentHeading) {
$indexBox.find('.catalog-link').removeClass(settings.activeClass);
$indexBox.find('.catalog-link[data-target="' + currentHeading + '"]')
.addClass(settings.activeClass);
}
}, 100);
});
return this;
};
window.argonPluginsReady = true;
}
})();
</script>
<!-- argontheme.js 需要在 jQuery 和插件加载后执行 -->
<script>
(function() {
function loadArgonTheme() {
// 检查 jQuery 和关键插件是否都已加载
if (typeof jQuery !== 'undefined' &&
typeof jQuery.fn.lazyload === 'function' &&
window.argonPluginsReady === true) {
var script = document.createElement('script');
script.src = '<?php echo $GLOBALS['assets_path']; ?>/argontheme.js?v<?php echo $GLOBALS['theme_version']; ?>';
document.body.appendChild(script);
} else {
setTimeout(loadArgonTheme, 50);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadArgonTheme);
} else {
loadArgonTheme();
}
})();
</script>
<!-- 加载主题核心 JS -->
<script src="<?php echo $GLOBALS['assets_path']; ?>/argontheme.js?v=<?php echo $GLOBALS['theme_version']; ?>"></script>
<?php if (get_option('argon_math_render') == 'mathjax3') { /*Mathjax V3*/?>
@@ -162,7 +35,7 @@
inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
displayMath: [['$$','$$']],
displayMath: [['$','$']],
processEscapes: true,
@@ -190,15 +63,7 @@
</script>
<script src="<?php echo get_option('argon_mathjax_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js' : get_option('argon_mathjax_cdn_url'); ?>" id="MathJax-script" async onerror="
var fallbackScript = document.createElement('script');
fallbackScript.src = '<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/external/mathjax3/tex-chtml-full.js';
fallbackScript.async = true;
document.head.appendChild(fallbackScript);
if (typeof ArgonLogger !== 'undefined') {
ArgonLogger.warn('MathJax 3 CDN失败使用本地备用');
}
"></script>
<script src="<?php echo get_option('argon_mathjax_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js' : get_option('argon_mathjax_cdn_url'); ?>" id="MathJax-script" async></script>
<?php }?>
@@ -214,7 +79,7 @@
inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
displayMath: [['$$','$$']],
displayMath: [['$','$']],
processEscapes: true,
@@ -240,24 +105,17 @@
</script>
<script src="<?php echo get_option('argon_mathjax_v2_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mathjax@2.7.5/MathJax.js?config=TeX-AMS_HTML' : get_option('argon_mathjax_v2_cdn_url'); ?>" onerror="
var fallbackScript = document.createElement('script');
fallbackScript.src = '<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/external/mathjax2/MathJax.js';
document.head.appendChild(fallbackScript);
if (typeof ArgonLogger !== 'undefined') {
ArgonLogger.warn('MathJax 2 CDN失败使用本地备用');
}
"></script>
<script src="<?php echo get_option('argon_mathjax_v2_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mathjax@2.7.5/MathJax.js?config=TeX-AMS_HTML' : get_option('argon_mathjax_v2_cdn_url'); ?>"></script>
<?php }?>
<?php if (get_option('argon_math_render') == 'katex') { /*Katex*/?>
<link rel="stylesheet" href="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>katex.min.css" onerror="this.href='<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/external/katex/katex.min.css'">
<link rel="stylesheet" href="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>katex.min.css">
<script src="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>katex.min.js" onerror="this.src='<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/external/katex/katex.min.js'"></script>
<script src="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>katex.min.js"></script>
<script src="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>contrib/auto-render.min.js" onerror="this.src='<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/external/katex/auto-render.min.js'"></script>
<script src="<?php echo get_option('argon_katex_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/katex@0.11.1/dist/' : get_option('argon_katex_cdn_url'); ?>contrib/auto-render.min.js"></script>
<script>
@@ -267,7 +125,7 @@
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: true},
{left: "$", right: "$", display: false},
@@ -308,4 +166,3 @@
</html>

View File

@@ -9,11 +9,6 @@
* @link https://www.gnu.org/licenses/gpl-3.0.html
*/
// 强制使用本地资源(修复 JavaScript 错误)
add_filter('option_argon_assets_path', function($value) {
return ''; // 空值表示使用本地资源
});
if (version_compare( $GLOBALS['wp_version'], '4.4-alpha', '<' )) {
echo "<div style='background: #5e72e4;color: #fff;font-size: 30px;padding: 50px 30px;position: fixed;width: 100%;left: 0;right: 0;bottom: 0;z-index: 2147483647;'>" . __("Argon 主题不支持 Wordpress 4.4 以下版本,请更新 Wordpress", 'argon') . "</div>";
}
@@ -26,33 +21,9 @@ add_action('after_setup_theme','theme_slug_setup');
$argon_version = !(wp_get_theme() -> Template) ? wp_get_theme() -> Version : wp_get_theme(wp_get_theme() -> Template) -> Version;
$GLOBALS['theme_version'] = $argon_version;
$argon_assets_path = get_option("argon_assets_path");
switch ($argon_assets_path) {
case "jsdelivr":
$GLOBALS['assets_path'] = "https://cdn.jsdelivr.net/gh/solstice23/argon-theme@" . $argon_version;
break;
case "fastgit":
$GLOBALS['assets_path'] = "https://raw.fastgit.org/solstice23/argon-theme/v" . $argon_version;
break;
case "sourcegcdn":
$GLOBALS['assets_path'] = "https://gh.sourcegcdn.com/solstice23/argon-theme/v" . $argon_version;
break;
case "jsdelivr_gcore":
$GLOBALS['assets_path'] = "https://gcore.jsdelivr.net/gh/solstice23/argon-theme@" . $argon_version;
break;
case "jsdelivr_fastly":
$GLOBALS['assets_path'] = "https://fastly.jsdelivr.net/gh/solstice23/argon-theme@" . $argon_version;
break;
case "jsdelivr_cf":
$GLOBALS['assets_path'] = "https://testingcf.jsdelivr.net/gh/solstice23/argon-theme@" . $argon_version;
break;
case "custom":
$GLOBALS['assets_path'] = preg_replace('/\/$/', '', get_option("argon_custom_assets_path"));
$GLOBALS['assets_path'] = preg_replace('/%theme_version%/', $argon_version, $GLOBALS['assets_path']);
break;
default:
$GLOBALS['assets_path'] = get_bloginfo('template_url');
}
// 强制使用本地资源,避免 CDN 加载问题
$GLOBALS['assets_path'] = get_bloginfo('template_url');
//翻译 Hook
function argon_locate_filter($locate){
@@ -136,6 +107,11 @@ if (version_compare($argon_last_version, $GLOBALS['theme_version'], '<' )){
}
//引入邮件模板系统
require_once(get_template_directory() . '/email-templates/base.php');
require_once(get_template_directory() . '/email-templates/comment-notify.php');
require_once(get_template_directory() . '/email-templates/reply-notify.php');
//检测更新
require_once(get_template_directory() . '/theme-update-checker/plugin-update-checker.php');
$argon_update_source = get_option('argon_update_source');
@@ -1847,57 +1823,30 @@ function comment_mail_notify($comment){
$content = htmlspecialchars(get_comment_meta($id, "comment_content_source", true));
$link = get_permalink($commentPostID) . "#comment-" . $id;
$unsubscribeLink = site_url("unsubscribe-comment-mailnotice?comment=" . $parentID . "&token=" . get_comment_meta($parentID, "mailnotice_unsubscribe_key", true));
$html = '
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
</head>
<body>
<div style="background: #fff;box-shadow: 0 15px 35px rgba(50,50,93,.1), 0 5px 15px rgba(0,0,0,.07);border-radius: 6px;margin: 15px auto 50px auto;padding: 35px 30px;max-width: min(calc(100% - 100px), 1200px);">
<div style="font-size:30px;text-align:center;margin-bottom:15px;">' . htmlspecialchars($fullTitle) .'</div>
<div style="background: rgba(0, 0, 0, .15);height: 1px;width: 300px;margin: auto;margin-bottom: 35px;"></div>
<div style="font-size: 18px;border-left: 4px solid rgba(0, 0, 0, .15);width: max-content;width: -moz-max-content;margin: auto;padding: 20px 30px;background: rgba(0,0,0,.08);border-radius: 6px;box-shadow: 0 2px 4px rgba(0,0,0,.075)!important;min-width: 60%;max-width: 90%;margin-bottom: 40px;">
<div style="margin-bottom: 10px;"><strong><span style="color: #5e72e4;">@' . htmlspecialchars($commentAuthor) . '</span> ' . __('回复了你', "argon") . ':</strong></div>
' . str_replace('\n', '<div></div>', $content) . '
</div>
<table width="100%" style="border-collapse:collapse;border:none;empty-cells:show;max-width:100%;box-sizing:border-box" cellspacing="0" cellpadding="0">
<tbody style="box-sizing:border-box">
<tr style="box-sizing:border-box" align="center">
<td style="min-width:5px;box-sizing:border-box">
<table style="border-collapse:collapse;border:none;empty-cells:show;max-width:100%;box-sizing:border-box" cellspacing="0" cellpadding="0">
<tbody style="box-sizing:border-box">
<tr style="box-sizing:border-box">
<td style="box-sizing:border-box">
<a href="' . $link . '" style="display: block; line-height: 1; color: #fff;background-color: #5e72e4;border-color: #5e72e4;box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08);padding: 15px 25px;font-size: 18px;border-radius: 4px;text-decoration: none; margin: 10px;">' . __('前往查看', "argon") . '</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table width="100%" style="border-collapse:collapse;border:none;empty-cells:show;max-width:100%;box-sizing:border-box" cellspacing="0" cellpadding="0">
<tbody style="box-sizing:border-box">
<tr style="box-sizing:border-box" align="center">
<td style="min-width:5px;box-sizing:border-box">
<table style="border-collapse:collapse;border:none;empty-cells:show;max-width:100%;box-sizing:border-box" cellspacing="0" cellpadding="0">
<tbody style="box-sizing:border-box">
<tr style="box-sizing:border-box">
<td style="box-sizing:border-box">
<a href="' . $unsubscribeLink . '" style="display: block; line-height: 1;color: #5e72e4;font-size: 16px;text-decoration: none; margin: 10px;">' . __('退订该评论的邮件提醒', "argon") . '</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>';
// 使用新的邮件模板系统
$settings = argon_get_email_settings();
$post = get_post($commentPostID);
// 生成新模板内容
$email_content = argon_get_reply_notify_content(array(
'post_title' => $post->post_title,
'post_url' => get_permalink($post->ID),
'original_comment' => wp_trim_words($parentComment->comment_content, 50, '...'),
'replier_name' => $commentAuthor,
'reply_content' => $content,
'comment_url' => $link,
'theme_color' => $settings['theme_color']
));
// 添加退订链接
$email_content .= '<p style="margin-top: 24px; text-align: center;">
<a href="' . esc_url($unsubscribeLink) . '" style="color: #8898aa; font-size: 12px; text-decoration: none;">' . __('退订该评论的邮件提醒', 'argon') . '</a>
</p>';
// 渲染完整邮件
$html = argon_render_email($email_content, array('subject' => $title));
$html = apply_filters("argon_comment_mail_notification_content", $html);
send_mail($emailTo, $title, $html);
}

View File

@@ -1,5 +1,5 @@
{
"version": "1.4.0",
"version": "1.5.0",
"details_url": "https://github.com/nanhaoluo/argon-theme/releases",
"download_url": "https://github.com/nanhaoluo/argon-theme/releases/download/v1.4.0/argon.zip"
"download_url": "https://github.com/nanhaoluo/argon-theme/releases/download/v1.5.0/argon.zip"
}

View File

@@ -4007,6 +4007,114 @@ window.pjaxLoaded = function(){
</tr>
<!-- ========== 邮件模板设置 ========== -->
<tr><th class="subtitle"><h3 id="subsection-email-template"><?php _e('邮件模板', 'argon');?></h3></th></tr>
<tr>
<th><label><?php _e('邮件主题色', 'argon');?></label></th>
<td>
<input type="text" class="regular-text" name="argon_email_theme_color" value="<?php echo esc_attr(get_option('argon_email_theme_color', '#5e72e4')); ?>" style="width: 100px;" />
<input type="color" value="<?php echo esc_attr(get_option('argon_email_theme_color', '#5e72e4')); ?>" onchange="document.querySelector('input[name=argon_email_theme_color]').value = this.value;" style="vertical-align: middle; cursor: pointer;" />
<p class="description"><?php _e('邮件中按钮、链接和强调色的颜色', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('邮件 Logo', 'argon');?></label></th>
<td>
<input type="text" class="regular-text" name="argon_email_logo_url" value="<?php echo esc_attr(get_option('argon_email_logo_url', '')); ?>" placeholder="https://example.com/logo.png" />
<button type="button" class="button" onclick="argonUploadEmailLogo();"><?php _e('上传图片', 'argon');?></button>
<p class="description"><?php _e('邮件页眉显示的 Logo 图片 URL留空则显示博客名称文字', 'argon');?></p>
<script>
function argonUploadEmailLogo() {
var mediaUploader = wp.media({
title: '<?php _e('选择 Logo 图片', 'argon');?>',
button: { text: '<?php _e('使用此图片', 'argon');?>' },
multiple: false
});
mediaUploader.on('select', function() {
var attachment = mediaUploader.state().get('selection').first().toJSON();
document.querySelector('input[name=argon_email_logo_url]').value = attachment.url;
});
mediaUploader.open();
}
</script>
</td>
</tr>
<tr>
<th><label><?php _e('邮件显示名称', 'argon');?></label></th>
<td>
<input type="text" class="regular-text" name="argon_email_blog_name" value="<?php echo esc_attr(get_option('argon_email_blog_name', '')); ?>" placeholder="<?php echo esc_attr(get_bloginfo('name')); ?>" />
<p class="description"><?php _e('邮件中显示的博客名称,留空则使用站点名称', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('页脚版权信息', 'argon');?></label></th>
<td>
<input type="text" class="regular-text" name="argon_email_footer_text" value="<?php echo esc_attr(get_option('argon_email_footer_text', '')); ?>" placeholder="© <?php echo date('Y'); ?> <?php echo esc_attr(get_bloginfo('name')); ?>. All rights reserved." />
<p class="description"><?php _e('邮件页脚显示的版权信息,留空则使用默认格式', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('社交链接', 'argon');?></label></th>
<td>
<?php $social_links = get_option('argon_email_social_links', array()); ?>
<div style="margin-bottom: 8px;">
<label style="display: inline-block; width: 80px;">Twitter:</label>
<input type="text" name="argon_email_social_twitter" value="<?php echo esc_attr(isset($social_links['twitter']) ? $social_links['twitter'] : ''); ?>" placeholder="https://twitter.com/username" style="width: 300px;" />
</div>
<div style="margin-bottom: 8px;">
<label style="display: inline-block; width: 80px;">GitHub:</label>
<input type="text" name="argon_email_social_github" value="<?php echo esc_attr(isset($social_links['github']) ? $social_links['github'] : ''); ?>" placeholder="https://github.com/username" style="width: 300px;" />
</div>
<div style="margin-bottom: 8px;">
<label style="display: inline-block; width: 80px;"><?php _e('微博', 'argon');?>:</label>
<input type="text" name="argon_email_social_weibo" value="<?php echo esc_attr(isset($social_links['weibo']) ? $social_links['weibo'] : ''); ?>" placeholder="https://weibo.com/username" style="width: 300px;" />
</div>
<p class="description"><?php _e('邮件页脚显示的社交链接,留空则不显示', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('邮件预览', 'argon');?></label></th>
<td>
<button type="button" class="button" onclick="argonPreviewEmail('comment');"><?php _e('预览评论通知邮件', 'argon');?></button>
<button type="button" class="button" onclick="argonPreviewEmail('reply');"><?php _e('预览回复通知邮件', 'argon');?></button>
<p class="description"><?php _e('预览当前设置下的邮件效果', 'argon');?></p>
<div id="argon_email_preview_modal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 100000;">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; border-radius: 8px; width: 90%; max-width: 700px; max-height: 90vh; overflow: auto;">
<div style="padding: 16px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;">
<strong><?php _e('邮件预览', 'argon');?></strong>
<button type="button" onclick="document.getElementById('argon_email_preview_modal').style.display='none';" style="border: none; background: none; font-size: 20px; cursor: pointer;">&times;</button>
</div>
<iframe id="argon_email_preview_iframe" style="width: 100%; height: 500px; border: none;"></iframe>
</div>
</div>
<script>
function argonPreviewEmail(type) {
var formData = new FormData();
formData.append('action', 'argon_preview_email');
formData.append('type', type);
formData.append('nonce', '<?php echo wp_create_nonce('argon_preview_email'); ?>');
fetch(ajaxurl, {
method: 'POST',
body: formData
})
.then(function(response) { return response.text(); })
.then(function(html) {
var iframe = document.getElementById('argon_email_preview_iframe');
iframe.srcdoc = html;
document.getElementById('argon_email_preview_modal').style.display = 'block';
});
}
</script>
</td>
</tr>
<tr><th class="subtitle"><h3 id="subsection-friend-links"><?php _e('友情链接', 'argon');?></h3></th></tr>
<tr>
@@ -5164,6 +5272,20 @@ function argon_update_themeoptions(){
argon_update_option('argon_friend_link_hide_no_backlink');
argon_update_option_allow_tags('argon_friend_link_requirements');
// 邮件模板相关配置
argon_update_option('argon_email_theme_color');
argon_update_option('argon_email_logo_url');
argon_update_option('argon_email_blog_name');
argon_update_option('argon_email_footer_text');
// 保存社交链接为数组
$social_links = array(
'twitter' => isset($_POST['argon_email_social_twitter']) ? sanitize_url($_POST['argon_email_social_twitter']) : '',
'github' => isset($_POST['argon_email_social_github']) ? sanitize_url($_POST['argon_email_social_github']) : '',
'weibo' => isset($_POST['argon_email_social_weibo']) ? sanitize_url($_POST['argon_email_social_weibo']) : ''
);
update_option('argon_email_social_links', $social_links);
argon_update_option('argon_hide_footer_author');
argon_update_option('argon_card_radius');

View File

@@ -8,7 +8,7 @@ Author URI: https://solstice23.top/
Description: 轻盈、简洁、美观的 Wordpress 主题
Version: 1.4.0
Version: 1.5.0
License: GNU General Public License v3.0