这是冰老师做的友链朋友圈项目,我最开始看到它的时候,真就眼前一亮,这就是我想要的效果,立马加了群去了解这个项目,在部署项目的时候冰老师也一直耐心指导,给冰老师点赞👍冰老师还直接帮忙写了css,我觉得这个项目真的特别不错,就转载了冰老师的文章,另外帮他宣传宣传这个项目(虽然我这是个时常日ip不到20的贵站,但是说不定有人看到了呢)。
先上图
我的朋友圈页面
开始正文
首先上冰老师的教程
一直做到第三步,第四步你可以跳回这里。
添加页面
1.在hexo根目录下source文件夹下添加文件夹friendcircle并新建index.md文件写入以下内容
---
title: 朋友圈
date: 2021-02-19 19:27:32
type: "friendcircle"
layout: "friendcircle"
---
2.下载 js 文件并配置前往冰老师提供的地址hexo-moments-js下载 js。修改 js 中的 api 地址(冰老师教程第三步的api链接),
将以下代码中的api链接换位你部署的 api 链接即可。
requests_url = "https://hexo-circle-of-friends-api.vercel.app/api";
改完以后将该js文件保存在主题目录下source/js文件夹下
3.在主题目录下layout文件夹中新建friendcircle.ejs写入以下内容
<%- partial('_partial/bg-cover') %>
<style>
.journal {
padding: 12px;
border: 1px dashed #e6e6e6;
color: #969696;
position: relative;
display: inline-block;
width: 95%;
background: #fbfbfb50;
border-radius: 10px;
font-size: 16px;
margin: 12px auto;
}
</style>
<main class="content">
<div class="container">
<div class="card">
<div class="card-content">
<div class="journal">
<div class="title center-align">“友链朋友圈”</div>
</div>
<div id="friend_link_circle">
<h2>统计信息</h2>
<div id="info_user_poor" class="article-sort-item" style="display: flex; box-shadow: rgba(0, 0, 0, 0.07) 0px 2px 2px 0px, rgba(0, 0, 0, 0.1) 0px 1px 5px 0px; border-radius: 2px">
<div class="chart">
<span class="friend_post_info_title">当前友链数:</span><span class="friend_post_info_number">{{user_lenth}}个</span><br />
<span class="friend_post_info_title">失败数:</span><span class="friend_post_info_number">{{error}}个</span><br />
</div>
<div class="chart">
<span class="friend_post_info_title">活跃友链数:</span><span class="friend_post_info_number">{{unique_live_link}}个</span><br />
<span class="friend_post_info_title">当前库存:</span><span class="friend_post_info_number">{{listlenth}}篇</span><br />
</div>
<div class="chart">
<span class="friend_post_info_title">今日更新:</span><span class="friend_post_info_number">{{today_post}}篇</span><br />
<span class="friend_post_info_title">最近更新:</span><span class="friend_post_info_number">{{last_update_time}}</span>
</div>
</div>
<div v-for="datalist in datalist_slice">
<h2 v-if="datalist[2]-maxnumber<0">{{datalist[0]}}</h2>
<div v-if="item[6]-maxnumber<0" v-for="(item,i) in datalist[1]" class="article-sort-item" style="box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.07), 0 1px 5px 0 rgba(0, 0, 0, 0.1); border-radius: 2px">
<a :target="opentype" class="article-sort-item-img" :href="item[2]" :title="item[0]"><img onerror='this.onerror=null,this.src="/medias/pyq/404.png"' data-ll-status="loaded" class="entered loaded" :src="item[4]" /></a>
<div class="article-sort-item-info">
<div class="article-sort-item-time">
<i class="far fa-user"></i>
<span style="padding-left: 10px; padding-right: 10px">{{item[3]}}</span>
<div class="friend_post_time">
<i class="far fa-calendar-alt"></i>
<time class="post-meta-date-created" :datetime="item[1]" :title="item[1]">{{item[1]}}</time>
</div>
</div>
<a :target="opentype" style="-webkit-line-clamp: 1" class="article-sort-item-title" :href="item[2]" :title="item[0]">{{item[0]}}</a>
</div>
</div>
</div>
<div style="text-align: center">
<button v-if="loadmore_display" type="button" class="load_button" @click="addmaxnumber()">加载更多...</button>
</div>
</div>
<style>
.friend_post_info_title {
font-weight: 700;
}
.friend_post_info_number {
float: right;
}
.chart {
align-items: flex-start;
flex: 1;
width: 100px;
height: 60px;
margin: 20px;
}
@media screen and (max-width: 500px) {
#info_user_poor {
padding: 10px;
flex-direction: column;
max-height: 200px;
}
.chart {
flex: 0;
width: 100%;
height: 160px;
margin: 0;
}
}
.article-sort-item:before {
border: none;
}
@media screen and (min-width: 500px) {
.friend_post_time {
float: right;
}
}
.load_button {
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
text-align: center;
border: 1px solid #ededed;
border-radius: 0.3em;
display: inline-block;
background: transparent;
color: #555;
padding: 0.5em 1.25em;
}
.load_button:hover {
color: #3090e4;
border-color: #3090e4;
}
</style>
<style>
.friend_post_info_title {
font-weight: 700;
}
.friend_post_info_number {
float: right;
}
.chart {
align-items: flex-start;
flex: 1;
width: 100px;
height: 60px;
margin: 20px;
}
@media screen and (max-width: 500px) {
#info_user_poor {
padding: 10px;
flex-direction: column;
max-height: 200px;
}
.chart {
flex: 0;
width: 100%;
height: 160px;
margin: 0;
}
}
.article-sort-item:before {
border: none;
}
@media screen and (min-width: 500px) {
.friend_post_time {
float: right;
}
}
.load_button {
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
text-align: center;
border: 1px solid #ededed;
border-radius: 0.3em;
display: inline-block;
background: transparent;
color: #555;
padding: 0.5em 1.25em;
}
.load_button:hover {
color: #3090e4;
border-color: #3090e4;
}
.article-sort-item {
position: relative;
display: -webkit-box;
display: -moz-box;
display: -webkit-flex;
display: -ms-flexbox;
display: box;
display: flex;
-webkit-box-align: center;
-moz-box-align: center;
-o-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
margin: 0 0 1rem 0.5rem;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.article-sort-item-img {
overflow: hidden;
width: 80px;
height: 80px;
}
.article-sort-item-img img {
max-width: 100%;
}
.article-sort-item-info {
-webkit-box-flex: 1;
-moz-box-flex: 1;
-o-box-flex: 1;
box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
padding: 0 0.8rem;
}
.article-sort-item-title {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
font-size: 1.1em;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
-o-transition: all 0.3s;
-ms-transition: all 0.3s;
transition: all 0.3s;
-webkit-line-clamp: 1;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="/js/request.js"></script>
</div>
</div>
</div>
</main>
4.在主题yml中添加到导航栏
朋友圈:
url: /pyq
icon: fa fa-puzzle-piece
5.添加404图片
如果友链头像错误,直接显示404图片下载该图片并在主题目录下medias文件夹下新建文件夹pyq保存(你也可以自己更改ejs里的位置随意保存)。
最后hexo三连即可,再次感谢冰老师。
本地化api
由于近来vercel十分不稳定动不动就被q,所以我就把友链朋友圈的api放到了本地,如果你也需要的话请先完成集成化部署(源码再github私人仓库),可以参考前文配置。另外需要保证你的友链朋友圈的action还在正常运行,友链朋友圈的action作用是把友链信息友链文章爬取下来保存在leancloud里。友链朋友圈api项目是把leancloud里存放的数据写成json,以便调用。所以我就把冰老师友链朋友圈api项目拿了过来小小的修改一下。为了避免跨域问题需要将原来request.js移到与友链朋友圈同一目录下。如下结构图所示:
source
└── friendcircle
└── index.md //辅助统计页面渲染
└── request.js //处理数据
└── requirements.txt
└── api
└──index.py //获取保存数据
.github
└── workflows
└── api.yml //定时启动api.py更新数据
将request.js覆盖写入如下内容
requests_url = 'api/api.json?date';
const friend_link_circle = new Vue({
el: '#friend_link_circle',
data: {
datalist: [],
datalist_slice:[],
maxnumber:20,
addnumber:10,
display:true,
loadmore_display:false,
listlenth:0,
today_post:0,
last_update_time:'',
user_lenth:'',
error:0,
unique_live_link:0,
opentype:'_blank' //'_blank'打开新标签,'_self'本窗口打开
},
methods:{
unique (arr) {
return Array.from(new Set(arr))
},
formatDate(strDate) {
try{
let date = new Date(Date.parse(strDate.replace(/-/g, "/")));
let gettimeoffset = 0
if (new Date().getTimezoneOffset()){
gettimeoffset = new Date().getTimezoneOffset();
}
else{
gettimeoffset = 8;
}
let timeoffset = gettimeoffset * 60 * 1000;
let len = date.getTime();
let date2 = new Date(len - timeoffset);
let sec = date2.getSeconds().toString();
let min = date2.getMinutes().toString();
if (sec.length === 1) {
sec = "0" + sec;
}
if (min.length === 1) {
min = "0" + min;
}
return date2.getFullYear().toString() + "/" + (date2.getMonth() + 1).toString() + "/" + date2.getDate().toString() + " " + date2.getHours().toString() + ":" + min + ":" + sec
}catch(e){return ""}
},
timezoon(){
let time = this.datalist_slice[0][1][0][5];
return this.formatDate(time)
},
todaypost(){
let date= new Date();
let year = date.getFullYear();
let month =(date.getMonth() + 1).toString();
let day = (date.getDate()).toString();
if (month.length === 1) {
month = "0" + month;
}
if (day.length === 1) {
day = "0" + day;
}
return year + "-" + month + "-" + day
},
addmaxnumber(){
this.maxnumber = this.maxnumber + this.addnumber;
if (this.maxnumber >= this.listlenth){
this.loadmore_display=false;
}
},
slice(data){
let monthlist=[];
let datalist=[];
let data_slice = data;
for (let item in data_slice) {
data_slice[item].push(item);
if (data_slice[item][1].lenth !== 10) {
let list = data_slice[item][1].split('-')
if (list[1].length < 2){
list[1]="0" + list[1]
}
if (list[2].length < 2){
list[2]="0" + list[2]
}
data_slice[item][1] = list.join('-')
}
let month=data_slice[item][1].slice(0,7);
if(monthlist.indexOf(month) !== -1){
console.log(month);
datalist[monthlist.length-1][1].push(data_slice[item]);
}
else{
monthlist.push(month);
datalist.push([month,[data_slice[item]]]);
}
}
for (let mounthgroup of datalist){
mounthgroup.push(mounthgroup[1][0][6]);
}
console.log(datalist);
return datalist
}
},
mounted: function () {
fetch(requests_url+new Date()).then(
data => data.json()
).then(
data => {
let today = this.todaypost();
let Datetody = new Date(today);
for (let item = 0; item <data[1].length ;item++){
let Datedate = new Date(data[1][item][1])
if (Datedate>Datetody){
data[1].splice(item --, 1);
console.log('穿越了');
}
}
this.datalist = data[1];
this.listlenth = data[1].length;
this.user_lenth = data[0].length;
this.datalist_slice = this.slice(data[1]);
this.last_update_time =this.timezoon();
this.loadmore_display = true;
let link_list=[];
for (let item of data[1]){
if (item[1] === today ){
this.today_post +=1;
}
link_list.push(item[3]);
}
let arr = this.unique(link_list);
this.unique_live_link = arr.length;
for (let item of data[0]){
if (item[3] === 'true' ){
this.error +=1;
}
}
}
)
}
})
主要是修改了api请求地址以及在加上了时间戳减少浏览器缓存的影响。
如上新建好index.py后在里面写入如下内容(注意修改里面的appid和AppKey为你友链朋友圈的leancloud的appid和AppKey):
# -*- codeing = utf-8 -*-
# @Time : 2021/02/08 9:05 上午
# @Author : Zfour
# @File : spyder1.py
# @Software : PyCharm
import leancloud
from http.server import BaseHTTPRequestHandler
import json
import datetime
def getdata():
list = ['title','time','link','author','headimg']
list_user = ['frindname','friendlink','firendimg','error']
# Verify key
leancloud.init('appid', 'AppKey')
# Declare class
Friendspoor = leancloud.Object.extend('friend_poor')
# Create an alias for the query
query = Friendspoor.query
# Select the sort methods
query.descending('time')
# Limit the number of queries
query.limit(1000)
# Choose class
query.select('title','time','link','author','headimg','createdAt')
# Execute the query, returning result
query_list = query.find()
Friendlist = leancloud.Object.extend('friend_list')
query_userinfo = Friendlist.query
query_userinfo.limit(1000)
query_userinfo.select('frindname','friendlink','firendimg','error')
query_list_user = query_userinfo.find()
# Result to arr
datalist=[]
for i in query_list:
itemlist=[]
for item in list:
itemlist.append(i.get(item))
update_time = i.get('createdAt')
itemlist.append(update_time.strftime('%Y-%m-%d %H:%M:%S'))
datalist.append(itemlist)
datalist_user =[]
for j in query_list_user:
itemlist_user=[]
for item2 in list_user:
itemlist_user.append(j.get(item2))
datalist_user.append(itemlist_user)
total_data = []
total_data.append(datalist_user)
total_data.append(datalist)
return total_data
# Api handler
class handler(BaseHTTPRequestHandler):
def do_GET(self):
data = getdata()
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(data).encode('utf-8'))
return
print(getdata())
with open("api.json", "w") as f:
json.dump(getdata(), f)
如上新建好requirements.txt后写入如下内容:
arrow==0.17.0
certifi==2020.12.5
cffi==1.14.4
chardet==3.0.4
gevent==1.5.0
greenlet==1.0.0
idna==2.8
iso8601==0.1.13
leancloud==2.9.0
pycparser==2.20
python-dateutil==2.8.1
qiniu==7.2.2
requests==2.22.0
secure-cookie==0.1.0
six==1.15.0
urllib3==1.25.3
Werkzeug==1.0.1
如上新建好api.yml后写入如下内容:
# 当有改动推送到master分支时,启动Action
name: 获取友链api
on:
push:
branches:
- main #2020年10月后github新建仓库默认分支改为main,注意更改
release:
types:
- published
schedule:
- cron: ' 0 * * * *'#每小时执行一次
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: main #2020年10月后github新建仓库默认分支改为main,注意更改
- name: Set up Python #安装python
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: 获取数据
run: |
cd /home/runner/work/hexo/hexo/source/friendcircle
pip3 install -r requirements.txt
cd /home/runner/work/hexo/hexo/source/friendcircle/api
python index.py
git config --global user.name "你的GitHub用户名如我的brqs"
git config --global user.email "你的GitHub邮箱如3447851674@qq.com"
git add .
git commit -m "友链朋友圈api更新"
git push origin main
完成之后上传github即可自动运行。
思考
其实这样做也存在一些弊端,解决vercel不稳定问题的最简单的办法还是用couldflare反代一层最为简单快速。