一、部署Artalk评论系统
- Artalk官方文档
博主使用的是Docker-Compose文件部署,一键部署运行。
二、增加主题侧边栏组件
1. 创建文件widgets.yml
- 在
/blog/source/_data/
文件夹下,创建 widgets.yml 文件
2. 创建热门/热评文章组件
1. /blog/source/_data/widgets.yml
most_comment_pv_pages:
layout: markdown
content: |
<link rel="stylesheet" href="/path/to/your/tab-widget.css">
<div id="tab-widget-artalk">
<div class="widget-header dis-select">
<span class="tab-title" data-tab-title="popular" style="display: inline;">热门文章</span>
<span class="tab-title" data-tab-title="commented" style="display: none;">热评文章</span>
<div class="cap-action">
<div class="icon-button active" data-tab="popular" title="热门文章">
<svg t="1754474761360" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5468" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M388.266667 512l149.333333 149.333333 59.733333-59.733333-89.6-89.6L597.333333 422.4l-59.733333-59.733333L388.266667 512zM853.333333 512c0-187.733333-153.6-341.333333-341.333333-341.333333s-341.333333 153.6-341.333333 341.333333 153.6 341.333333 341.333333 341.333333 341.333333-153.6 341.333333-341.333333z m-85.333333 0c0 140.8-115.2 256-256 256s-256-115.2-256-256 115.2-256 256-256 256 115.2 256 256z" fill="#444444" p-id="5469"></path></svg>
</div>
<div class="icon-button" data-tab="commented" title="热评文章">
<svg t="1754474765479" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5615" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M635.733333 512l-149.333333-149.333333L426.666667 422.4l89.6 89.6-89.6 89.6 59.733333 59.733333 149.333333-149.333333zM170.666667 512c0 187.733333 153.6 341.333333 341.333333 341.333333s341.333333-153.6 341.333333-341.333333-153.6-341.333333-341.333333-341.333333-341.333333 153.6-341.333333 341.333333z m85.333333 0c0-140.8 115.2-256 256-256s256 115.2 256 256-115.2 256-256 256-256-115.2-256-256z" fill="#444444" p-id="5616"></path></svg>
</div>
</div>
</div>
<div class="tab-content active" id="tab-popular" style="margin-top: 0px;">
<div class="loading">加载中...</div>
</div>
<div class="tab-content" id="tab-commented" style="margin-top: 0px;">
<div class="loading">加载中...</div>
</div>
</div>
<script src="/path/to/your/tab-widget.js"></script>
2. 对应的CSS样式代码
#tab-widget-artalk {
border-radius: 12px;
color: #000;
font-size: calc(var(--fsp) * 0.85);
line-height: 1.4;
font-family: "LXGW WenKai Screen", system-ui, "Microsoft Yahei", "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility;
-webkit-tap-highlight-color: transparent;
word-break: break-all;
text-align: justify;
outline: 0;
user-select: none;
}
.widget-body:has(#tab-widget-artalk) {
background: transparent !important;
}
.widget-header.dis-select {
font-family: "LXGW WenKai Screen", system-ui, "Microsoft Yahei", "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility;
-webkit-tap-highlight-color: transparent;
word-break: break-all;
text-align: justify;
visibility: visible;
outline: 0;
user-select: none;
display: flex;
justify-content: space-between;
align-items: baseline;
line-height: 28px;
font-weight: 500;
font-size: .8125rem;
color: var(--text-p1);
}
#tab-widget-artalk .widget-header.dis-select {
padding-left: 0px !important;
padding-right: 0px !important;
}
.widget-header .tab-title {
font-size: inherit;
font-weight: inherit;
color: var(--text-p1);
margin: 0;
}
.widget-header .cap-action {
display: flex;
gap: 12px;
}
.widget-header .icon-button {
cursor: pointer;
color: var(--text-p2);
padding: 4px;
border-radius: 6px;
outline: 0;
}
.widget-header .icon-button svg {
width: 15.6px;
height: 15.6px;
stroke: currentColor;
stroke-width: 1.5;
fill: none;
}
.tab-content {
display: none;
padding: 0;
}
.tab-content.active {
display: block;
}
.article-item {
margin-bottom: 4px;
padding: 4px 10px 4px 22px;
border-radius: 8px;
color: #000;
position: relative;
font-size: 0.95em;
display: flex;
align-items: center;
height: 32px;
font-size: 12px;
}
.tab-content .article-item:last-child {
margin-bottom: 8px;
}
.article-item a {
color: #000;
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
padding-left: 10px;
font-weight: 500;
flex: 1;
}
.article-item .meta {
display: none;
}
badge.img-badge.left.hot.em12 {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 18px;
height: 18px;
padding: 0;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, .1);
z-index: 1;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
}
badge.img-badge.left.hot.em12 svg {
width: 12px;
height: 12px;
}
badge.img-badge.left.hot.em12.top1 { background: #FF3B30; }
badge.img-badge.left.hot.em12.top2 { background: #FF958C; }
badge.img-badge.left.hot.em12.top3 { background: #FF9500; }
badge.img-badge.left.hot.em12.top4,
badge.img-badge.left.hot.em12.top5,
badge.img-badge.left.hot.em12.top6,
badge.img-badge.left.hot.em12.top7,
badge.img-badge.left.hot.em12.top8,
badge.img-badge.left.hot.em12.top9,
badge.img-badge.left.hot.em12.top10 { background: #8E8E93; }
.loading {
text-align: center;
padding: 20px;
color: #555;
}
#tab-widget-artalk {
--bg-a100: transparent !important;
}
.icon-button,
.icon-button:hover,
.icon-button:focus,
.icon-button:active {
background-color: transparent !important;
background: transparent !important;
box-shadow: none !important;
border: none !important;
outline: none !important;
-webkit-tap-highlight-color: transparent !important;
}
3. 对应的JS代码
const config = {
apiBaseUrl: "",
siteName: "",
popularLimit: ,
commentedLimit: ,
author: ""
};
const popularApiUrl = `${config.apiBaseUrl}/api/v2/stats/pv_most_pages?site_name=${config.siteName}&limit=${config.popularLimit}`;
const commentedApiUrl = `${config.apiBaseUrl}/api/v2/stats/comment_most_pages?site_name=${config.siteName}&limit=${config.commentedLimit}`;
function processTitle(title) {
if (!title) return '无标题文章';
const authorSuffixPattern = new RegExp(`\\s*-\\s*${config.author}\\s*$`, 'i');
return title.replace(authorSuffixPattern, '').trim() || title.trim();
}
// 生成数字1-10的圆角正方形SVG图标(数字占比80%)
function getNumberSvg(number) {
const svgs = {
1: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">1</text></svg>`,
2: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">2</text></svg>`,
3: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">3</text></svg>`,
4: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">4</text></svg>`,
5: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">5</text></svg>`,
6: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">6</text></svg>`,
7: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">7</text></svg>`,
8: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">8</text></svg>`,
9: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="75" font-family="Arial" font-size="70" font-weight="bold" text-anchor="middle" fill="#FFFFFF">9</text></svg>`,
10: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="80" height="80" rx="15" ry="15" fill="none"/><text x="50" y="78" font-family="Arial" font-size="60" font-weight="bold" text-anchor="middle" fill="#FFFFFF">10</text></svg>`
};
return svgs[number] || svgs[1];
}
function loadPopularArticles() {
const container = document.getElementById('tab-popular');
fetch(popularApiUrl)
.then(res => res.json())
.then(data => {
container.innerHTML = '';
(data.data || []).forEach((article, index) => {
const title = processTitle(article.title);
const item = document.createElement('div');
item.className = 'article-item';
item.innerHTML = `<badge class="img-badge left hot em12 top${index + 1}">${getNumberSvg(index + 1)}</badge><a href="${article.url}" title="${title}" >${title}</a>`;
container.appendChild(item);
});
})
.catch(() => container.innerHTML = '<div class="loading">热门文章加载失败</div>');
}
function loadCommentedArticles() {
const container = document.getElementById('tab-commented');
fetch(commentedApiUrl)
.then(res => res.json())
.then(data => {
container.innerHTML = '';
(data.data || []).forEach((article, index) => {
const title = processTitle(article.title);
const item = document.createElement('div');
item.className = 'article-item';
item.innerHTML = `<badge class="img-badge left hot em12 top${index + 1}">${getNumberSvg(index + 1)}</badge><a href="${article.url}" title="${title}" >${title}</a>`;
container.appendChild(item);
});
})
.catch(() => container.innerHTML = '<div class="loading">热评文章加载失败</div>');
}
document.querySelectorAll('.widget-header .icon-button').forEach(button => {
button.addEventListener('click', () => {
const tab = button.dataset.tab;
if (!tab) return;
document.querySelectorAll('.widget-header .icon-button')
.forEach(btn => btn.classList.toggle('active', btn === button));
document.querySelectorAll('#tab-widget-artalk .tab-content')
.forEach(content => content.classList.toggle('active', content.id === `tab-${tab}`));
document.querySelectorAll('.widget-header .tab-title')
.forEach(title => {
title.style.display = (title.dataset.tabTitle === tab) ? 'inline' : 'none';
});
if (tab === 'commented' && document.getElementById('tab-commented').innerHTML.includes('加载中')) {
loadCommentedArticles();
}
});
});
// 初始化加载热门文章
loadPopularArticles();
3. 修改配置变量
- 找到这三个变量名
apiBaseUrl
、siteName
、popularLimit
、commentedLimit
、author
分别是Artalk部署地址、网站名称、展示热门文章数、展示热评文章数、站点标题/站点副标题。 - 复制代码时注意代码缩进,不正确的缩进可能会使代码不生效。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容