Stellar主题侧边栏热门/热评文章

一、部署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. 修改配置变量

  • 找到这三个变量名 apiBaseUrlsiteNamepopularLimitcommentedLimitauthor 分别是Artalk部署地址、网站名称、展示热门文章数、展示热评文章数、站点标题/站点副标题。
  • 复制代码时注意代码缩进,不正确的缩进可能会使代码不生效。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
访客头像 - 云晓晨 KaiQi.Wang
欢迎您留下宝贵的见解!
提交
访客头像 - 云晓晨 KaiQi.Wang

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容