今天给安知鱼主题的文章添加一个点赞按钮,主要是用来为文章点赞,让浏览者参与文章的评优,很多网站程序都有顶踩功能,算是添加一个小功能。
1.必须的JavaScript代码 点赞按钮的 JavaScript 样式,需要在 /themes/anzhiyu/source/js/ 文件夹下新建 zan.js 文件,或者整合到其他 js 文件里面,复制粘贴代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 document .addEventListener ("DOMContentLoaded" , () => { ctrl.getIp (); ctrl.refreshLikeCount (); }); document .addEventListener ("pjax:complete" , () => { ctrl.refreshLikeCount (); }) var ipAddress = '' ;var ctrl = { getIp ( ) { fetch ('https://www.hexoblog.cn/likecount/index.php?mode=getip' ) .then (response => response.json ()) .then (data => { ipAddress = data.ip ; console .log ('您的 IP 地址:' + ipAddress); }) .catch (error => { console .error ('获取 IP 地址失败:' , error); }); }, refreshLikeCount ( ) { var p = window .location .pathname var q = p.substring (1 ,9 ) if (q == 'articles' ) { var i = p.substring (10 ,18 ) fetch (`https://www.hexoblog.cn/likecount/index.php?mode=get&id=${i} ` ) .then (response => response.json ()) .then (data => { if (data.code == 200 ) { var likeCount = data.content [0 ].count document .querySelector ("#article-like-box .like-count" ).innerText = likeCount } else console .log (data.message ) }) .catch (error => { console .error ('获取点赞信息失败:' , error) }) } }, sendArticleLike ( ) { var a = document .querySelector ("#article-like-box .like-button" ) var i = window .location .pathname .substring (10 ,18 ) a.classList .add ("loading" ) fetch (`https://www.hexoblog.cn/likecount/index.php?mode=add&id=${i} &ip=${ipAddress} ` ) .then (response => response.json ()) .then (data => { if (data.code == 200 ) { var likeCount = data.content [0 ].count a.querySelector (".like-count" ).innerText = likeCount a.classList .remove ("loading" ) tools.showMessage ("感谢您的认可!" , "success" , 2 ) } else if (data.code == 205 ) { a.classList .remove ("loading" ) tools.showMessage (data.message , "warning" , 2 ) } else { a.classList .remove ("loading" ) console .log (data.message ) tools.showMessage (data.message , "error" , 2 ) } }) .catch (error => { console .error ('获取点赞信息失败:' , error) a.classList .remove ("loading" ) }) } } window .tools = window .tools || {};window .tools .showMessage = function (text, type, duration ) { const oldMsg = document .querySelector ('#article-like-box .custom-like-message' ); if (oldMsg) oldMsg.remove (); const likeBtn = document .querySelector ("#article-like-box" ); if (!likeBtn) return ; const msgBox = document .createElement ('div' ); msgBox.className = 'custom-like-message' ; msgBox.innerText = text; const typeStyles = { success : { background : 'rgba(67, 181, 129, 0.95)' , color : '#fff' }, warning : { background : 'rgba(250, 173, 20, 0.95)' , color : '#fff' }, error : { background : 'rgba(235, 87, 87, 0.95)' , color : '#fff' } }; const style = typeStyles[type] || typeStyles.success ; msgBox.style .background = style.background ; msgBox.style .color = style.color ; likeBtn.appendChild (msgBox); setTimeout (() => { msgBox.classList .add ('show' ); }, 10 ); setTimeout (() => { msgBox.classList .remove ('show' ); setTimeout (() => { msgBox.remove (); }, 300 ); }, (duration || 2 ) * 1000 ); }; document .addEventListener ('DOMContentLoaded' , bindLikeBtn);document .addEventListener ('pjax:complete' , bindLikeBtn);function bindLikeBtn ( ) { const likeBtn = document .querySelector (".post-like .like-button" ); if (likeBtn) { likeBtn.style .position = 'relative' ; likeBtn.onclick = () => ctrl.sendArticleLike (); } }
1.1.需要结合容器id 上面的代码,需要结合下面 CSS 的容器标识来使用,例如
1 document .querySelector ("#article-like-box .like-count" ).innerText = likeCount
1 var a = document .querySelector ("#article-like-box .like-button" )
1 a.querySelector (".like-count" ).innerText = likeCount
消息弹窗效果中也有类似的代码,这一点儿大家要注意。
1.2.需要结合文章链接特征 因为我的文章链接,都是:articles/3f9e592b.html 的形式,所以截取字符的设置如下:
1 2 3 var q = p.substring (1 ,9 )if (q == 'articles' ) { var i = p.substring (10 ,18 )
同时还需要修改
1 var i = window .location .pathname .substring (10 ,18 )
大家可以研究一下下面的索引:
a
r
t
i
c
l
e
s
/
3
f
9
e
5
9
2
b
.
h
t
m
l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2.必须的 CSS 样式代码 点赞按钮的 CSS 样式,需要在 /themes/anzhiyu/source/css/ 文件夹下新建 zan.css 或者使用默认的 custom.css 都可以。复制粘贴代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 .post-like #article-like-box { width : 100% !important ; max-width : 180px !important ; display : block !important ; position : relative; text-align : center; margin-top : 0rem ; display : -webkit-box; display : -moz-box; display : -webkit-flex; display : -ms-flexbox; display : box; display : flex; -webkit-box-pack: center; -moz-box-pack: center; -o-box-pack: center; -ms-flex -pack: center; -webkit-justify-content : center; justify-content : center; } .post-like #article-like-box .like-button { border-radius : 8px ; background : var (--anzhiyu-red); color : var (--anzhiyu-white); padding : 0 ; margin-right : 0.5rem ; width : 126px ; height : 40px ; line-height : 39px ; -webkit-box-shadow : var (--anzhiyu-shadow-red); box-shadow : var (--anzhiyu-shadow-red); display : inline-block; cursor : pointer; -webkit-transition : all 0.4s ease 0s ; -moz-transition : all 0.4s ease 0s ; -o-transition : all 0.4s ease 0s ; -ms-transition : all 0.4s ease 0s ; transition : all 0.4s ease 0s ; } .post-like #article-like-box .like-button :hover { background : var (--anzhiyu-main) !important ; transform : translateY (-2px ) !important ; } .post-like #article-like-box .like-button .loading { cursor : not-allowed !important ; pointer-events : none !important ; opacity : 0.8 !important ; } .post-like #article-like-box .like-button .loading .like-count { display : none !important ; } .post-like #article-like-box .like-button .loading .load { display : inline-block !important ; } .post-like #article-like-box .like-count , .post-like #article-like-box .load { width : 20px !important ; display : inline-block !important ; text-align : center !important ; } .post-like #article-like-box .load { display : none !important ; } .post-reward { width : 100% !important ; max-width : 180px !important ; display : block !important ; } @media (max-width : 767.98px ) { .post-reward .reward-main { position : absolute; bottom : 40px ; left : -120% !important ; z-index : 90 ; display : none; padding : 0px 0px 15px ; width : 100% ; } } #article-like-box .custom-like-message { position : absolute; left : 50% ; bottom : calc (100% + 10px ); transform : translateX (-50% ) translateY (10px ); padding : 8px 16px ; border-radius : 6px ; font-size : 14px ; z-index : 9999 ; opacity : 0 ; transition : all 0.3s ease; pointer-events : none; white-space : nowrap; box-shadow : 0 2px 8px rgba (0 , 0 , 0 , 0.15 ); } #article-like-box .custom-like-message .show { opacity : 1 ; transform : translateX (-50% ) translateY (0 ); } [data-theme="dark" ] #article-like-box .custom-like-message { box-shadow : 0 2px 8px rgba (0 , 0 , 0 , 0.35 ); border : 1px solid rgba (255 , 255 , 255 , 0.1 ); } [data-theme="dark" ] #article-like-box .custom-like-message [style*="background: rgba(67, 181, 129, 0.95)" ] { background : rgba (52 , 168 , 83 , 0.9 ) !important ; } [data-theme="dark" ] #article-like-box .custom-like-message [style*="background: rgba(250, 173, 20, 0.95)" ] { background : rgba (251 , 191 , 36 , 0.9 ) !important ; } [data-theme="dark" ] #article-like-box .custom-like-message [style*="background: rgba(235, 87, 87, 0.95)" ] { background : rgba (239 , 68 , 68 , 0.9 ) !important ; }
最后的响应式代码,主要是为了纠正手机端赞赏二维码错位被遮挡。
3.主题 pug 模板文件 安知鱼主题的点赞按钮的模板文件,默认路径为: /themes/anzhiyu/layout/includes/post/ 文件夹下面的 reward.pug 文件,复制粘贴代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 .post-like#article-like-box .like-button.button--animated(onclick='ctrl.sendArticleLike()' title='点赞文章') i.anzhiyufont.anzhiyu-icon-thumbs-up =_p('点个赞') span.like-count= '0' span.load i.fas.fa-spinner.fa-spin .post-reward(onclick='anzhiyu.addRewardMask()') .reward-button.button--animated(title=_p('reward.title')) i.anzhiyufont.anzhiyu-icon-hand-heart-fill =_p('reward.button') .reward-main .reward-all span.reward-title=_p('reward.thanks') ul.reward-group each item in theme.reward.QR_code - var clickTo = item.link ? item.link : item.img li.reward-item a(href=url_for(clickTo) target='_blank') img.post-qr-code-img(src=url_for(item.img) alt=item.text) .post-qr-code-desc=item.text a.reward-main-btn(href='/about/#about-reward' target='_blank') .reward-text=_p('reward.list') .reward-dec=_p('reward.list_desc') #quit-box(onclick="anzhiyu.removeRewardMask()" style="display: none")
4.数据库表的创建 因为点赞的信息需要写入数据库,所以需要提前设置好数据库表,注意是数据库表,先新建数据库,在创建数据库表,用来存储获取到的信息,下面的数据库代码需要在数据库里面的 SQL 里面去执行,我使用的 MySQL 数据库,其他数据库对应创建就可以。
4.1.MySQL 1 2 3 4 5 6 7 8 CREATE TABLE IF NOT EXISTS `article_likes` ( `article_id` VARCHAR(64) NOT NULL COMMENT '文章唯一ID', `like_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '点赞数(无符号,避免负数)', `ip_list` TEXT NOT NULL COMMENT '点赞IP列表(逗号分隔)', `update_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '最后更新时间戳', PRIMARY KEY (`article_id`), KEY `idx_update_time` (`update_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章点赞表(稳定版)';
4.2.PostgreSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -- PostgreSQL 文章点赞表(兼容9.5+,可直接复制粘贴执行) CREATE TABLE IF NOT EXISTS article_likes ( article_id VARCHAR(64) NOT NULL, -- 文章唯一ID like_count INT4 NOT NULL DEFAULT 0 CHECK (like_count >= 0), -- 点赞数(无符号) ip_list TEXT NOT NULL, -- 点赞IP列表(逗号分隔) update_time INT4 NOT NULL DEFAULT 0, -- 最后更新时间戳 PRIMARY KEY (article_id) ) WITH (OIDS = FALSE) TABLESPACE pg_default; -- 添加字段注释(PostgreSQL 9.5+ 支持) COMMENT ON COLUMN article_likes.article_id IS '文章唯一ID'; COMMENT ON COLUMN article_likes.like_count IS '点赞数(无符号,避免负数)'; COMMENT ON COLUMN article_likes.ip_list IS '点赞IP列表(逗号分隔)'; COMMENT ON COLUMN article_likes.update_time IS '最后更新时间戳'; -- 添加表注释 COMMENT ON TABLE article_likes IS '文章点赞表(稳定版)'; -- 单独创建索引(PostgreSQL 标准写法,避免语法报错) CREATE INDEX IF NOT EXISTS idx_update_time ON article_likes (update_time);
5.必须的 PHP 文件 在网站根目录新建 likecount 文件夹,在这个文件夹里面新建 index.php 文件,复制粘贴下面的代码,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 <?php header ("Content-Type: application/json; charset=utf-8" );header ("Access-Control-Allow-Origin: *" );error_reporting (E_ALL & ~E_NOTICE);ini_set ('display_errors' , 0 );$db_config = [ 'host' => 'localhost' , 'user' => 'sery2fct1p4bq' , 'pass' => '46qhn44c0hjr' , 'dbname' => 'sery2fct1p4bq' , 'charset' => 'utf8mb4' ]; if (!function_exists ('str_contains' )) { function str_contains ($h , $n ) { return $n === '' ? true : (strpos ($h , $n ) !== false ); } } $conn = mysqli_connect ( $db_config ['host' ], $db_config ['user' ], $db_config ['pass' ], $db_config ['dbname' ] ); if (!$conn ) { echo json_encode (["code" => 500 , "message" => "数据库连接失败" ]); exit ; } mysqli_set_charset ($conn , $db_config ['charset' ]);$mode = trim ($_GET ['mode' ] ?? '' );$id = trim ($_GET ['id' ] ?? '' );$ip = trim ($_GET ['ip' ] ?? '' );$id = mysqli_real_escape_string ($conn , $id );$ip = mysqli_real_escape_string ($conn , $ip );if ($mode === 'getip' ) { $real_ip = $_SERVER ['REMOTE_ADDR' ]; if (!empty ($_SERVER ['HTTP_X_FORWARDED_FOR' ])) { $ip_list = explode (',' , $_SERVER ['HTTP_X_FORWARDED_FOR' ]); $real_ip = trim ($ip_list [0 ]); } echo json_encode (["code" => 200 , "ip" => filter_var ($real_ip , FILTER_VALIDATE_IP) ?: 'unknown' ]); exit ; } if (empty ($id )) { echo json_encode (["code" => 400 , "message" => "文章ID不能为空" ]); exit ; } if ($mode === 'get' ) { $sql = "SELECT `like_count` FROM `article_likes` WHERE `article_id` = '$id ' LIMIT 1" ; $res = mysqli_query ($conn , $sql ); $count = 0 ; if ($res && mysqli_num_rows ($res ) > 0 ) { $row = mysqli_fetch_assoc ($res ); $count = (int )$row ['like_count' ]; } echo json_encode (["code" => 200 , "content" => [["count" => $count ]]]); mysqli_free_result ($res ); exit ; } if ($mode === 'add' ) { if (empty ($ip )) { echo json_encode (["code" => 400 , "message" => "IP地址不能为空" ]); exit ; } $sql_check = "SELECT `ip_list`, `like_count` FROM `article_likes` WHERE `article_id` = '$id ' LIMIT 1" ; $res_check = mysqli_query ($conn , $sql_check ); $ips = '' ; $current_count = 0 ; if ($res_check && mysqli_num_rows ($res_check ) > 0 ) { $row = mysqli_fetch_assoc ($res_check ); $ips = $row ['ip_list' ]; $current_count = (int )$row ['like_count' ]; } else { $sql_insert = "INSERT INTO `article_likes` (`article_id`, `like_count`, `ip_list`, `update_time`) VALUES ('$id ', 0, '', " . time () . ")" ; mysqli_query ($conn , $sql_insert ); } if (str_contains ($ips , $ip )) { echo json_encode (["code" => 205 , "message" => "您已经点过赞啦!" ]); mysqli_free_result ($res_check ); exit ; } $new_ips = empty ($ips ) ? $ip : $ips . ',' . $ip ; $new_count = $current_count + 1 ; $sql_update = "UPDATE `article_likes` SET `like_count` = $new_count , `ip_list` = '$new_ips ', `update_time` = " . time () . " WHERE `article_id` = '$id '" ; mysqli_query ($conn , $sql_update ); echo json_encode (["code" => 200 , "content" => [["count" => $new_count ]]]); mysqli_free_result ($res_check ); exit ; } echo json_encode (["code" => 400 , "message" => "无效的请求模式" ]);mysqli_close ($conn );?>
大家也可以借助 ai 来进行修改,制作符合自己网站的 PHP 文件和 SQL 数据库
6.引入 CSS 和 JS 文件 在安知鱼主题的配置文件中搜索 inject ,就会找到如下代码
1 2 3 4 5 6 7 8 9 10 11 # Inject # Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag) # 插入代码到头部 </head> 之前 和 底部 </body> 之前 inject: head: # 自定义css # - <link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'"> bottom: # 自定义js # - <script src="/js/xxx"></script>
在 head 处进行自定义 CSS 的引入工作
1 - <link rel="stylesheet" href="/css/zan.css" media="defer" onload="this.media='all'">
在 bottom 处进行自定义 js 的引入工作:
1 - <script src="/js/zan.js"></script>
修改完之后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 inject: head: - <link rel="stylesheet" href="/css/zan.css" media="defer" onload="this.media='all'"> bottom: - <script src="/js/zan.js"></script>
7.检查效果生成 最后 Hexo 三连(hexo c && hexo g && hexo s )就可以看到文章中截图所示的效果。