Tim's Blog

Tim's Blog

Halo【Walker】主题改造记录(二):打造一个专属的朋友圈

2025-09-23
Halo【Walker】主题改造记录(二):打造一个专属的朋友圈

上次改造完友链和页脚后,博客的功能完整了不少,但由于官方暂时不考虑第三方插件的适配。于是朋友圈插件的适配便顺理成章地提上了日程

从零开始适配显然不是我的风格 (不会)Walker 主题适配的「links」插件已经有一套非常成熟的配置和渲染逻辑,我的想法就是以此为基础,进行扩展和样式重塑。这不仅能节省大量时间,还能确保后台配置入口的统一性。

⚙️ 配置思路

为了保持配置的简洁,我决定直接复用「links」插件的设置面板。

  • 布局与样式复用:朋友圈的布局模式链接样式以及格栅列数,这些选项都直接沿用了「links」的现有设置。这样一来,我无需为朋友圈单独设计一套配置。

  • 新增页面描述:在复用的基础上,我顺手为朋友圈友链这两个页面都增加了一个小功能——页面描述。它们是在主题配置中的独立文本框,方便在各自的页面顶部加上一段自定义的文字,让页面显得不那么单调,更具个人色彩。

✨ 效果预览

通过配置进行排列组合,可以轻松实现多种视觉效果。下面是我在自己博客上的几种搭配展示。

主题配置

doc_walker3.webp

传统样式 - 格栅 - 三列

doc_walker4.webp

传统样式 - 单行

doc_walker5.webp

链接卡片 - 格栅 -两列

doc_walker6.webp

🚀 动手实践

整个适配过程的核心改动,在于修改主题的配置文件和新增一个渲染模板

1. 整合主题配置

为了实现上述构想,我首先修改了 Walker 主题的 settings.yaml 文件。在其中我为 插件适配 相关的设置分组下,添加了链接插件(links)的 页面描述朋友圈插件(friends)的相关配置。这一步是实现后台统一控制的关键!

以下是主题整体的配置代码:
apiVersion: v1alpha1
kind: Setting
metadata:
  name: theme-walker-setting
spec:
  forms:
    - group: styles
      label: 样式
      formSchema:
        - $formkit: select
          name: menu_position
          label: 菜单位置
          value: "drawer"
          options:
            - label: 抽屉
              value: drawer
            - label: 侧边栏
              value: sidebar
        - $formkit: text
          name: content_width
          label: 内容区域宽度
          value: 1200px
          help: 默认为 1200px
          validation: required
        - $formkit: group
          name: color_scheme
          label: 配色方案
          help: 由于使用的 UI 框架(daisyui)限制,目前可能无法在部分低版本浏览器中切换配色,在这种情况下,将会使用默认配色且隐藏切换按钮。
          value:
            default: "system"
            default_light: "light"
            default_dark: "dark"
            enable_change: true
            switcher_type: "switch"
            enable_view_transition_api: true
          children:
            - $formkit: select
              name: default
              label: 默认配色
              value: "system"
              options:
                - label: 跟随系统
                  value: system
                - label: 浅色
                  value: light
                - label: 深色
                  value: dark
            - $formkit: select
              name: default_light
              label: 默认浅色配色
              value: "light"
              options:
                - label: 默认亮色
                  value: light
                - label: cupcake
                  value: cupcake
                - label: bumblebee
                  value: bumblebee
                - label: emerald
                  value: emerald
                - label: corporate
                  value: corporate
                - label: retro
                  value: retro
                - label: cyberpunk
                  value: cyberpunk
                - label: valentine
                  value: valentine
                - label: garden
                  value: garden
                - label: lofi
                  value: lofi
                - label: pastel
                  value: pastel
                - label: fantasy
                  value: fantasy
                - label: wireframe
                  value: wireframe
                - label: cmyk
                  value: cmyk
                - label: autumn
                  value: autumn
                - label: acid
                  value: acid
                - label: lemonade
                  value: lemonade
                - label: winter
                  value: winter
                - label: nord
                  value: nord
            - $formkit: select
              name: default_dark
              label: 默认深色配色
              value: "light"
              options:
                - label: 默认暗色
                  value: dark
                - label: synthwave
                  value: synthwave
                - label: halloween
                  value: halloween
                - label: forest
                  value: forest
                - label: aqua
                  value: aqua
                - label: black
                  value: black
                - label: luxury
                  value: luxury
                - label: dracula
                  value: dracula
                - label: business
                  value: business
                - label: night
                  value: night
                - label: coffee
                  value: coffee
                - label: dim
                  value: dim
                - label: sunset
                  value: sunset
            - $formkit: checkbox
              name: enable_change
              label: 允许访客切换配色
              value: true
            - $formkit: select
              name: switcher_type
              label: 切换器样式
              value: "switch"
              options:
                - label: 开关
                  value: switch
                - label: 图标
                  value: icon
            - $formkit: checkbox
              name: enable_view_transition_api
              label: 开启切换动画
              value: true
              help: 开启后,切换配色时会有一个过渡动画,但这个特性只在 Chrome 111 以上的版本才能够支持
    - group: opengraph
      label: Open Graph
      formSchema:
        - $formkit: checkbox
          name: enabled
          label: 启用
          value: true
          help: 如果已经有插件提供了 Open Graph 功能,那么可以关闭此设置
        - $formkit: attachment
          name: image
          label: 默认图片
          help: 如果页面没有设置图片,那么将会使用此图片作为 Open Graph 图片
    - group: post
      label: 文章
      formSchema:
        - $formkit: group
          name: list
          label: 文章列表
          value:
            show_cover: true
            show_categories: true
            show_visit: true
            show_comments: true
            show_upvote: true
          children:
            - $formkit: checkbox
              name: show_cover
              label: 显示文章封面
              value: true
            - $formkit: checkbox
              name: show_categories
              label: 显示分类数据
              value: true
            - $formkit: checkbox
              name: show_tags
              label: 显示标签数据
              value: false
            - $formkit: checkbox
              name: show_visit
              label: 显示浏览量
              value: true
            - $formkit: checkbox
              name: show_comments
              label: 显示评论量
              value: true
            - $formkit: checkbox
              name: show_upvote
              label: 显示点赞
              value: true
        - $formkit: group
          name: detail
          label: 文章页面
          value:
            show_cover: true
            content_style: "github"
            show_floating_bar: true
            share_item_ids:
              - wechat
              - x
              - telegram
              - facebook
              - qq
              - qzone
              - weibo
              - douban
              - native
            show_donate: false
            donate_items: []
          children:
            - $formkit: checkbox
              name: show_cover
              label: 显示文章封面
              value: true
            - $formkit: select
              name: content_style
              label: 内容样式
              value: "github"
              options:
                - label: GitHub 风格
                  value: github
                - label: Tailwind CSS Typography
                  value: tailwind
            - $formkit: checkbox
              name: show_floating_bar
              label: 显示浮动工具栏
              value: true
              help: 开启之后,将隐藏文章顶部的点赞、评论、分享按钮
            - $formkit: select
              name: share_item_ids
              label: 分享渠道
              multiple: true
              sortable: true
              options:
                - label: 微信
                  value: wechat
                - label: X
                  value: x
                - label: Telegram
                  value: telegram
                - label: Facebook
                  value: facebook
                - label: QQ
                  value: qq
                - label: QQ 空间
                  value: qzone
                - label: 微博
                  value: weibo
                - label: 豆瓣
                  value: douban
                - label: 系统分享
                  value: native
            - $formkit: checkbox
              name: show_donate
              id: show_donate
              label: 显示赞赏
              value: false
            - $formkit: repeater
              name: donate_items
              label: 赞赏渠道
              if: $value.show_donate
              children:
                - $formkit: text
                  name: name
                  label: 名称
                - $formkit: attachment
                  name: url
                  label: 图片链接
                  validation: "required"
    - group: sidebar
      label: 侧边栏
      formSchema:
        - $formkit: text
          name: title
          label: 标题
          help: 自定义侧边栏标题,默认采用站点名称
        - $formkit: text
          name: description
          label: 描述
          help: 自定义侧边栏描述,默认采用站点名称
        - $formkit: select
          name: avatar_shape
          label: 头像形状
          value: "square"
          options:
            - label: 圆形
              value: round
            - label: 方形
              value: square
        - $formkit: checkbox
          name: enabled_login_button
          label: 显示登录按钮
          help: 开启后,将鼠标放置在头像上时会显示登录按钮
          value: false
        - $formkit: select
          name: search_mode
          label: 搜索功能模式
          value: "modal"
          options:
            - label: 弹窗
              value: modal
            - label: 跳转至搜索页面
              value: page
        - $formkit: repeater
          name: social_media
          label: 社交媒体
          value: []
          children:
            - $formkit: select
              name: icon
              label: 图标
              options:
                - label: 电子邮箱
                  value: i-ri-mail-line
                - label: 微信
                  value: i-ri-wechat-2-line
                - label: 腾讯 QQ
                  value: i-ri-qq-line
                - label: 新浪微博
                  value: i-ri-weibo-fill
                - label: 知乎
                  value: i-ri-zhihu-line
                - label: 豆瓣
                  value: i-ri-douban-line
                - label: 哔哩哔哩
                  value: i-ri-bilibili-line
                - label: 抖音 / TikTok
                  value: i-ri-tiktok-line
                - label: Telegram
                  value: i-ri-telegram-fill
                - label: Facebook
                  value: i-ri-facebook-box-line
                - label: Instagram
                  value: i-ri-instagram-line
                - label: LinkedIn
                  value: i-ri-linkedin-box-line
                - label: Twitter
                  value: i-ri-twitter-line
                - label: Slack
                  value: i-ri-slack-fill
                - label: Discord
                  value: i-ri-discord-line
                - label: YouTube
                  value: i-ri-youtube-line
                - label: Steam
                  value: i-ri-steam-fill
                - label: GitHub
                  value: i-ri-github-fill
                - label: GitLab
                  value: i-ri-gitlab-line
                - label: Gitee
                  value: i-simple-icons-gitee
                - label: CSDN
                  value: i-simple-icons-csdn
                - label: RSS
                  value: i-ri-rss-fill
                - label: Matrix
                  value: i-simple-icons-matrix
            - $formkit: text
              name: name
              label: 名称
            - $formkit: text
              name: url
              label: 链接
              validation: "required"
            - $formkit: select
              name: url_type
              label: 打开类型
              value: normal
              help: "如果选择了图片类型,那么在点击之后会使用弹框的形式加载"
              options:
                - label: 跳转链接
                  value: normal
                - label: 图片
                  value: image
    - group: footer
      label: 页脚
      formSchema:
        - $formkit: code
          name: copyright
          label: 版权信息
          help: 自定义页脚版权信息,支持 HTML
          language: html
          height: 100px
          value: |
            Powered by
            <a href="https://www.halo.run" class="hover:text-base-content" target="_blank">Halo</a> Theme
            <a href="https://www.halo.run/store/apps/app-GHgAR" target="_blank" class="hover:text-base-content">Walker</a>

    - group: moments
      label: 瞬间页面
      formSchema:
        - $formkit: checkbox
          name: enable_rss_button
          label: 显示 RSS 订阅按钮
          value: true
          help: 开启后,将会在瞬间页面显示 RSS 订阅按钮,需要安装 RSS 插件(>=1.4.0)
        - $formkit: group
          name: share
          label: 分享按钮
          value:
            enable: true
            share_item_ids:
              - wechat
              - x
              - telegram
              - facebook
              - qq
              - qzone
              - weibo
              - douban
              - native
          children:
            - $formkit: checkbox
              name: enable
              label: 显示分享按钮
              value: true
            - $formkit: select
              if: "$value.enable"
              name: share_item_ids
              id: share_item_ids
              key: share_item_ids
              label: 分享渠道
              multiple: true
              sortable: true
              options:
                - label: 微信
                  value: wechat
                - label: X
                  value: x
                - label: Telegram
                  value: telegram
                - label: Facebook
                  value: facebook
                - label: QQ
                  value: qq
                - label: QQ 空间
                  value: qzone
                - label: 微博
                  value: weibo
                - label: 豆瓣
                  value: douban
                - label: 系统分享
                  value: native
    - group: plugin
      label: 插件适配
      formSchema:
        - $formkit: group
          name: links
          label: 链接插件
          value:
            enable_comment: false
            type: legacy
            layout: grid
            grid_cols: 4
          children:
            - $formkit: select
              label: 链接样式
              name: type
              options:
                - label: 传统样式
                  value: legacy
                - label: 链接卡片(需要安装编辑器超链接卡片插件)
                  value: hyperlink-card
            - $formkit: select
              label: 链接布局
              name: layout
              id: layout
              options:
                - label: 单行
                  value: block
                - label: 格栅
                  value: grid
            - $formkit: select
              if: $get(layout).value === grid
              label: 格栅列数
              name: grid_cols
              options:
                - label: 四列
                  value: 4
                - label: 三列
                  value: 3
                - label: 两列
                  value: 2
            - $formkit: textarea
              name: description
              rows: 3
              label: '页面描述'
              help: '友链页面的顶部描述语句'
              value: '这里汇聚了我的朋友们。'
            - $formkit: checkbox
              name: enable_comment
              label: 启用评论
              help: "启用链接插件原生适配的评论组件,需要链接插件 >= 1.4.0 版本"
            - $formkit: code
              label: 页面内容
              language: html
              height: 400px
              name: content
              help: 页面底部内容,可以用于填写申请信息,支持 HTML
        - $formkit: group
          name: friends
          label: 朋友圈插件
          value: 
            type: legacy 
            layout: grid
            grid_cols: 4
          children:
            - $formkit: select
              label: 链接样式
              name: type
              options:
                - label: 传统样式
                  value: legacy
                - label: 链接卡片(需要安装编辑器超链接卡片插件)
                  value: hyperlink-card
            - $formkit: select
              label: 链接布局
              name: layout
              id: friends_layout
              options:
                - label: 单行
                  value: block
                - label: 格栅
                  value: grid
            - $formkit: select
              if: $get(friends_layout).value === grid
              label: 格栅列数
              name: grid_cols
              options:
                - label: 四列
                  value: 4
                - label: 三列
                  value: 3
                - label: 两列
                  value: 2
            - $formkit: textarea
              name: description
              rows: 3
              label: '页面描述'
              help: '朋友圈页面的顶部描述语句'
              value: '这里汇聚了朋友们的最新文章动态,通过RSS订阅自动更新。'
    - group: development
      label: 开发设置
      formSchema:
        - $formkit: checkbox
          name: enabled
          label: 启用开发模式
          value: false
          help: 仅供开发环境使用,开启此设置后,所有静态资源由 Vite 开发服务器提供,所以需要在本地启动 Vite 服务(pnpm dev)

2. 核心渲染模板

有了主题的配置作为“开关”,接下来就是创建负责渲染前端界面的模板。我在 theme-walker/templates/ 目录下,以 links 的组件代码为蓝本,新增了 friends.html 模板文件。

它的整体数据接收框架保持不变,但我对内部部分结构、样式进行了重写,以实现朋友圈特有的布局和细节样式,比如头像的尺寸、作者信息的排列方式,以及卡片化的设计。

以下是核心渲染模板代码:
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
  th:replace="~{modules/layout :: html(title = |${title} - ${site.title}|, content = ~{::content}, head = null, opengraph = ~{::opengraph})}">
<th:block th:fragment="opengraph">
  <!-- Open Graph Start -->
  <meta property="og:site_name" th:content="${site.title}" />
  <meta property="og:type" content="website" />
  <meta property="og:title" content="|${title} - ${site.title}|" />
  <meta property="og:url" th:content="|${site.url}/friends|" />
  <meta th:unless="${#strings.isEmpty(site.seo.description)}" property="og:description"
    th:content="${site.seo.description}" />
  <meta th:unless="${#strings.isEmpty(theme.config.opengraph.image)}" property="og:image"
    th:content="${theme.config.opengraph.image}" />

  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="|${title} - ${site.title}|" />
  <meta name="twitter:url" th:content="|${site.url}/friends|" />
  <meta th:unless="${#strings.isEmpty(site.seo.description)}" name="twitter:description"
    th:content="${site.seo.description}" />
  <meta th:unless="${#strings.isEmpty(theme.config.opengraph.image)}" name="twitter:image"
    th:content="${theme.config.opengraph.image}" />
  <!-- Open Graph End -->
</th:block>
<th:block th:fragment="content">

  <div class="main flex flex-col px-4 lg:flex-row">
    <th:block th:replace="~{modules/sidebar}" />
    <div class="content-wrapper pt-32 lg:w-3/4 lg:p-8" :class="{ 'is-second' : menuVisible }">

      <!-- 朋友圈描述 -->
      <th:block th:unless="${#strings.isEmpty(theme.config.plugin.friends.description)}">
        <div class="text-base-content/80">
          <p>
            <span th:text="${theme.config.plugin.friends.description}">这里汇聚了朋友们的最新文章动态,通过RSS订阅自动更新</span>
          </p>
          <p class="  text-xs text-base-content/60" style="margin-top: 0.2rem;">
            点击任意文章可跳转到原文阅读 • 内容来源于各位朋友的博客RSS源
          </p>
        </div>
      </th:block>

      <h2 class="mt-3 mb-6 inline-flex items-center gap-2 text-lg text-base-content/90">
        <i class="i-ri-link inline-block"></i>
        朋友圈
      </h2>

      <div class="flex flex-col gap-5">

        <div
          th:with="layout=${theme.config.plugin.friends.layout},grid_cols=${theme.config.plugin.friends.grid_cols},type=${theme.config.plugin.friends.type}"
          class="grid grid-cols-2 gap-4"
          th:classappend="${layout == 'block' ? '!grid-cols-1': grid_cols == 4 ? 'lg:grid-cols-4' : grid_cols == 3 ? 'lg:grid-cols-3' : ''}">

          <th:block th:if="${type == 'hyperlink-card'}">
            <th:block th:if="${pluginFinder.available('editor-hyperlink-card')}">
              <hyperlink-card th:each="friend : ${friends.items}" th:href="${friend.spec.postLink}" target="_blank"
                th:theme="${layout == 'block' ? 'regular' : 'grid'}"></hyperlink-card>
            </th:block>

            <th:block th:unless="${pluginFinder.available('editor-hyperlink-card')}">
              <div role="alert" class="alert col-span-full">
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                  class="h-6 w-6 shrink-0 stroke-info">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
                </svg>

                <span class="text-sm">
                        此类型需要先安装<a href="https://www.halo.run/store/apps/app-UpUJA" target="_blank">
                          编辑器超链接卡片
                        </a>
                        插件
                      </span>

                <div>
                  <a class="btn btn-neutral btn-sm" href="https://www.halo.run/store/apps/app-UpUJA" target="_blank">
                    去下载
                  </a>
                </div>

              </div>
            </th:block>
          </th:block>

          <th:block th:if="${type == 'legacy'}">

            <!-- 单行 -->
            <th:block th:if="${layout == 'block'}">
              <div class="inline-grid" th:each="friend : ${friends.items}">
                <div
                  class="animated fadeIn  relative flex items-center gap-3 rounded-lg border border-base-content/10 bg-base-100 px-3 py-4 shadow-sm hover:border-base-content/30 hover:shadow flex-row justify-start">
                  <!-- 头像 -->
                  <a class="group flex-none" th:href="${friend.spec.authorUrl}" target="_blank">
                    <img class="size-12 rounded-full object-contain shadow-md transition-all group-hover:shadow-xl"
                        width="48"
                        height="48"
                        th:src="${friend.spec.logo}"
                        loading="lazy"
                      />
                  </a>
                  <!-- 作者及内容 -->
                  <a class="min-w-0 flex-1 shrink text-start" th:href="${friend.spec.postLink}" target="_blank">
                    <span style="word-break: break-all"
                        class="line-clamp-1 text-sm font-medium text-base-content"
                        th:text="${friend.spec.title}"
                      ></span>
                    <span style="word-break: break-all"
                        class="line-clamp-2 text-sm text-base-content/60"
                        th:text="${friend.spec.description}"
                      ></span>
                    <div class="mt-2 flex items-center   text-xs text-base-content/50 justify-between">
                      <span style="word-break: break-all" th:text="${friend.spec.author}"></span>
                      <time th:datetime="${#dates.format(friend.spec.pubDate,'yyyy-MM-dd')}"
                        th:text="${#dates.format(friend.spec.pubDate,'yyyy.MM.dd')}"></time>
                    </div>
                  </a>
                </div>
              </div>
            </th:block>

            <th:block th:if="${layout != 'block'}">
              <div class="post-item animated fadeIn rounded-lg border border-base-content/10 "
                th:each="friend : ${friends.items}">
                <!-- 头像和作者信息 -->
                <div class="p-4 flex items-center justify-between">
                  <div class="flex items-center gap-2.5">
                    <a class="group flex-none" th:href="${friend.spec.authorUrl}" target="_blank">
                      <img class="size-6 rounded-full object-contain shadow-md transition-all group-hover:shadow-xl"
                             th:src="${friend.spec.logo}"
                             loading="lazy" />
                    </a>
                    <a style="word-break: break-all" class="text-sm font-medium text-base-content hover:text-primary transition-all duration-200 line-clamp-1"
                      th:href="${friend.spec.authorUrl}" target="_blank" th:text="${friend.spec.author}"></a>
                  </div>
                  <time class="text-xs text-base-content/60"
                    th:datetime="${#dates.format(friend.spec.pubDate,'yyyy-MM-dd')}"
                    th:text="${#dates.format(friend.spec.pubDate,'yyyy.MM.dd')}"></time>
                </div>

                <!-- 文章内容 -->
                <div class="px-4 pb-4">
                  <a class="block" th:href="${friend.spec.postLink}" target="_blank">
                    <div class="post-title ">
                      <h3 class="mb-2 text-base font-medium text-base-content line-clamp-1"
                        th:text="${friend.spec.title}"></h3>
                    </div>
                    <p class="text-sm text-base-content/70 leading-relaxed break-words line-clamp-3"
                      style="margin-top:0.3rem" th:text="${friend.spec.description}"
                      th:title="${friend.spec.description}"></p>
                  </a>
                </div>
              </div>
            </th:block>

          </th:block>
        </div>

        <!-- 翻页 -->
        <div class="animated fadeIn flex justify-between font-bold"
          th:if="${friends.hasPrevious() || friends.hasNext()}">
          <a th:if="${friends.hasPrevious()}"
            class="inline-flex items-center gap-1 border-b-2 border-b-transparent text-sm text-base-content/90 hover:border-base-content hover:text-base-content"
            th:href="@{${friends.prevUrl}}">
            <i class="i-ri-arrow-left-line"></i>
            上一页
          </a>
          <a th:if="${friends.hasNext()}"
            class="inline-flex items-center gap-1 border-b-2 border-b-transparent text-sm text-base-content/90 hover:border-base-content hover:text-base-content"
            th:href="@{${friends.nextUrl}}">
            下一页
            <i class="i-ri-arrow-right-line"></i>
          </a>
        </div>

      </div>
    </div>
  </div>
</th:block>

</html>

友链页面的描述代码就不贴了,可以参考以上

✍️ 写在结尾

通过这两步改动,就成功让 Walker 主题优雅地展示朋友圈动态了。这种在现有基础上进行扩展改造的方式,既高效又充满乐趣。