友链实现在线申请及修改

友链实现在线申请及修改

预览效果:

前期准备

  • 准备好电脑
  • 连上网络,WiFi or 4/5G
  • 准备好鼠标准备 Ctrl + CV

所需要的接口

  • pub/friends/ 获取友链 无需鉴权
  • pub/add_friend 新增友链
  • pub/edit_friend 编辑友链

🔔 注意:

  • 在使用这个教程前, 你需要先关闭主题自带的有页面的友链系统
  • 本教程只供参考
  • 有一定的前端代码基础
  • 会用Qexo的管理面板
  • 友链功能要求 Qexo >= 1.5.0 且用户浏览器必须支持文件上传

总体结构

在根目录打开命令行 输入命令创建页面

1
hexo new page links

打开 source/links/index.md 修改页面配置

在页面内引入 Qexo-Friends 将其中的 ${SITE} 改为你的 Qexo 链接

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
---
aside: false
date: '2023-02-18T15:53:53+08:00'
title: 友情链接
type: links
updated: 2023-6-3T16:33:35.746+8:0
---
<div id="qexo-friends"></div>
<link href="/css/friends.css" rel="stylesheet">
<script src="/js/frends.js"></script>
<script>loadQexoFriends("qexo-friends", "${SITE}")</script>

<article class="message is-info">
<div class="message-header">
一键填充
<button class="button is-info" type="submit" onclick="onekey(event)">一键填充</button>
</div>
<div class="control" style="text-align: center;padding: 1.25em 1.5em;">
<textarea id="message" name="message" cols="50" rows="10" placeholder="
一键填充友链信息格式如下:
name: REAI
link: https://blog.reaicc.com
avatar: https://q1.qlogo.cn/g?b=qq&nk=2877406366&s=640
descr: 热爱漫无边际,生活自有分寸!
siteshot: https://image.thum.io/get/width/400/crop/800/allowJPG/wait/20/noanimate/http://blog.reaicc.com
" style="width: 100%; resize:none;border-radius: 4px;border: 0;font-size: 10px;"></textarea>
</div>
<div class="message-header">
申请友链
<div class="field is-grouped" style="width: 190px;justify-content: space-between;">
<div class="control">
<button class="button is-info" type="submit" onclick="askFriend(event)">申请友链</button>
</div>
<div class="control">
<button class="button is-info" type="submit" onclick="updFriend(event)">更新友链</button>
</div>
</div>
</div>
<div class="message-body">
<div class="form-ask-friend">
<div class="field">
<label class="label">名称</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="您的站点名" id="friend-name" required>
<span class="icon is-small is-left">
<i class="fas fa-signature"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">链接</label>
<div class="control has-icons-left">
<input class="input" type="url" placeholder="您网站首页的链接" id="friend-link" required>
<span class="icon is-small is-left">
<i class="fas fa-link"></i>
</span>
</div>
<p class="help ">请确保站点可访问!</p>
</div>
<div class="field">
<label class="label">图标</label>
<div class="control has-icons-left">
<input class="input" type="url" placeholder="您的网站图标(尽量为正圆形)" id="friend-icon" required>
<span class="icon is-small is-left">
<i class="fas fa-image"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">描述</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="请用一句话介绍您的站点" id="friend-des" required>
<span class="icon is-small is-left">
<i class="fas fa-info"></i>
</span>
</div>
</div>
<div class="field">
<div class="control">
<label class="checkbox">
<input type="checkbox" id="friend-check"/> 我提交的不是无意义信息
</label>
</div>
</div>
</div>
</div>
</article>
<script src="https://recaptcha.net/recaptcha/api.js?render=6LcMvpUkAAAAAG0fQhVsZVcBzS04dB8dZzEMiKKg"></script>
<script>
function onekey (event) {
var textarea = document.getElementById('message');
var content = textarea.value;
const regex = /(\w+):\s(.+)/g;
const json = {};
let match;
while ((match = regex.exec(content)) !== null) {
json[match[1]] = match[2];
}
$("#friend-name").val(json.name);
$("#friend-link").val(json.link);
$("#friend-icon").val(json.avatar);
$("#friend-des").val(json.descr);
console.log(json);
if(Object.keys(json).length === 0){alert("填充格式错误!");}else {alert("一键填充成功,请选择申请友链,或者更新友链!");}
}
function TestUrl(url) {
var Expression=/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
var objExp=new RegExp(Expression);
if(objExp.test(url) !== true){
return false;
}
return true;
}
function askFriend (event) {
let check = $("#friend-check").is(":checked");
let name = $("#friend-name").val();
let url = $("#friend-link").val();
let image = $("#friend-icon").val();
let des = $("#friend-des").val();
if(!check){
alert("请勾选\"我提交的不是无意义信息\"");
return;
}
if(!(name&&url&&image&&des)){
alert("信息填写不完整! ");
return;
}
if (!(TestUrl(url))){
alert("URL格式错误! 需要包含HTTP协议头! ");
return;
}
if (!(TestUrl(image))){
alert("图片URL格式错误! 需要包含HTTP协议头! ");
return;
}
event.target.classList.add('is-loading');
grecaptcha.ready(function() {
grecaptcha.execute('你的reCaptcha 密钥', {action: 'submit'}).then(function(token) {
$.ajax({
type: 'get',
cache: false,
url: url,
dataType: "jsonp",
async: false,
processData: false,
complete: function (data) {
if(data.status==200){
let exist = false;
$.ajax({
type: 'POST',
dataType: "json",
url: '/pub/friends/',
success: function (list) {
for(let i=0;i<list.data.length;i++){
if(list.data[i].url === url){
exist = true;
break;
}
}
if(exist){
alert("友链已经存在喽!");
event.target.classList.remove('is-loading');
return;
}else{
$.ajax({
type: 'POST',
dataType: "json",
data: {
"name": name,
"url": url,
"image": image,
"description": des,
"token": API密钥,
"status": "显示"
},
url: '/pub/add_friend/',
success: function (data) {
alert("友链添加成功,准备刷新页面...");
location.reload();
}
});
}
}
});}
else{
alert("URL无法连通!");
}
event.target.classList.remove('is-loading');
}
});
});
});
}
function updFriend (event) {
let check = $("#friend-check").is(":checked");
let name = $("#friend-name").val();
let url = $("#friend-link").val();
let image = $("#friend-icon").val();
let des = $("#friend-des").val();
if(!check){
alert("请勾选\"我提交的不是无意义信息\"");
return;
}
if(!(name&&url&&image&&des)){
alert("信息填写不完整! ");
return;
}
if (!(TestUrl(url))){
alert("URL格式错误! 需要包含HTTP协议头! ");
return;
}
if (!(TestUrl(image))){
alert("图片URL格式错误! 需要包含HTTP协议头! ");
return;
}
event.target.classList.add('is-loading');
grecaptcha.ready(function() {
grecaptcha.execute('你的reCaptcha 密钥', {action: 'submit'}).then(function(token) {
$.ajax({
type: 'get',
cache: false,
url: url,
dataType: "jsonp",
async: false,
processData: false,
complete: function (data) {
if(data.status==200){
let exist = false;
let time ;
$.ajax({
type: 'POST',
dataType: "json",
url: '/pub/friends/',
success: function (list) {
for(let i=0;i<list.data.length;i++){
if(list.data[i].url === url){
exist = true;
time = list.data[i].time;
break;
}
}
if(exist){
$.ajax({
type: 'POST',
dataType: "json",
data: {
"name": name,
"url": url,
"image": image,
"description": des,
"token": API密钥,
"status": "显示",
"time": time
},
url: '/pub/edit_friend/',
success: function (data) {
alert("友链更新成功,准备刷新页面...");
location.reload();
}
});
}else{
alert("友链不存在呀!");
event.target.classList.remove('is-loading');
return;
}
}
});}
else{
alert("URL无法连通!");
}
event.target.classList.remove('is-loading');
}
});
});
});
}
</script>

引入themes/anzhiyu/source/js/frends.js文件

  • 这里遍历友链列表的html模板不喜欢可以自行研究开发
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
function loadQexoFriends(id, url) {
var uri = url + "/pub/friends/";
var loadStyle = '<div class="qexo_loading"><div class="qexo_part"><div style="display: flex; justify-content: center"><div class="qexo_loader"><div class="qexo_inner one"></div><div class="qexo_inner two"></div><div class="qexo_inner three"></div></div></div></div><p style="text-align: center; display: block">友链加载中...</p></div>';
document.getElementById(id).className = "flexcard-flink-list";
document.getElementById(id).innerHTML = loadStyle;
var ajax;
try {
// Firefox, Opera 8.0+, Safari
ajax = new XMLHttpRequest();
} catch (e) {
// Internet Explorer
try {
ajax = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("糟糕,你的浏览器不能上传文件!");
return false;
}
}
}
let list = [];
ajax.open("get", uri, true);
ajax.setRequestHeader("Content-Type", "text/plain");
ajax.onreadystatechange = function () {
if (ajax.readyState == 4) {
if (ajax.status == 200) {
var res = JSON.parse(ajax.response);
if (res["status"]) {
var friends = res["data"];
document.getElementById(id).innerHTML = "";
let h2Tag = document.createElement('h2');
h2Tag.textContent = '相知无远近,万里尚为邻。'+"("+friends.length+")" ;
document.getElementById(id).appendChild(h2Tag);
// 遍历友链列表
for (let i = 0; i < friends.length; i++) {

// 创建链接标签
let aTag = document.createElement('a');
aTag.className = 'flink-list-card cf-friends-link';
aTag.href = friends[i]['url'];
aTag.dataset.title = friends[i]['description'];
aTag.target = '_blank';

// 创建包裹图片的div
let divWrapper = document.createElement('div');
divWrapper.className = 'wrapper cover';

// 创建缩略图链接
let aThumb = document.createElement('a');
aThumb.href = 'https://image.thum.io/get/width/400/crop/800/allowJPG/wait/20/noanimate/'+ friends[i]["url"];
aThumb.dataset.fancybox = 'gallery';
aThumb.dataset.caption = '';
aThumb.dataset.thumb = 'https://image.thum.io/get/width/400/crop/800/allowJPG/wait/20/noanimate/'+ friends[i]["url"];

// 创建图片
let imgCover = document.createElement('img');
imgCover.className = 'cover fadeIn entered loaded';
imgCover.dataset.lazySrc = 'https://image.thum.io/get/width/400/crop/800/allowJPG/wait/20/noanimate/'+ friends[i]["url"];
imgCover.onerror = function() {
this.onerror = null;
this.src = '/img/404.jpg';
};
imgCover.alt = '';
imgCover.draggable = false;
imgCover.dataset.llStatus = 'loaded';
imgCover.src = 'https://image.thum.io/get/width/400/crop/800/allowJPG/wait/20/noanimate/'+ friends[i]["url"];

aThumb.appendChild(imgCover);
divWrapper.appendChild(aThumb);

// 创建信息div
let divInfo = document.createElement('div');
divInfo.className = 'info';

// 创建头像链接
let aAvatar = document.createElement('a');
aAvatar.href = friends[i]["image"];
aAvatar.dataset.fancybox = 'gallery';
aAvatar.dataset.caption = '';
aAvatar.dataset.thumb = friends[i]["image"];

// 创建头像图片
let imgAvatar = document.createElement('img');
imgAvatar.className = 'cf-friends-avatar flink-avatar entered loaded';
imgAvatar.dataset.lazySrc = friends[i]["image"];
imgAvatar.onerror = function() {
this.onerror = null;
this.src = '/img/friend_404.gif';
};
imgAvatar.alt = '';
imgAvatar.draggable = false;
imgAvatar.dataset.llStatus = 'loaded';
imgAvatar.src = friends[i]["image"];

aAvatar.appendChild(imgAvatar);
divInfo.appendChild(aAvatar);

// 创建友链名称
let spanName = document.createElement('span');
spanName.className = 'flink-sitename cf-friends-name';
spanName.textContent = friends[i]["name"];
divInfo.appendChild(spanName);

// 添加标签
aTag.appendChild(divWrapper);
aTag.appendChild(divInfo);

// 添加到页面
document.getElementById(id).appendChild(aTag);
}
// 遍历友链列表,创建友链标签并添加到页面中去,包括友链名称、友链头像、友链缩略图等信息
} else {
console.log(res["data"]["msg"]);
}
} else {
console.log("友链获取失败! 网络错误");
}
}
};
ajax.send(null);
}

参考资料