0 前言

我在Typecho默认主题的基础上做了一款自己的主题,但Typecho自带的评论区结构不利于CSS样式设计,因此自己重构了结构,在此记录下。主要参考了typecho官方教程《自定义评论列表区域》。

1 评论区结构设计与实现

  • 首先是设计好单条评论的HTML代码结构,如
<li id="comment-7" class="comment-body comment-parent comment-odd">
    <div class="comment-header">
        <img class="avatar" loading="lazy" src="avatar.png">
        <div class="comment-info">
            <div>
                <span class="username"><a href=评论者主页 rel="external nofollow">评论者名字</a></span>
            </div>
            <div class="comment-meta">
                <span class="comment-time">评论时间</span>
                <span class="comment-source">系统名称</span>
                <span class="comment-source">浏览器</span>
            </div>
        </div>
    </div>
    <div class="comment-content">评论内容</div>

    <div class="comment-children">
    		<!-- 嵌套评论相关 -->
    </div>
</li>
  • 然后根据设计好的结构,重构threadedComments函数,函数中<li></li>标签内部的内容为使用typecho系统中的评论变量替换HTML中相关属性后的结果,如下
<?php function threadedComments($comments, $options) {
    $commentClass = '';
    if ($comments->authorId) {
        $commentClass = ($comments->authorId == $comments->ownerId) ? ' comment-by-author' : ' comment-by-user';
    }
    $info = parseClientInfo($comments->agent);

    global $parent_author;
    if ($comments->parent == 0) {
        $parent_author = '';
    }
    ?>
<li id="<?php $comments->theId(); ?>" class="comment-body<?php
        if ($comments->levels > 0) {
            echo ' comment-child';
            $comments->levelsAlt(' comment-level-odd', ' comment-level-even');
        } else {
            echo ' comment-parent';
        }
        $comments->alt(' comment-odd', ' comment-even');
        echo $commentClass;
    ?>">

    <div class="comment-header">
        <img class="avatar" loading="lazy" src="<?php echo customGravatar($comments->mail, 100); ?>">
        <div class="comment-info">
            <div>
                <span class="username"><?php $comments->author(); ?></span>
                <?php if ($comments->authorId == $comments->ownerId): ?><span class="is-author">作者</span><?php endif; ?>
            </div>
            <div class="comment-meta">
                <span class="comment-time"><?php $comments->date(); ?></span>
                <span class="comment-source">
                    <i class="<?php echo $info['system']['icon']; ?>"></i> <?php echo $info['system']['name']; ?>
                </span>
                <span class="comment-source">
                    <i class="<?php echo $info['browser']['icon']; ?>"></i> <?php echo $info['browser']['name']; ?>
                </span>
            </div>
        </div>
    </div>

    <div class="comment-content">
        <?php if($parent_author): ?>
            <span class="username"><?php echo $parent_author; ?></span>
        <?php endif; ?>
        <?php $comments->content(); ?>
    </div>

    <div class="comment-reply"><i class="fas fa-reply"></i> <?php $comments->reply(); ?></div>

    <?php if ($comments->children): ?>
        <?php $parent_author = '@' . htmlspecialchars($comments->author); ?>
        <div class="comment-children"><?php $comments->threadedComments($options); ?></div>
    <?php endif; ?>
</li>
<?php } ?>
  • 因为是重写了typecho的内部函数,所以可以直接使用原有的调用方式,即
<?php $comments->listComments(); ?>

2 关键功能

2.1 子评论中添加@父评论作者

采用全局变量$parent_author传递父评论作者信息。若当前评论层级为顶级,则设为空,即不存在更高一级的评论;当存在子评论时,将该参数赋值为'@' . htmlspecialchars($comments->author),即@当前评论作者,这样嵌套循环至子评论时,就是@父评论作者。关键代码为以下几行

global $parent_author;
if ($comments->parent == 0) {
        $parent_author = '';
    }
......
<?php if($parent_author): ?>
    <span class="username"><?php echo $parent_author; ?></span>
<?php endif; ?>
......
<?php if ($comments->children): ?>
    <?php $parent_author = '@' . htmlspecialchars($comments->author); ?>
    <div class="comment-children"><?php $comments->threadedComments($options); ?></div>
<?php endif; ?>

2.2 文章作者评论时添加作者标识

利用评论作者IDauthorId与文章所有者IDownerId是否相同来判断,关键代码如下

<?php if ($comments->authorId == $comments->ownerId): ?><span class="is-author">作者</span><?php endif; ?>

2.3 更多的评论者信息

显示评论者的操作系统、浏览器名称,评论数据库中的agent字段记录了评论者的user-agent,通过解析其中的字段获取相关信息,此功能需要自行编写一个解析函数parseClientInfo($comments->agent),关键代码如下

$info = parseClientInfo($comments->agent);
......
<span class="comment-source">
    <i class="<?php echo $info['system']['icon']; ?>"></i> <?php echo $info['system']['name']; ?>
</span>
<span class="comment-source">
    <i class="<?php echo $info['browser']['icon']; ?>"></i> <?php echo $info['browser']['name']; ?>
</span>

解析函数parseClientInfo($comments->agent)可以使用AI直接生成,我用腾讯元宝生成函数源码如下。其中我额外采用了font-awesome 6.7.2版本用来显示各种操作系统图标。

<?php 
/**
 * 获取客户端系统、浏览器信息
 */
function parseClientInfo($userAgent) {
    $userAgent = (string) $userAgent;

    $systemDefault = ['name' => 'Unknown', 'icon' => 'fas fa-question-circle'];
    $browserDefault = ['name' => 'Unknown', 'icon' => 'fas fa-globe'];

    $systems = [
        '/Windows NT 10\\.0/i' => ['Windows 10', 'fab fa-windows'],
        '/Windows NT 6\\.3/i' => ['Windows 8.1', 'fab fa-windows'],
        '/Windows NT 6\\.2/i' => ['Windows 8', 'fab fa-windows'],
        '/Windows NT 6\\.1/i' => ['Windows 7', 'fab fa-windows'],
        '/Windows NT 6\\.0/i' => ['Windows Vista', 'fab fa-windows'],
        '/Windows NT 5\\.1/i' => ['Windows XP', 'fab fa-windows'],
        '/Ubuntu/i' => ['Ubuntu', 'fab fa-ubuntu'],
        '/Macintosh; Intel Mac OS X/i' => ['Mac OS', 'fab fa-apple'],
        '/iPad/i' => ['iPad', 'fas fa-apple'],
        '/Android/i' => ['Android', 'fas fa-mobile-alt'],
        '/iPhone OS/i' => ['IOS', 'fas fa-mobile-alt'],
        '/Linux/i' => ['Linux', 'fab fa-linux'],
        '/CrOS/i' => ['Unknown', 'fas fa-question-circle']
    ];

    $browsers = [
        '/Edg/i' => ['Edge', 'fab fa-edge'],
        '/OPR/i' => ['Opera', 'fab fa-opera'],
        '/SamsungBrowser/i' => ['Unknown', 'fas fa-globe'],
        '/QQBrowser/i' => ['Unknown', 'fas fa-globe'],
        '/WeChat/i' => ['Unknown', 'fas fa-globe'],
        '/Chrome/i' => ['Chrome', 'fab fa-chrome'],
        '/Firefox/i' => ['Firefox', 'fab fa-firefox'],
        '/Safari/i' => ['Safari', 'fab fa-safari'],
        '/Trident/i' => ['IE', 'fab fa-internet-explorer']
    ];

    $systemResult = $systemDefault;
    foreach ($systems as $pattern => $info) {
        if (preg_match($pattern, $userAgent)) {
            $systemResult = ['name' => $info[0], 'icon' => $info[1]];
            break;
        }
    }

    $browserResult = $browserDefault;
    foreach ($browsers as $pattern => $info) {
        if (preg_match($pattern, $userAgent)) {
            $browserResult = ['name' => $info[0], 'icon' => $info[1]];
            break;
        }
    }

    return ['system' => $systemResult, 'browser' => $browserResult];
<?php } ?>

2.4 自定义评论时间样式

默认的评论时间样式很丑,我更习惯年-月-日 时:分的显示样式,设置方法为进入typecho网站后台->设置->评论,设置评论日期格式

Y/m/d H:i

其它样式参考PHP 日期格式写法

3 评论区数据库字段

typecho官方博客《Typecho数据库设计》详细介绍了评论区的数据库结构,倘若想添加其它功能,如显示评论者IP属地,可通过解析数据库中的ip字段获取。我暂时没有做这一步,有点麻烦。

4 后记

在AI如此成熟的时代,网站设计技术已经变得十分容易,就像我可以完全不懂PHP语言,但使用AI几分钟就可以实现一个user-agent的解析函数。当然,这需要能够给AI明确的表达出自己的想法,需求越具体、描述的越清楚,AI实现的效果就会越好。在我看来,未来可能更侧重于"我想做什么"、"我想使用什么实现什么"等,在相对较为成熟的领域内,有需求和想法可能要大于能够实现的能力本身。