我的网站有个“节日”分类,发布的是各种纪念日介绍文章,即x月x日是什么日,这类文章发布后变动的不多,但每年同一日期都有用。
为了增加节日文章的曝光率,我之前是在网站侧边栏添加了一个“本月纪念日”的文章聚合,但每月要手动指定拟显示的文章,有点麻烦。我想能不能显示当天的节日文章,比如今天是12月4日,就显示“12月4日,国家宪法日”,有多个节日的就显示多个卡片,这样就省去了手动添加的麻烦,而且显示的是当天的节日,对访客更加友好。
我把这个需求提交给了 DeepSeek ,并且经过几轮的调整,实现了这个功能:显示节日文章的标题、缩略图和超链接,并有日期状态提示。

在设计时,考虑了缩略图的显示问题,文章设有特色图像的,显示特色图像,否则会自动提取内容中的第一张图;如果文章没有配图,则显示默认缩略图,确保有张图片,让卡片更饱满。
而如果当天没有节日,则显示最近的未来节日,确保小工具不会空白。日期状态提示,有两种:今天节日:显示"🎯 就是今天"(有呼吸动画效果),未来节日:显示"📅 还有X天"(精确计算天数),这样也可提醒访客正常的节日信息。
这个功能以小工具的形式实现,简洁实用且无需任何配置,只需在后台-外观-小工具中,把这个小工具拖到合适的侧边栏位置即可。
1、小工具完整代码
// 节日文章小工具 - 带标题和状态提示版
class Festival_Complete_Widget extends WP_Widget {public function __construct() {
parent::__construct(
'festival_complete_widget',
__('今日节日', 'text_domain'),
array('description' => __('显示节日文章,包含日期状态提示', 'text_domain'))
);
}// 前端显示
public function widget($args, $instance) {
// 使用固定标题
$title = '今日节日';echo $args['before_widget'];
// 显示小工具标题
if (!empty($title)) {
echo $args['before_title'] . $title . $args['after_title'];
}// 获取匹配的节日文章
$festival_posts = $this->get_festival_posts_by_title_date(5);if (!empty($festival_posts)) {
echo '<div class="festival-complete-container">';foreach ($festival_posts as $post) {
$this->display_festival_post($post);
}echo '</div>';
} else {
echo '<div class="no-festival-message">';
echo '<p>暂无节日文章</p>';
echo '</div>';
}echo $args['after_widget'];
}// 后台表单
public function form($instance) {
echo '<p>今日节日小工具</p>';
echo '<p><strong>显示规则:</strong></p>';
echo '<ul>';
echo '<li>标题:今日节日</li>';
echo '<li>显示5篇文章</li>';
echo '<li>分类:jieri</li>';
echo '<li>匹配:标题包含"x月x日"格式</li>';
echo '</ul>';
}// 保存设置
public function update($new_instance, $old_instance) {
return $old_instance;
}// 核心函数:获取节日文章
private function get_festival_posts_by_title_date($max_posts = 5) {
global $wpdb;// 获取今天的月日
$current_month = date('n');
$current_day = date('j');// 构建日期匹配模式
$date_patterns = array();
$date_patterns[] = $current_month . '月' . $current_day . '日';if ($current_month < 10) {
$date_patterns[] = '0' . $current_month . '月' . $current_day . '日';
}
if ($current_day < 10) {
$date_patterns[] = $current_month . '月' . '0' . $current_day . '日';
}
if ($current_month < 10 && $current_day < 10) {
$date_patterns[] = '0' . $current_month . '月' . '0' . $current_day . '日';
}// 获取jieri分类ID
$category = get_category_by_slug('jieri');
if (!$category) {
return array();
}$category_id = $category->term_id;
// 构建SQL查询
$like_clauses = array();
foreach ($date_patterns as $pattern) {
$like_clauses[] = $wpdb->prepare("post_title LIKE %s", '%' . $wpdb->esc_like($pattern) . '%');
}$like_sql = implode(' OR ', $like_clauses);
$sql = $wpdb->prepare("
SELECT p.*
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
AND tr.term_taxonomy_id = %d
AND ({$like_sql})
ORDER BY p.post_date DESC
LIMIT %d
", $category_id, $max_posts);$today_posts = $wpdb->get_results($sql);
// 如果找到当天日期的文章,直接返回
if (!empty($today_posts)) {
return $today_posts;
}// 如果没有当天日期的文章,查找最近的未来文章
return $this->get_nearest_future_posts($max_posts);
}// 获取未来的最近节日文章
private function get_nearest_future_posts($max_posts = 5) {
global $wpdb;$current_month = date('n');
$current_day = date('j');$category = get_category_by_slug('jieri');
if (!$category) {
return array();
}$category_id = $category->term_id;
// 获取所有jieri分类的文章
$sql = $wpdb->prepare("
SELECT p.*
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
AND tr.term_taxonomy_id = %d
ORDER BY p.post_date DESC
LIMIT 100
", $category_id);$all_posts = $wpdb->get_results($sql);
if (empty($all_posts)) {
return array();
}// 提取日期并计算距离
$posts_with_dates = array();foreach ($all_posts as $post) {
if (preg_match('/(\d{1,2})月(\d{1,2})日/', $post->post_title, $matches)) {
$post_month = intval($matches[1]);
$post_day = intval($matches[2]);$current_date_value = $current_month * 100 + $current_day;
$post_date_value = $post_month * 100 + $post_day;$distance = $post_date_value - $current_date_value;
if ($distance < 0) {
$distance += 1200; // 加12个月
}$posts_with_dates[] = array(
'post' => $post,
'distance' => $distance,
'month' => $post_month,
'day' => $post_day
);
}
}// 按距离排序
usort($posts_with_dates, function($a, $b) {
return $a['distance'] - $b['distance'];
});// 取前N个
$nearest_posts = array_slice($posts_with_dates, 0, $max_posts);$result = array();
foreach ($nearest_posts as $item) {
$result[] = $item['post'];
}return $result;
}// 显示单篇节日文章
private function display_festival_post($post) {
// 获取图片
$image_url = $this->get_post_image($post);// 提取标题中的日期
$festival_date = '';
$festival_month = 0;
$festival_day = 0;if (preg_match('/(\d{1,2})月(\d{1,2})日/', $post->post_title, $matches)) {
$festival_date = $matches[1] . '月' . $matches[2] . '日';
$festival_month = intval($matches[1]);
$festival_day = intval($matches[2]);
}
?>
<div class="festival-item">
<div class="festival-image">
<a href="<?php echo get_permalink($post->ID); ?>">
<img src="<?php echo esc_url($image_url); ?>"
alt="<?php echo esc_attr($post->post_title); ?>"
onerror="this.src='https://www.munue.com/wp-content/uploads/2021/03/1616935520-noimage.jpg'">
</a>
</div>
<div class="festival-info">
<h3 class="festival-title">
<a href="<?php echo get_permalink($post->ID); ?>">
<?php echo esc_html($post->post_title); ?>
</a>
</h3><!-- 日期状态提示 -->
<div class="festival-status">
<?php if ($festival_date): ?>
<?php
$current_month = date('n');
$current_day = date('j');if ($festival_month == $current_month && $festival_day == $current_day) {
echo '<span class="status-badge today-badge">🎯 就是今天</span>';
} else {
// 计算距离天数
$current_date = strtotime(date('Y') . '-' . $current_month . '-' . $current_day);
$festival_date_this_year = strtotime(date('Y') . '-' . $festival_month . '-' . $festival_day);// 如果今年节日已过,计算明年的
if ($festival_date_this_year < $current_date) {
$festival_date_this_year = strtotime((date('Y') + 1) . '-' . $festival_month . '-' . $festival_day);
}$days_left = round(($festival_date_this_year - $current_date) / (60 * 60 * 24));
if ($days_left > 0) {
echo '<span class="status-badge upcoming-badge">📅 还有' . $days_left . '天</span>';
} else {
echo '<span class="status-badge upcoming-badge">📅 即将到来</span>';
}
}
?>
<?php endif; ?>
</div>
</div>
</div>
<?php
}// 获取文章图片
private function get_post_image($post) {
// 1. 特色图像
if (has_post_thumbnail($post->ID)) {
$image = wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), 'medium');
if ($image) {
return $image[0];
}
}// 2. 文章内容中的第一张图片
$first_image = $this->get_first_image_from_content($post->post_content);
if ($first_image) {
return $first_image;
}// 3. 默认图片
return 'https://www.munue.com/wp-content/uploads/2021/03/1616935520-noimage.jpg';
}// 从文章内容中提取第一张图片
private function get_first_image_from_content($content) {
if (empty($content)) {
return false;
}preg_match('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $content, $matches);
if (!empty($matches[1])) {
return $matches[1];
}return false;
}
}// 注册小工具
function register_festival_complete_widget() {
register_widget('Festival_Complete_Widget');
}
add_action('widgets_init', 'register_festival_complete_widget');// 添加CSS样式
function festival_complete_widget_styles() {
?>
<style>
.festival-complete-container {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 10px;
}.festival-item {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: all 0.3s ease;
border: 1px solid #eee;
}.festival-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
border-color: #ddd;
}.festival-image {
height: 140px;
overflow: hidden;
}.festival-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}.festival-item:hover .festival-image img {
transform: scale(1.05);
}.festival-info {
padding: 12px;
}.festival-title {
margin: 0 0 8px 0;
font-size: 14px;
line-height: 1.4;
font-weight: 500;
}.festival-title a {
color: #2c3e50;
text-decoration: none;
transition: color 0.2s ease;
}.festival-title a:hover {
color: #3498db;
}.festival-status {
margin-top: 5px;
}.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 4px;
}.today-badge {
background: linear-gradient(135deg, #d4edda, #c3e6cb);
color: #155724;
border: 1px solid #b1dfbb;
animation: pulse 2s infinite;
}.upcoming-badge {
background: linear-gradient(135deg, #d1ecf1, #bee5eb);
color: #0c5460;
border: 1px solid #abdde5;
}.no-festival-message {
background: #f8f9fa;
padding: 15px;
text-align: center;
border-radius: 8px;
color: #666;
font-size: 13px;
margin-top: 10px;
}@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.8; }
100% { opacity: 1; }
}/* 响应式设计 */
@media (max-width: 768px) {
.festival-image {
height: 120px;
}.festival-info {
padding: 10px;
}.festival-title {
font-size: 13px;
}.status-badge {
font-size: 10px;
padding: 3px 6px;
}
}
</style>
<?php
}
add_action('wp_footer', 'festival_complete_widget_styles');
2、根据实际调整
-
“节日”文章分类的别名为 jieri,请修改为自己的别名
-
“节日”文章分类的别名为 jieri,请修改为自己的别名
-
若当天有多个节日,我设定为显示5篇文章,请按真实需求修改
-
代码中添加了 CSS样式,可以根据自己的喜好修改
3. 小工具安装步骤
-
将完整代码复制到主题的
functions.php文件末尾 -
进入后台 → 外观 → 小工具
-
找到 "今日节日" 小工具
-
拖拽到侧边栏区域




还有宪法日。不说不知道哇。