抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

首先感谢下林木木童鞋,参考他的代码才有此篇教程,效果可以参考本博说说广场。杜老师不擅长前端,如有更好样式,欢迎在评论区提出建议。本教程样式不兼容所有博客模板,如有错位等问题的出现,可在评论区中留言!

准备工作

此篇教程兼容各类博客框架,不管用的是 Hexo/Hugo/Typecho/WordPress 都可直接使用。

若无服务器可以不用搭建 Memos,借助现有平台「如杜老师的 https://s.dusays.com」注册账户即可。

广场代码

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
<script>
if(typeof Lately==='undefined'){const script=document.createElement('script');script.src='https://jsd.onmicrosoft.cn/gh/Tokinx/Lately/lately.min.js';script.onload=()=>{Lately.init({target:'.bbs-date'});};document.head.appendChild(script);}else{Lately.init({target:'.bbs-date'});}
const urls = [
{host:"https://s.dusays.com/",creatorId:"1",imgsrc:"https://cravatar.cn/avatar/28b57baa4e8f13fe4292ccb2de267e30"},
{host:"https://s.dusays.com/",creatorId:"34",imgsrc:"https://bu.dusays.com/2023/03/10/640b2d3bbaa65.png"}
]
var bbDom=document.querySelector('#bbs');var load='<div id="load" onclick="nextFetch()" ><button class="load-btn button-load">加载更多</button></div>'
var loading='<div class="loader"><svg class="circular" viewBox="25 25 50 50"><circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/></svg></div>'
var bbsDatas=[],bbsData={},nextDatas=[],nextData={},limit=2
var page=1,offset=0,nextLength=0,nextDom='',bbUrlNow='',imgsrcNow='',hostNow='',creIdNow=''
bbDom.innerHTML=loading
allUrls()
function allUrls(){var myHtml=''
for(var i=0;i<urls.length;i++){myHtml+='<div class="bbs-urls " onclick="urlsNow(this)" data-host="'+urls[i].host+'" data-creatorId="'+urls[i].creatorId+'" data-imgsrc="'+urls[i].imgsrc+'" data-index="'+i+'"><img src="'+urls[i].imgsrc+'" alt=""></div>'}
myHtml+='<div class="bbs-urls urls-button" onclick="urlsNow(this)" data-type="random"><svg t="1665928089691" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2562" width="32" height="32"><path d="M913.2 672l98.8 57.1c5.3 3.1 5.3 10.8 0 13.9l-43.4 25L710.4 924c-2.7 1.5-6-0.4-6-3.5V772c0-2.2-1.8-4-4-4H544c-70.4 0-134.4-28.8-180.8-75.2-11.1-11.1-21.2-23.2-30.1-36.1-6.4-9.2-20-9.1-26.4 0.1C260.5 723.9 183.1 768 96 768h-48c-26.5 0-48-21.5-48-48s21.5-48 48-48h48c42.5 0 82.6-16.7 112.9-47.1 30.4-30.4 47.1-70.5 47.1-112.9s-16.7-82.6-47.1-112.9C178.6 368.7 138.4 352 96 352h-48c-26.5 0-48-21.5-48-48s21.5-48 48-48h48c70.4 0 134.4 28.8 180.8 75.2 11.1 11.1 21.2 23.2 30.1 36.1 6.4 9.2 20 9.1 26.4-0.1 46.3-67 123.6-111.1 210.8-111.1H700.4c2.2 0 4-1.8 4-4V103.4c0-3.1 3.3-5 6-3.5l258.2 156 43.4 25.1c5.3 3.1 5.3 10.8 0 13.9L913.2 352 710.4 476c-2.7 1.5-6-0.4-6-3.5V356c0-2.2-1.8-4-4-4H544c-42.5 0-82.6 16.7-112.9 47.1-30.4 30.4-47.1 70.5-47.1 112.9 0 42.5 16.7 82.6 47.1 112.9C461.4 655.3 501.5 672 544 672H700.4c2.2 0 4-1.8 4-4V551.4c0-3.1 3.3-5 6-3.5L913.2 672z" p-id="2563" fill="#f5f5f5"></path></svg></div>'
myHtml+='<div class="bbs-urls urls-button"><a href="https://dusays.com/says/"><svg t="1665929410343" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6308" width="32" height="32"><path d="M906.212134 565.732986 565.732986 565.732986 565.732986 906.212134C565.732986 926.013685 541.666486 959.972 511.97312 959.972 482.297674 959.972 458.213254 926.013685 458.213254 906.212134L458.213254 565.732986 117.734106 565.732986C97.950475 565.732986 63.97424 541.666486 63.97424 511.97312 63.97424 482.279754 97.950475 458.213254 117.734106 458.213254L458.213254 458.213254 458.213254 117.734106C458.213254 97.950475 482.297674 63.97424 511.97312 63.97424 541.666486 63.97424 565.732986 97.950475 565.732986 117.734106L565.732986 458.213254 906.212134 458.213254C925.995765 458.213254 959.972 482.279754 959.972 511.97312 959.972 541.666486 925.995765 565.732986 906.212134 565.732986Z" p-id="6309" fill="#f5f5f5"></path></svg></a></div>'
myHtml='<div id="bbs-urls">'+myHtml+'</div>'
bbDom.insertAdjacentHTML('beforebegin',myHtml);}
function nextFetch(){document.querySelector("button.button-load").textContent='加载中……';updateHTMl(nextDatas)
if(nextLength<10){document.querySelector("button.button-load").remove()
return}
getNextList()};function urlsNow(e){var domUrls=document.querySelectorAll('#bbs-urls .bbs-urls')
if(e.classList.contains('url-now')){domUrls[e.getAttribute("data-index")].classList.remove("url-now")
fetchBBser()}else{domUrls.forEach(function(value,index){domUrls[index].classList.remove("url-now")})
var btn=document.querySelector('button.button-load')
if(btn){btn.remove()}
page=1,offset=0
bbDom.innerHTML=loading
var type=e.getAttribute("data-type")
if(type=='random'){var num=Math.round(Math.random()*(urls.length-1))
hostNow=urls[num].host
creIdNow=urls[num].creatorId
imgsrcNow=urls[num].imgsrc
domUrls[num].classList.add("url-now")}else{domUrls[e.getAttribute("data-index")].classList.add("url-now")
hostNow=e.getAttribute("data-host")
creIdNow=e.getAttribute("data-creatorId")
imgsrcNow=e.getAttribute("data-imgsrc")}
bbUrlNow=hostNow+"api/memo?creatorId="+creIdNow+"&rowStatus=NORMAL&limit=10"
fetch(bbUrlNow).then(res=>res.json()).then(resdata=>{bbDom.innerHTML=''
bbsDatas.length=0
for(var j=0;j<resdata.data.length;j++){var resValue=resdata.data[j]
bbsData={updatedTs:resValue.updatedTs,creatorId:resValue.creatorId,creator:resValue.creatorName||resValue.creator.nickname||resValue.creator.name,imgsrc:imgsrcNow,content:resValue.content,resourceList:resValue.resourceList,url:hostNow}
bbsDatas.push(bbsData)}
updateHTMl(bbsDatas)
bbDom.insertAdjacentHTML('afterend',load);var nowLength=bbsData.length
if(nowLength<10){document.querySelector("button.button-load").remove()
return}
page++
offset=10*(page-1)
getNextList()});}}
function getNextList(){var bbUrl=bbUrlNow+"&offset="+offset;fetch(bbUrl).then(res=>res.json()).then(resdata=>{nextDom=resdata.data
nextLength=nextDom.length
page++
offset=10*(page-1)
if(nextLength<1){document.querySelector("button.button-load").remove()
return}
nextDatas.length=0
for(var j=0;j<nextDom.length;j++){var resValue=nextDom[j]
nextData={updatedTs:resValue.updatedTs,creatorId:resValue.creatorId,creator:resValue.creatorName||resValue.creator.nickname||resValue.creator.name,imgsrc:imgsrcNow,content:resValue.content,resourceList:resValue.resourceList,url:hostNow}
nextDatas.push(nextData)}})}
const withTimeout=(millis,promise)=>{const timeout=new Promise((resolve,reject)=>setTimeout(()=>reject(`Timed out after ms.`),millis));return Promise.race([promise,timeout]);};const fetchBBser=async()=>{const results=await Promise.allSettled(urls.map(url=>withTimeout(2000,fetch(url.host+"api/memo?creatorId="+url.creatorId+"&rowStatus=NORMAL&limit="+limit).then(response=>response.json()).then(resdata=>resdata.data)))).then(results=>{bbDom.innerHTML=''
for(var i=0;i<results.length;i++){var status=results[i].status
if(status=="fulfilled"){var resultsRes=results[i].value
for(var j=0;j<resultsRes.length;j++){var resValue=resultsRes[j]
bbsData={updatedTs:resValue.updatedTs,creatorId:resValue.creatorId,creator:resValue.creatorName||resValue.creator.nickname||resValue.creator.name,imgsrc:urls[i].imgsrc,content:resValue.content,resourceList:resValue.resourceList,url:urls[i].host}
bbsDatas.push(bbsData)}}}
bbsDatas.sort(compare("updatedTs"));updateHTMl(bbsDatas)})}
fetchBBser()
function compare(p){return function(m,n){var a=m[p];var b=n[p];return b-a;}}
function uniqueFunc(arr){const res=new Map();return arr.filter((item)=>!res.has(item.creator)&&res.set(item.creator,1));}
function updateHTMl(data){var result="",resultAll="";const TAG_REG=/#([^\s#]+?) /g,BILIBILI_REG=/<a.*?href="https:\/\/www\.bilibili\.com\/video\/((av[\d]{1,10})|(BV([\w]{10})))\/?".*?>.*<\/a>/g,NETEASE_MUSIC_REG=/<a.*?href="https:\/\/music\.163\.com\/.*id=([0-9]+)".*?>.*<\/a>/g,QQMUSIC_REG=/<a.*?href="https\:\/\/y\.qq\.com\/.*(\/[0-9a-zA-Z]+)(\.html)?".*?>.*?<\/a>/g,QQVIDEO_REG=/<a.*?href="https:\/\/v\.qq\.com\/.*\/([a-z|A-Z|0-9]+)\.html".*?>.*<\/a>/g,YOUKU_REG=/<a.*?href="https:\/\/v\.youku\.com\/.*\/id_([a-z|A-Z|0-9|==]+)\.html".*?>.*<\/a>/g,YOUTUBE_REG=/<a.*?href="https:\/\/www\.youtube\.com\/watch\?v\=([a-z|A-Z|0-9]{11})\".*?>.*<\/a>/g;marked.setOptions({breaks:true,smartypants:true,langPrefix:'language-'});for(var i=0;i<data.length;i++){var memos=data[i].url
var bbContREG=data[i].content.replace(TAG_REG,"<span class='tag-span'>#$1</span> ")
bbContREG=marked.parse(bbContREG).replace(BILIBILI_REG,"<div class='video-wrapper'><iframe src='//player.bilibili.com/player.html?bvid=$1&as_wide=1&high_quality=1&danmaku=0' scrolling='no' border='0' frameborder='no' framespacing='0' allowfullscreen='true'></iframe></div>").replace(NETEASE_MUSIC_REG,"<meting-js auto='https://music.163.com/#/song?id=$1'></meting-js>").replace(QQMUSIC_REG,"<meting-js auto='https://y.qq.com/n/yqq/song$1.html'></meting-js>").replace(QQVIDEO_REG,"<div class='video-wrapper'><iframe src='//v.qq.com/iframe/player.html?vid=$1' allowFullScreen='true' frameborder='no'></iframe></div>").replace(YOUKU_REG,"<div class='video-wrapper'><iframe src='https://player.youku.com/embed/$1' frameborder=0 'allowfullscreen'></iframe></div>").replace(YOUTUBE_REG,"<div class='video-wrapper'><iframe src='https://www.youtube.com/embed/$1' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen title='YouTube Video'></iframe></div>")
if(data[i].resourceList&&data[i].resourceList.length>0){var resourceList=data[i].resourceList;var imgUrl='',resUrl='',resImgLength=0;for(var j=0;j<resourceList.length;j++){var restype=resourceList[j].type.slice(0,5);var resexlink=resourceList[j].externalLink
var resLink='',fileId=''
if(resexlink){resLink=resexlink}else{fileId=resourceList[j].publicId||resourceList[j].filename
resLink=memos+'o/r/'+resourceList[j].id+'/'+fileId}
if(restype=='image'){imgUrl+='<figure class="gallery-thumbnail"><img class="img thumbnail-image" src="'+resLink+'"/></figure>'
resImgLength=resImgLength+1}
if(restype!=='image'){resUrl+='<a target="_blank" rel="noreferrer" href="'+resLink+'">'+resourceList[j].filename+'</a>'}}
if(imgUrl){var resImgGrid=""
if(resImgLength!==1){var resImgGrid="grid grid-"+resImgLength}
bbContREG+='<div class="resimg '+resImgGrid+'">'+imgUrl+'</div></div>'}
if(resUrl){bbContREG+='<div class="resour">'+resUrl+'</div>'}}
result+='<li class=""><div class="bbs-avatar"><img src="'+data[i].imgsrc+'" alt=""><a href="'+data[i].url+'u/'+data[i].creatorId+'" target="_blank" rel="noopener noreferrer" class="bbs-creator">'+data[i].creator+'</a><span class="bbs-dot">·</span><span class="bbs-date">'+new Date(data[i].updatedTs*1000).toLocaleString()+'</span></div><div class="bbs-content"><div class="bbs-text">'+bbContREG+'</div></div></li>'}
var bbBefore="<section class='bbs-timeline'><ul class='list'>"
var bbAfter="</ul></section>"
resultAll=bbBefore+result+bbAfter
bbDom.insertAdjacentHTML('beforeend',resultAll);var btn=document.querySelector('button.button-load')
if(btn){btn.textContent='加载更多';}
window.ViewImage&&ViewImage.init('.bbs-content img')
window.Lately&&Lately.init({target:'.bbs-date'});}
</script>

注意:如需增加站点,可增加 const urls 字段,注意格式。

样式代码

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
<style>
#bbs{padding: 2rem 0;}
#bbs-urls{margin-top: 2rem;}
.bbs-urls{display:inline-block;background: #4a4b50;border-radius:10%;margin:0 .6rem 0 0;padding:4px;width:3.4rem;height:3.4rem;cursor: pointer;vertical-align: text-bottom;}
.bbs-urls img{border-radius:50%;width:100%;height:100%;}
.bbs-urls.url-now{background:#42b983;transition: 0.6s;}
.urls-button svg.icon{padding:10px;width:100%;height: 100%;}
.bbs-timeline ul {margin:0;padding: 0;}
.bbs-timeline ul li{list-style-type:none;position:relative;}
.bbs-timeline{max-width:1200px;margin:0 auto;}
.bbs-avatar{position: relative;}
.bbs-avatar img{width:24px;height:24px;border-radius:50%;margin-right:1rem;}
div.bbs-avatar > img {
display: inline-block;
margin: 0 10px 0 0;
}
.bbs-creator,.bbs-date,.bbs-dot{position:relative;top:-5px;}
.bbs-dot{font-weight: 800;margin:0 .5rem;}
.bbs-content {margin-bottom: 3rem;}
.bbs-text,.resour{background: var(--color-block);border-radius: 8px;font-size: 1em;padding:10px 14px;position: relative;}
.resour{font-size: 0.9rem;margin-top: 2px;padding: 5px 14px;}
.bbs-text{overflow:hidden;max-height:90vh;}
.bbs-text blockquote{font-family: KaiTi,STKaiti,STFangsong;margin:0 0 0 1rem;padding:.25rem 2rem;position: relative;border-left:0 none;}
.bbs-text blockquote::before{line-height: 2rem;content: "“";font-family: Georgia, serif;font-size: 28px;font-weight: bold;position: absolute;left: 10px;top:5px;}
.bbs-text p{margin:0;}
.bbs-text pre p{display: inline-block;}
.bbs-text pre p:empty{display: none;}
.tag-span{color: #42b983;}
#load button.load-btn{width:100%;padding:8px 0;background: var(--color-block);}
#bb-footer{letter-spacing:8px;margin:5rem auto 1rem;text-align:center;}
.dark .bbs-text,.dark .resour{background:#4a4b50;}
.dark .bbs-text p{color:#fafafa;}
.loader {position: relative;margin:3rem auto;width: 100px;}
.loader::before {content: '';display: block;padding-top: 100%;}
.circular {animation: rotate 2s linear infinite;height: 100%;transform-origin: center center;width: 100%;position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;}
.path {stroke-dasharray: 1, 200;stroke-dashoffset: 0;animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;stroke-linecap: round;}
@keyframes rotate {100% {transform: rotate(360deg);}}
@keyframes dash {
0% {stroke-dasharray: 1, 200;stroke-dashoffset: 0;}
50% {stroke-dasharray: 89, 200;stroke-dashoffset: -35px;}
100% {stroke-dasharray: 89, 200;stroke-dashoffset: -124px;}
}
@keyframes color {
100%,0% {stroke: #d62d20;}40% {stroke: #0057e7;}66% {stroke: #008744;}80%,90% {stroke: #ffa700;}
}
.bbs-content p > img{cursor:pointer;border:1px solid #3b3d42;}
.bbs-content p:has(img.img){display: inline-block;}
.bbs-text p > img {display: block;}
.bbs-text p > img:first-child:nth-last-child(n+2),.bbs-text p > img:first-child:nth-last-child(n+2) ~ img {display: inline-block;}
.bbs-content p > img.square{height:180px;width:180px;object-fit:cover;}
.resimg.grid{
display: grid;
grid-template-columns: repeat(3,1fr);
grid-template-rows:auto;
gap: 4px;
width: calc(100%* 2 / 3);
box-sizing: border-box;
margin: 4px 0 0;
}
.resimg.grid-2{
grid-template-columns: repeat(2, 1fr);
width: 80%;
}
.resimg.grid-4{
grid-template-columns: repeat(2, 1fr);
width: calc(80% * 2 / 3);
}
.resimg.grid figure.gallery-thumbnail {
position: relative;
width: 100%;
height: 0;
padding-top: 100%;
cursor: zoom-in;
}
.resimg figure{
text-align: left;
max-height:50%;
}
.resimg figure img{
max-height:50vh;
}
.resimg.grid figure, figcaption {
margin: 0 !important;
}
.resimg.grid figure.gallery-thumbnail > img.thumbnail-image {
position: absolute;
left: 0;
top: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: 50% 50%;
}
.video-wrapper{position:relative;padding-bottom:55%;width:100%;height:0}
.video-wrapper iframe{position:absolute;height:100%;width:100%;}
</style>

注意:上面的样式杜老师做了微调,效果可见本站说说广场,如不满意也可自行调整。

展示代码

1
2
3
4
<div id="bbs"></div>
<script type="text/javascript" src="https://jsd.onmicrosoft.cn/npm/marked/marked.min.js"></script>
<script type="text/javascript" src="https://jsd.onmicrosoft.cn/gh/Tokinx/ViewImage/view-image.min.js"></script>
<script type="text/javascript" src="https://jsd.onmicrosoft.cn/gh/Tokinx/Lately/lately.min.js"></script>

注意:根据自身需求修改对应代码,如有任何问题,可随时在评论区中留言!

评论