这个功能的核心在于记录和判断,下面我将为您提供两种主流的实现方案:

(图片来源网络,侵删)
- 基于Cookie的方案:简单易行,无需修改数据库,但用户清除Cookie后失效,且多设备间不互通。
- 基于数据库的方案:更可靠,用户登录后状态持久化,但需要修改数据库结构和编写更复杂的逻辑。
基于Cookie的实现(推荐新手)
这个方案的核心思想是:当用户阅读一篇文章时,就在他的浏览器里设置一个Cookie,记录下这篇文章的ID,下次再加载页面时,就去检查这个Cookie,如果文章ID在Cookie里,就显示“已读”标记。
步骤详解:
修改文章列表模板文件
你需要找到你网站的文章列表模板,通常是 /templets/default/list_*.htm 或者你自定义的列表模板。
在 {dede:list} 标签循环中,找到文章标题的链接 <a> 标签,在其附近添加判断逻辑。

(图片来源网络,侵删)
{dede:list}
<li>
<!-- 原有的文章标题链接 -->
<a href="[field:arcurl/]">[field:title/]</a>
<!-- ====== 在这里添加以下代码 ====== -->
<span class="read-status">
{dede:php}
// 获取当前文章ID
$aid = $GLOBALS['arcid'];
// 从Cookie中获取已读文章ID列表(假设Cookie名为 'dede_read_articles')
$read_articles = isset($_COOKIE['dede_read_articles']) ? $_COOKIE['dede_read_articles'] : '';
// 将Cookie字符串转换成数组
$read_ids = $read_articles ? explode(',', $read_articles) : array();
// 判断当前文章ID是否在已读数组中
if (in_array($aid, $read_ids)) {
// 如果已读,显示“已读”标记
echo '<span class="read-tag">已读</span>';
}
{/dede:php}
</span>
<!-- ================================= -->
<!-- 其他信息,如时间、点击等 -->
<span class="info">[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:list}
修改文章内容页模板文件
当用户在文章内容页时,也需要记录他刚刚阅读了这篇文章,所以要在文章内容页模板(通常是 /templets/default/article_article.htm)的底部,添加设置Cookie的代码。
... 文章内容 ...
<!-- 在页面底部,</body> 标签之前添加以下代码 -->
<script>
// 获取当前文章ID
var articleId = "{dede:field.id/}";
// 获取或创建已读文章ID的Cookie
var readArticles = getCookie('dede_read_articles');
// 如果Cookie不存在,则初始化为一个空字符串
if (!readArticles) {
readArticles = '';
}
// 检查当前文章ID是否已在Cookie中
if (readArticles.indexOf(articleId) === -1) {
// 如果不在,则将当前文章ID添加到Cookie字符串中
readArticles += (readArticles ? ',' : '') + articleId;
// 设置Cookie,有效期设为30天
setCookie('dede_read_articles', readArticles, 30);
}
// 设置Cookie的函数
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
// 获取Cookie的函数(如果需要的话,但上面代码可以直接用document.cookie)
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
</script>
</body>
</html>
添加CSS样式(可选)
为了让“已读”标记更美观,你可以在模板的CSS文件或<style>标签中添加一些样式。
<!-- 在你的CSS文件或模板的 <head> 标签内添加 -->
<style>
.read-status {
margin-left: 10px;
font-size: 12px;
color: #999;
}
.read-tag {
color: #ccc;
text-decoration: line-through; /* 可以加个删除线效果 */
}
/* 或者用一个图标代替文字 */
.read-tag::before {
content: "👁"; /* 眼睛图标 */
margin-right: 3px;
}
</style>
基于数据库的实现(更专业)
这个方案要求用户登录,已读状态会保存在数据库的用户表中,更稳定。
步骤详解:
修改数据库
在 dede_member 表(或其他会员表)中增加一个字段来存储已读文章ID。
ALTER TABLE `dede_member` ADD COLUMN `readarticleids` TEXT NOT NULL DEFAULT '';
readarticleids字段类型为TEXT,因为ID可能会很长。- 默认值为空字符串 。
修改文章列表模板
逻辑和方案一类似,但数据来源变成了数据库。
{dede:list}
<li>
<a href="[field:arcurl/]">[field:title/]</a>
<!-- ====== 在这里添加以下代码 ====== -->
<span class="read-status">
{dede:php}
global $dsql;
$aid = $GLOBALS['arcid'];
// 获取当前登录用户的信息
$uid = isset($cfg_ml->M_ID) ? $cfg_ml->M_ID : 0;
if ($uid > 0) {
// 查询用户的已读文章ID列表
$row = $dsql->GetOne("SELECT `readarticleids` FROM `dede_member` WHERE `mid` = '$uid'");
if ($row) {
$read_ids = $row['readarticleids'] ? explode(',', $row['readarticleids']) : array();
if (in_array($aid, $read_ids)) {
echo '<span class="read-tag">已读</span>';
}
}
}
{/dede:php}
</span>
<!-- ================================= -->
<span class="info">[field:pubdate function="MyDate('Y-m-d',@me)"/]</span>
</li>
{/dede:list}
修改文章内容页模板
当用户阅读文章时,更新其 readarticleids 字段。
... 文章内容 ...
<!-- 在页面底部,</body> 标签之前添加以下代码 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取当前文章ID
var articleId = "{dede:field.id/}";
// 获取当前登录用户ID
var userId = <?php echo isset($cfg_ml->M_ID) ? $cfg_ml->M_ID : 0; ?>;
if (userId > 0) {
// 发送AJAX请求到后端更新数据库
var xhr = new XMLHttpRequest();
xhr.open('POST', '/plus/read_record.php', true); // 我们需要创建这个文件
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('aid=' + articleId + '&uid=' + userId);
}
});
</script>
</body>
</html>
创建处理记录的PHP文件
在网站根目录的 /plus/ 文件夹下,创建一个名为 read_record.php 的文件,内容如下:
<?php
require_once(dirname(__FILE__)."/../include/common.inc.php");
require_once(DEDEINC."/memberlogin.class.php");
// 只接受POST请求
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
exit('Error: Request method not allowed.');
}
// 获取参数
$aid = isset($_POST['aid']) ? intval($_POST['aid']) : 0;
$uid = isset($_POST['uid']) ? intval($_POST['uid']) : 0;
if ($aid <= 0 || $uid <= 0) {
exit('Error: Invalid parameters.');
}
// 判断用户是否登录
if ($cfg_ml->M_ID != $uid) {
exit('Error: User not logged in or ID mismatch.');
}
// 获取用户的已读文章ID列表
$read_ids = isset($cfg_ml->M_Infos['readarticleids']) ? $cfg_ml->M_Infos['readarticleids'] : '';
$read_ids_array = $read_ids ? explode(',', $read_ids) : array();
// 如果当前文章ID不在列表中,则添加
if (!in_array($aid, $read_ids_array)) {
$read_ids_array[] = $aid;
// 将数组重新组合成字符串
$new_read_ids = implode(',', $read_ids_array);
// 更新数据库
$sql = "UPDATE `dede_member` SET `readarticleids` = '$new_read_ids' WHERE `mid` = '$uid'";
$dsql->ExecuteNoneQuery($sql);
}
echo 'Record updated successfully.';
?>
总结与建议
| 特性 | 方案一 (Cookie) | 方案二 (数据库) |
|---|---|---|
| 实现难度 | 简单 | 较复杂 |
| 用户体验 | 非登录用户也可用,但多设备状态不互通 | 需登录,状态永久保存,体验一致 |
| 数据可靠性 | 低,用户清除Cookie即失效 | 高,数据存储在服务器 |
| 适用场景 | 普通资讯类、博客类网站 | 需要用户登录的门户、社区、学习平台 |
对于大多数网站,方案一(Cookie)已经足够好用且易于实现,如果你的网站有严格的会员体系和用户数据追踪需求,那么方案二(数据库)是更可靠的选择。
希望这个详细的教程能帮助到你!
