给Typecho增加文章置顶功能

AI摘要:文章介绍了如何在Typecho博客中实现文章置顶功能,避免使用插件。作者首先讨论了在主题设置项中添加文章ID的置顶方法,但认为操作繁琐。随后,作者分享了一个自定义字段的无插件置顶方案,通过修改`functions.php`和`index.php`文件,在后台编辑页面添加置顶选项,并在首页优先显示置顶文章。最后,作者提醒置顶文章会占用每页显示数量,建议合理设置。

在逛博客的时候,发现资深网民孙先生写了一篇:Typecho文章置顶(非插件),看了下实现思路发现是在 functions.php 添加主题设置项,然后写完文章之后,在设置项中把文章的id放进去来实现的置顶。

其实这个就是仁者见仁智者见智了,我觉得还是在管理文章的时候,设置比较合适,毕竟不想找文章的id,然后再去主题设置那里去搞,步骤比较繁琐。

Typecho其实是在发布文章时可以添加自定义字段,通过自定义字段去给文章做一些特殊配置是比较合适的,而且这个字段是跟文章保存的,后面导出或者迁移,这些字段也是很方便的备份出来。

于是网上搜索了一番,找到了一篇使用自定义字段无插件实现typecho文章置顶功能,借助AI的能力,让他根据我的主题调整了一下,于是代码如下:

functions.php 的最上面添加

// 添加置顶选项确认按钮
Typecho_Plugin::factory('admin/write-post.php')->option = 'addStickyCheckbox';

然后在 functions.php 随便找个位置,插入以下代码:

// 添加文章置顶选项到后台编辑页面
function addStickyCheckbox() {
  $post = Typecho_Widget::widget('Widget_Contents_Post_Edit');
  // 默认不勾选置顶,只有在明确设置了isSticky=1时才勾选
  $isChecked = ($post->fields->isSticky == 1) ? 'checked' : '';
  $html = '<section class="typecho-post-option"><label class="typecho-label">文章置顶</label><p><input type="checkbox" id="is_sticky" name="fields[isSticky]" value="1" ' . $isChecked .'> <label for="is_sticky">首页置顶文章</label></p></section>';
  $html .= '<script>
  document.addEventListener("DOMContentLoaded", function() {
    var checkbox = document.getElementById("is_sticky");
    var isStickyCustom = document.getElementById("fieldvalue");
    // 初始化时设置默认值为0(不置顶)
    if (!isStickyCustom.value) {
        isStickyCustom.value = "0";
    }
    checkbox.addEventListener("change", function() {
      if (this.checked) {
          isStickyCustom.value = "1";
      } else {
          isStickyCustom.value = "0";
      }
    });
  });
  </script>';
  echo $html;
}

// 获取所有置顶文章的ID
function getStickyPostIds() {
  $db = Typecho_Db::get();
  $query = $db->select('cid')
    ->from('table.fields')
    ->where('name = ?', 'isSticky')
    ->where('str_value = ?', '1');

  return $db->fetchAll($query, function($row) {
    return (int)$row['cid'];
  });
}

最麻烦的一步来了,打开 index.php,这个是负责首页展示的,

我最开始的代码是:

<div class="col-mb-12 col-8" id="main" role="main">
  <?php while($this->next()): ?>
  <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
    <h2 class="post-title" itemprop="name headline"><a itemprop="url" href="<?php $this->permalink() ?>"><?php $this->title() ?></a></h2>
    <ul class="post-meta">
      <li><time datetime="<?php $this->date('c'); ?>" itemprop="datePublished"><?php $this->date(); ?></time></li>
      <li><?php $this->category(','); ?></li>
      <li itemprop="interactionCount"><a itemprop="discussionUrl" href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('抢沙发', '抢板凳', '%d 条评论'); ?></a></li>
      <li><?php $this->charactersNum(); ?>字</li>
      <li><?php get_post_view($this) ?>次阅读</li>
    </ul>
    <div class="post-content" itemprop="articleBody">
      <?php
        $this->content('- 阅读剩余部分 -');
      ?>
    </div>
  </article>
  <?php endwhile; ?>
  <?php $this->pageNav('&laquo; 前一页', '后一页 &raquo;'); ?>
</div>

然后把这里文章的内容块提取出来,命名为 article.php 调整成这样:

<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <h2 class="post-title" itemprop="name headline">
    <a itemprop="url" href="<?php $post->permalink() ?>"><?php $post->title() ?></a>
    <?php if ($isSticky): ?>
      <span class="sticky-tag">置顶</span>
    <?php endif; ?>
  </h2>
  <ul class="post-meta">
    <li><time datetime="<?php $post->date('c'); ?>" itemprop="datePublished"><?php $post->date(); ?></time></li>
    <li><?php $post->category(','); ?></li>
    <li itemprop="interactionCount"><a itemprop="discussionUrl" href="<?php $post->permalink() ?>#comments"><?php $post->commentsNum('抢沙发', '抢板凳', '%d 条评论'); ?></a></li>
    <li><?php $post->charactersNum(); ?>字</li>
    <li><?php get_post_view($post) ?>次阅读</li>
  </ul>
  <div class="post-content" itemprop="articleBody">
    <?php
      $post->content('- 阅读剩余部分 -');
    ?>
  </div>
</article>

然后修改 index.php 替换成下面的:

<div class="col-mb-12 col-8" id="main" role="main">
  <?php 
    // 获取所有置顶文章的 CID
    $stickyIds = getStickyPostIds();
    $stickyPosts = [];
    $normalPosts = [];

    // 遍历文章,将置顶文章和普通文章分开
    while ($this->next()) {
        if (in_array($this->cid, $stickyIds)) {
            $stickyPosts[] = clone $this; // 置顶文章
        } else {
            $normalPosts[] = clone $this; // 普通文章
        }
    }

    // 优先显示置顶文章
    foreach ($stickyPosts as $post) {
      $isSticky = true;
      include 'article.php';
    }

    // 显示普通文章
    foreach ($normalPosts as $post) {
      $isSticky = false;
      include 'article.php';
    }
  ?>
  <?php $this->pageNav('&laquo; 前一页', '后一页 &raquo;'); ?>
</div>

我把无关的代码都去掉,就能看明白了吧,区分开然后分别循环!

然后再打开文章编辑器,右侧就出现了下图:

2025-10-22T12:50:53.png

这样再发布文章或者编辑文章的时候,就能够自己动手勾选一下就完事了。

不过,这里需要注意的是,置顶文章会占用每页显示文章的数量,例如我每页设置 10 篇,如果置顶了 2 篇,那么非置顶的文章只能显示 8 篇。所以,在置顶的时候要最好不要超过设置的文章数量。

上面的代码如果实在是看不明白的话,可以让AI按照这个例子改就可以,现在AI还是比较厉害的。

目前主题基本上完善了常用的功能,下次把 Typecho 切换暗色和亮色这个功能分享出来,主题折腾的也就告一段落了。

已有 2 条评论

  1. 回复

    我用的也是通过文章ID来实现置顶的插件,ID的话还好,我文章链接用的/{cid}.html,一眼就能看出来ID是多少 ꈍ◡ꈍ

    1. 王叨叨 王叨叨 [作者]
      回复

      看来用文章ID的还是比较多,最近在研究Typecho,发现官方给的自定义字段挺好用的,而且也是推荐使用自定义字段来扩展模板功能,后面准备再深入研究研究看看能把模块弄的个性一点。

添加新评论