代码实现 WordPress 漂亮的标签归档页面

前段时间把 WordPress 文章归档页面、网站分类、文章标签以及导航菜单等进行了优化,但目前在实际使用过程中仍觉得“分类”不够清晰。思来想去,还是觉得用“标签归档”来作为分类导航比较直观。于是 Kevin 着手创建一个新的 WordPress 标签归档页面…嗯,还是觉得原来 Tiny 主题用的标签页好看,扒下来吧。

代码实现 WordPress 漂亮的标签归档页面
代码实现 WordPress 漂亮的标签归档页面

可以打开咱新建的标签归档页面看看效果。如上图所示,WordPress 标签按照英文字母排序(中文字符提取了首字母),右侧是浮动的导航键盘。以下是实现该页面效果的核心程序和样式表:

// 新发表文章/修改文章时清理文章归档、标签归档缓存。此函数必须循环进 func.php,否则不生效
function clear_archives_tags_caches() {
    //update_option('zww_db_cache_archives_list', ''); // 刷新 文章归档页面缓存
    update_option('specs_tags_list', ''); // 清空 specs_tags_list
}

add_action('save_post', 'clear_archives_tags_caches'); 
// 标签归档页面所需函数开始,注释为 AI 生成

// 获取字符串的首个字符
function specs_getfirstchar($s0) {  
    $fchar = ord($s0[0]);
    if ($fchar >= ord("A") and $fchar <= ord("z")) return strtoupper($s0[0]);

    // 尝试进行编码转换
    $s1 = @iconv("UTF-8//IGNORE", "GBK//IGNORE", $s0);
    $s2 = iconv("GBK", "UTF-8", $s1);
    if ($s2 == $s0) {
        $s = $s1;
    } else {
        $s = $s0;
    }
    
    // 根据 ASCII 码判断并返回对应的首个大写英文字母
    $asc = ord($s[0]) * 256 + ord($s[1]) - 65536;
    // 接下来是一系列的中文编码的范围,用于确定首字母
    if ($asc >= -20319 and $asc <= -20284) return "A";
    // ... 省略了其他的判断

    return null; // 如果都不符合上述条件,则返回 null
}

// 将中文转换为拼音首字母
function specs_pinyin($zh) {
    $ret = "";
    // 进行编码转换并获取第一个字符
    $s1 = @iconv("UTF-8//IGNORE", "GBK//IGNORE", $zh);
    $s2 = iconv("GBK", "UTF-8", $s1);
    if ($s2 == $zh) {
        $zh = $s1;
    }

    // 循环处理字符串中的每个字符
    $i = 0;
    while ($i < strlen($zh)) {
        $s1 = substr($zh, $i, 1);
        $p = ord($s1);
        if ($p > 160) {
            // 如果字符 ASCII 码大于 160,则认为是中文字符,并获取对应的首字母
            $s2 = substr($zh, $i++, 2);
            $ret .= specs_getfirstchar($s2);
        } else {
            // 否则,直接返回该字符
            $ret .= $s1;
        }
    }
    return strtoupper($ret); // 全部转为大写并返回
}

// 展示标签的函数
function specs_show_tags() {
    // 尝试从选项中获取缓存的标签列表
    if (!$output = get_option('specs_tags_list')) {
        // 获取所有标签,并按照文章数量排序
        $categories = get_terms('post_tag', array(
            'orderby'    => 'count',
            'hide_empty' => 1
        ));

        $r = array(); // 初始化数组,避免后面使用时未定义
        foreach ($categories as $v) {
            $letter = specs_pinyin($v->name); // 获取标签名的首字母
            if (ctype_alpha($letter)) { // 判断是否是字母
                $r[$letter][] = $v; // 按字母存放到数组中
            }
        }
        if (!empty($r)) {
            ksort($r); // 如果数组不为空,则进行排序
        }

        // 接下来的代码生成了 HTML 输出内容,其中包括所有标签的链接列表
        // ...

        // 将生成的标签列表内容更新到选项中
        update_option('specs_tags_list', $output);
    }
    echo $output; // 输出标签列表
}

// 根据标签获取文章数量的函数
function specs_post_count_by_tag($arg, $type = 'include') {
    $args = array(
        $type => $arg,
    );
    $tags = get_tags($args);
    if ($tags) {
        foreach ($tags as $tag) {
            return $tag->count; // 返回标签下文章的数量
        }
    }
    return 0; // 如果没有找到标签,返回 0
}

// 页面所需函数结束
.layoutSecondary {
    float: right;
    box-sizing: border-box;
    padding-left: 30px;
    width: 260px;
}
#tag-letter, .category-filter {
    position: fixed;
    width: 220px;
}
#tag-letter li {
    display: inline-block;
}
#tag-letter li a {
    display: inline-block;
    margin: 3px 5px;
    width: 24px;
    height: 32px;
    border: 1px solid #ddd;
    border-radius: 3px;
    background: #fff;
    text-align: center;
    font-size: 16px;
    line-height: 32px;
}
#tag-letter li a.none {
    color: #ddd;
    cursor: not-allowed;
}

#all-tags li {
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px dashed #eee;
    max-width: 70%;
}
#all-tags .tag-name {
    font-style: italic;
    font-size: 28px;
}
#all-tags .tag-list {
    margin-left: 24px;
}
#all-tags .tag-list a {
    display: inline-block;
    margin-right: 20px;
}
#all-tags .number {
    display: inline-block;
    margin-left: 3px;
    font-size: 12px;
    -webkit-transform: translateY(-10px);
    transform: translateY(-10px);
}

#all-tags .tag-list a:hover .number {
    font-size: 14px;
}

.layoutPrimary ul{list-style: none;}

将以上主函数循环仅主题function.php中,将以上样式表添加进主题 CSS 中,然后复制一个主题的页面模板并修改Template Name为自定义页面名称,接着在合适的位置插入<?php specs_show_tags(); ?>,最后新建页面并且使用这个模板即可。为了省事儿和减少开销,我将以上除清除缓存部分的代码直接添加进了页面模板文件中,以下是我完整的 PHP 文件,给您各位作个参考:

<?php
/*
Template Name: Tags Page
*
* 	@version	1.0
* 	@author		牧羊人
* 	@URI		https://www.shephe.com/
*/

get_header();
the_post();
impeka_grve_print_header_title( 'page' );
impeka_grve_print_header_breadcrumbs( 'page' );
impeka_grve_print_anchor_menu( 'page' );

// 页面所需函数开始
function specs_getfirstchar($s0){  
    $fchar = ord($s0[0]);
    if($fchar >= ord("A") and $fchar <= ord("z") )return strtoupper($s0[0]);
    //$s1 = iconv("UTF-8","gb2312", $s0);
    //$s2 = iconv("gb2312","UTF-8", $s1);
    $s1 = @iconv("UTF-8//IGNORE","GBK//IGNORE", $s0);
    $s2 = iconv("GBK","UTF-8", $s1);
    if($s2 == $s0){$s = $s1;}else{$s = $s0;}
    $asc = ord($s[0]) * 256 + ord($s[1]) - 65536;
    if($asc >= -20319 and $asc <= -20284) return "A";
    if($asc >= -20283 and $asc <= -19776) return "B";
    if($asc >= -19775 and $asc <= -19219) return "C";
    if($asc >= -19218 and $asc <= -18711) return "D";
    if($asc >= -18710 and $asc <= -18527) return "E";
    if($asc >= -18526 and $asc <= -18240) return "F";
    if($asc >= -18239 and $asc <= -17923) return "G";
    if($asc >= -17922 and $asc <= -17418) return "H";
    if($asc >= -17417 and $asc <= -16475) return "J";
    if($asc >= -16474 and $asc <= -16213) return "K";
    if($asc >= -16212 and $asc <= -15641) return "L";
    if($asc >= -15640 and $asc <= -15166) return "M";
    if($asc >= -15165 and $asc <= -14923) return "N";
    if($asc >= -14922 and $asc <= -14915) return "O";
    if($asc >= -14914 and $asc <= -14631) return "P";
    if($asc >= -14630 and $asc <= -14150) return "Q";
    if($asc >= -14149 and $asc <= -14091) return "R";
    if($asc >= -14090 and $asc <= -13319) return "S";
    if($asc >= -13318 and $asc <= -12839) return "T";
    if($asc >= -12838 and $asc <= -12557) return "W";
    if($asc >= -12556 and $asc <= -11848) return "X";
    if($asc >= -11847 and $asc <= -11056) return "Y";
    if($asc >= -11055 and $asc <= -10247) return "Z";
    return null;
}
function specs_pinyin($zh){
    $ret = "";
    //$s1 = iconv("UTF-8","gb2312", $zh);
    //$s2 = iconv("gb2312","UTF-8", $s1);
    $s1 = @iconv("UTF-8//IGNORE","GBK//IGNORE", $zh);
    $s2 = iconv("GBK","UTF-8", $s1);
    if($s2 == $zh){$zh = $s1;}
    $i = 0;
    $s1 = substr($zh,$i,1);
    $p = ord($s1);
    if($p > 160){
        $s2 = substr($zh,$i++,2);
        $ret .= specs_getfirstchar($s2);
    }else{
        $ret .= $s1;
    }
    return strtoupper($ret);
}

function specs_show_tags() {
    if(!$output = get_option('specs_tags_list')){
        $categories = get_terms('post_tag', array(
            'orderby'    => 'count',
            'hide_empty' => 1
        ));

        $r = array(); // 初始化数组,避免后面使用时未定义
        foreach ($categories as $v){
            for ($i = 65; $i <= 90; $i++){
                if (specs_pinyin($v->name) == chr($i)){
                    $r[chr($i)][] = $v;
                }
            }
            for ($i = 48; $i <= 57; $i++){
                if (specs_pinyin($v->name) == chr($i)){
                    $r[chr($i)][] = $v;
                }
            }
        }
        
        if (!empty($r)) {
            ksort($r); // 排序前确保 $r 不为空
        }

        $output = "<div class='layoutSecondary fontJH'><ul id='tag-letter'>";
        for ($i = 65; $i <= 90; $i++){
            if (isset($r[chr($i)])){
                $tagi = $r[chr($i)];
                $output .= "<li><a href='#".chr($i)."'>".chr($i)."</a></li>";
            }else{
                $output .= "<li><a class='none' href='javascript:;'>".chr($i)."</a></li>";
            }
        }
        for ($i = 48; $i <= 57; $i++){
            if (isset($r[chr($i)])){
                $tagi = $r[chr($i)];
                $output .= "<li><a href='#".chr($i)."'>".chr($i)."</a></li>";
            }else{
                $output .= "<li><a class='none' href='javascript:;'>".chr($i)."</a></li>";
            }
        }
        $output .= "</ul></div>";
        $output .= "<div class='layoutPrimary'><ul id='all-tags'>";
        for ($i = 65; $i <= 90; $i++){
            if (isset($r[chr($i)])){
                $tagi = $r[chr($i)];
                $output .= "<li id='".chr($i)."'><h4 class='tag-name'>".chr($i)."</h4><div class='tag-list clearfix'>";
                foreach ($tagi as $tag){
                    $output .= "<a href='".get_tag_link($tag->term_id)."' target='_blank'>".$tag->name."<span class='number'>".specs_post_count_by_tag($tag->term_id)."</span></a>";
                }
                $output .= '</div></li>';
            }
        }
        for ($i = 48; $i <= 57; $i++){
            if (isset($r[chr($i)])){
                $tagi = $r[chr($i)];
                $output .= "<li id='".chr($i)."'><h4 class='tag-name'>".chr($i)."</h4><div class='tag-list clearfix'>";
                foreach ($tagi as $tag){
                    $output .= "<a href='".get_tag_link($tag->term_id)."' target='_blank'>".$tag->name."<span class='number'>".specs_post_count_by_tag($tag->term_id)."</span></a>";
                }
                $output .= '</div></li>';
            }
        }
        $output .= "</ul></div>";
        update_option('specs_tags_list', $output);
    }
    echo $output;
}

function specs_post_count_by_tag ( $arg ,$type = 'include'){
    $args=array(
        $type => $arg,
    );
    $tags = get_tags($args);
    if ($tags) {
        foreach ($tags as $tag) {
            return $tag->count;
        }
    }
}

// 页面所需函数结束


if ( 'yes' == impeka_grve_post_meta( '_impeka_grve_disable_content' ) ) {
	get_footer();
} else {
?>
<style>

.layoutSecondary {
    float: right;
    box-sizing: border-box;
    padding-left: 30px;
    width: 260px;
}
#tag-letter, .category-filter {
    position: fixed;
    width: 220px;
}
#tag-letter li {
    display: inline-block;
}
#tag-letter li a {
    display: inline-block;
    margin: 3px 5px;
    width: 24px;
    height: 32px;
    border: 1px solid #ddd;
    border-radius: 3px;
    background: #fff;
    text-align: center;
    font-size: 16px;
    line-height: 32px;
}
#tag-letter li a.none {
    color: #ddd;
    cursor: not-allowed;
}

#all-tags li {
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px dashed #eee;
    max-width: 70%;
}
#all-tags .tag-name {
    font-style: italic;
    font-size: 28px;
}
#all-tags .tag-list {
    margin-left: 24px;
}
#all-tags .tag-list a {
    display: inline-block;
    margin-right: 20px;
}
#all-tags .number {
    display: inline-block;
    margin-left: 3px;
    font-size: 12px;
    -webkit-transform: translateY(-10px);
    transform: translateY(-10px);
}

#all-tags .tag-list a:hover .number {
    font-size: 14px;
}

.layoutPrimary ul{list-style: none;}
</style>
<div class="grve-single-wrapper">
	<div id="grve-content" <?php impeka_grve_content_class(); ?>>
		<div class="grve-content-wrapper">
			<div id="grve-main-content">
				<div class="grve-main-content-wrapper clearfix">
					<div id="page-<?php the_ID(); ?>" <?php post_class(); ?>>
						<?php the_content(); ?>
						<!-- 自定义开始 -->
						<div class="grve-container">
						<?php specs_show_tags(); ?>
						</div>
						<!-- 自定义结束 -->
						<?php wp_link_pages(); ?>
					</div>
				</div>
			</div>
			<?php get_sidebar(); ?>
		</div>
	</div>
	<?php
		/**
		 * impeka_grve_singular_after_content hook
		 *
		 * @hooked impeka_grve_print_singular_navigation - 10
		 * @hooked impeka_grve_print_singular_comments_section - 20
		 */
		do_action( 'impeka_grve_singular_after_content' );
	?>	
</div>
<?php get_footer();
}

//Omit closing PHP tag to avoid accidental whitespace output errors.
1200 600 Kevin's
「代码实现 WordPress 漂亮的标签归档页面」有 25 条评论
  • 手机游戏网
    06/05/2024 at 11:10 回复

    收藏一下,过天折腾下看看

  • 王云子
    05/08/2024 at 21:21 回复

    soooo pretty !!!非常nice

    • 的头像
      Kevin
      05/11/2024 at 10:46 回复

      确实是因为评论不允许全英文,这个办法简单直接的防垃圾评论。。。。

  • 的头像
    威言威语
    05/08/2024 at 17:15 回复

    样式不错,配合右侧的字母快捷按钮,用起来很方便。
    火狐浏览器下面的, 快捷按钮区域位置展示有地那问题,掉下面去了。

    • 的头像
      Kevin
      05/11/2024 at 10:45 回复

      我在两台电脑的火狐上试了,似乎没有出错哎?

      • 的头像
        威言威语
        05/11/2024 at 17:13 回复

        最下面的4,5,6,7,8,9的一半度看不到了,和谷歌浏览器不一样,在页面底部偏下了。

  • 大峰
    05/08/2024 at 10:40 回复

    厉害厉害!

  • 的头像
    段先森
    05/07/2024 at 15:28 回复

    看汝博客,品WordPress

    • 的头像
      Kevin
      05/11/2024 at 10:44 回复

      我看你最近也在折腾WordPress嘛,我刚看了一眼,
      1、你的首页字体加载的太多了,好像有七八个额外字体,应该是用不了这么多的,改改
      2、其他评论的头像给懒加载吧,没必要出现在第一屏
      3、合并js/css
      4、搞个cdn?再不济把一些公共的js使用公共库的cdn也行

  • Lvtu
    05/07/2024 at 10:57 回复

    发现你最近折腾精神很足。点贊点赞。。。
    现在折腾WP的人貌似越来越少了,网上的教程好多都是几年前的!
    这个方法我貌似试过,最后因为那个“字母导航”感觉怎么放都不好看,干脆删除了,然后就折腾到我现在用的那种了。。。

    • 的头像
      Kevin
      05/11/2024 at 10:41 回复

      我专门又打开找了一下,没找到标签页面。。。我觉得你的分类是不是有点儿多了 tt

      • Lvtu
        05/11/2024 at 10:48 回复

        我的侧栏标签标题右上角那里有个图标,点击一下就可以看到标签页了,都是单独的page页,不算是分类,哈哈~~~

        • 的头像
          Kevin
          05/11/2024 at 11:53 回复

          你不会说的这个页面吧  https://www.wanghao.me/author/tags.html

          • Lvtu
            05/11/2024 at 15:41 回复

            这个页面:https://www.wanghao.me/tags.html 哈哈~~~

            • 的头像
              Kevin
              05/11/2024 at 16:43 回复

              按数量排序,也是一种办法~

  • 1900
    05/07/2024 at 09:27 回复

    很漂亮!

  • 格子老师
    05/07/2024 at 08:59 回复

    干得漂亮

  • 的头像
    obaby
    05/07/2024 at 08:58 回复

    这个东西高级啊

    • 的头像
      Kevin
      05/11/2024 at 10:39 回复

      跟姐姐你弄那些这都不入流啊

      • 的头像
        obaby
        05/11/2024 at 21:10 回复

        那天看了下你的nginx缓存的那篇文章,发现要改好多东西。放弃了

        • 的头像
          Kevin
          05/12/2024 at 08:58 回复

          我把它写复杂了,以避免一些其外的情况(对小白),实际要改的地方就三处

  • xige
    05/06/2024 at 21:56 回复

    有技术就是好呀,想怎么改就怎么改,舒服

    • 的头像
      Kevin
      05/11/2024 at 10:38 回复

      真没有,我都不太会这些,现在全靠AI

  • S
    05/06/2024 at 20:57 回复

    不错,我那个也是这么做的,不过有点问题,生僻字识别有误,比如“皤滩”、“黟县”。

    • 的头像
      Kevin
      05/11/2024 at 10:38 回复

      嗯,这些字已经超出常规库了,我也用不到啊,所以就这么着吧~

发表评论

请输入关键词…