200字
Mark瞬间动态功能添加
2026-03-17
2026-03-20

主题:theme-fuwari V1.0.3

https://github.com/jiewenhuang/halo-theme-fuwari#readme

瞬间功能插件:瞬间 V1.15.0

https://www.halo.run/store/apps/app-SnwWD

使用方法:下载主题(或直接修改主题内templates/moments.html)

<!doctype html>
<html
  xmlns:th="https://www.thymeleaf.org"
  th:replace="~{modules/layout :: html(title = ${site.title}, hero = null, content = ~{::content}, head = null, footer = null, sidebar = null, contentClass = null, pageType = 'home')}"
>
  <th:block th:fragment="content">
    <!-- 标签列表部分 -->
    <div class="mb-4">
      <ul class="flex flex-wrap gap-2">
        <li th:each="tag : ${tags}">
          <a
            th:href="|/moments?tag=${tag.name}|"
            th:classappend="${#lists.contains(param.tag, tag.name) ? 'active' : ''}"
            class="inline-block px-3 py-1 rounded-full bg-[var(--card-bg)] hover:bg-[var(--card-hover-bg)] transition"
          >
            <span th:text="${tag.name}"></span>
            <span th:text="'(' + ${tag.momentCount} + ')'" class="ml-1 text-sm"></span>
          </a>
        </li>
      </ul>
    </div>

    <!-- 瞬间列表部分 -->
    <div class="space-y-8">  <!-- 增大整体间距 -->
      <ul class="space-y-6">  <!-- 增大条目间距 -->
        <li th:each="moment : ${moments.items}" th:with="content=${moment.spec.content}" 
            class="p-6 mb-6 rounded-xl bg-[var(--card-bg)] shadow-md hover:shadow-lg transition-shadow duration-300">  <!-- 更圆润的圆角和阴影效果 -->
  
          <div class="px-4 py-4">
            <div th:if="${not #strings.isEmpty(content.html)}" th:utext="${content.html}" 
                class="prose prose-sm max-w-none mb-4"></div>  <!-- 增加底部间距 -->
      
            <th:block th:if="${not #lists.isEmpty(content.medium)}" class="mt-4 grid gap-3" 
                    th:classappend="${#lists.size(content.medium) > 1 ? 'grid-cols-2' : ''}">
                <th:block th:each="momentItem : ${content.medium}">
                <img th:if="${momentItem.type.name == 'PHOTO'}" th:src="${momentItem.url}" 
                    class="rounded-lg w-full h-auto object-cover shadow-sm hover:shadow-md transition-shadow" />
                <video th:if="${momentItem.type.name == 'VIDEO'}" th:src="${momentItem.url}" 
                        class="rounded-lg w-full shadow-sm hover:shadow-md transition-shadow" controls></video>
                <audio th:if="${momentItem.type.name == 'AUDIO'}" th:src="${momentItem.url}" 
                        class="w-full rounded-lg bg-[var(--card-hover-bg)] p-2" controls="true"></audio>
                </th:block>
            </th:block>

            <div class="flex mt-2 gap-3 text-base text-black/30 transition dark:text-white/30">
                <div class="flex items-center justify-center space-x-2">
                    <div class="flex space-x-2 items-center justify-center">
                        <span class="icon-[material-symbols--calendar-today-outline-rounded] mr-1 text-lg text-[var(--primary-color)]"></span>
                    </div>
                    <span class="text-sm font-medium" th:text="${#temporals.format(moment.metadata.creationTimestamp, 'yyyy-MM-dd')}"></span>
                </div>
                <div>|</div>
                <div class="flex items-center justify-center">
                    <span class="mr-1 text-lg">
                        <svg viewBox="0 0 24 24" width="1.0em" height="1.0em">
                            <path fill="currentColor" d="M16.5 3C19.538 3 22 5.5 22 9c0 7-7.5 11-10 12.5C9.5 20 2 16 2 9c0-3.5 2.5-6 5.5-6C9.36 3 11 4 12 5c1-1 2.64-2 4.5-2m-3.566 15.604a27 27 0 0 0 2.42-1.701C18.335 14.533 20 11.943 20 9c0-2.36-1.537-4-3.5-4c-1.076 0-2.24.57-3.086 1.414L12 7.828l-1.414-1.414C9.74 5.57 8.576 5 7.5 5C5.56 5 4 6.657 4 9c0 2.944 1.666 5.533 4.645 7.903c.745.593 1.54 1.146 2.421 1.7c.299.189.595.37.934.572c.339-.202.635-.383.934-.571"></path>
                        </svg>
                    </span>
                    <!-- 点赞数 -->
                    <span class="text-sm font-medium" th:text="${moment.stats.upvote}"></span>
                </div>
                <div>|</div>
                <div class="flex items-center justify-center">
                    <span class="icon-[material-symbols--ar-stickers-outline] mr-1 text-lg"></span>
                    <!-- 通过的评论数 -->
                    <span class="text-sm font-medium" th:text="${moment.stats.approvedComment}"></span>
                </div>
            </div>

          </div>

        </li>
      </ul>
  
      <!-- 分页部分 -->
      <div th:if="${moments.hasPrevious() || moments.hasNext()}" 
           class="flex justify-center gap-4 mt-8">
        <a th:href="@{${moments.prevUrl}}" th:if="${moments.hasPrevious()}" 
           class="px-4 py-2 rounded-lg bg-[var(--card-bg)] hover:bg-[var(--primary-color)] hover:text-white transition-colors">
          <span>上一页</span>
        </a>
        <span th:text="'第 ' + ${moments.page} + ' 页'" class="px-4 py-2 text-sm text-neutral-500"></span>
        <a th:href="@{${moments.nextUrl}}" th:if="${moments.hasNext()}" 
           class="px-4 py-2 rounded-lg bg-[var(--card-bg)] hover:bg-[var(--primary-color)] hover:text-white transition-colors">
          <span>下一页</span>
        </a>
      </div>
    </div>
  </th:block>
</html>

2026-03-17T02:09:05-ftabdsqg.png

改一版了:

<!doctype html>
<html
  xmlns:th="https://www.thymeleaf.org"
  th:replace="~{modules/layout :: html(title = ${site.title}, hero = null, content = ~{::content}, head = ~{::head}, footer = null, sidebar = null, contentClass = null, pageType = 'home')}"
>
  
  <th:block th:fragment="head">
    <style>
      /* 一行最多两个 */
      .moments-wall {
        display: flex;
        flex-wrap: wrap;
        gap: 16px;
      }
  
      /* 默认:一行两个(50%宽度) */
      .moment-item {
        flex: 1 1 calc(50% - 8px); /* 减去gap的一半 */
        /* 最小宽度:刚好容纳九宫格(3个小图 + 间距) */
        min-width: min(100%, 420px); /* 约3*136px + gaps,或手机屏幕时100% */
        max-width: calc(50% - 8px); /* 默认最多50% */
      }
  
      /* 有长图的卡片:扩展到100%宽度(占一整行) */
      .moment-item.has-long-image {
        flex: 1 1 100%;
        max-width: 100%;
      }
  
      /* 卡片内部 */
      .moment-card-inner {
        padding: 16px;
        border-radius: 12px;
        background: var(--card-bg);
        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        transition: transform 0.2s, box-shadow 0.2s;
        overflow: hidden;
      }
  
      .moment-card-inner:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(0,0,0,0.15);
      }
  
      /* Markdown 内容 */
      .moment-content {
        width: 100%;
        margin-bottom: 12px;
        word-wrap: break-word;
        overflow: hidden;
      }
  
      /* 长图样式:占满卡片宽度 */
      .moment-content img {
        width: 100%;
        height: auto;
        border-radius: 8px;
        display: block;
      }
  
      /* 九宫格:始终填满卡片宽度 */
      .media-grid {
        display: grid;
        gap: 4px;
        width: 100%;
      }
  
      /* 1张图:占满 */
      .moment-item[data-count="1"] .media-grid {
        grid-template-columns: 1fr;
      }
      .moment-item[data-count="1"] .media-item {
        aspect-ratio: 16/10;
      }
  
      /* 2张图:并排 */
      .moment-item[data-count="2"] .media-grid {
        grid-template-columns: repeat(2, 1fr);
      }
  
      /* 3-4张图:2x2 */
      .moment-item[data-count="3"] .media-grid,
      .moment-item[data-count="4"] .media-grid {
        grid-template-columns: repeat(2, 1fr);
      }
  
      /* 5-9张图:3x3 九宫格 */
      .moment-item[data-count="5"] .media-grid,
      .moment-item[data-count="6"] .media-grid,
      .moment-item[data-count="7"] .media-grid,
      .moment-item[data-count="8"] .media-grid,
      .moment-item[data-count="9"] .media-grid {
        grid-template-columns: repeat(3, 1fr);
      }
  
      /* 媒体项 */
      .media-item {
        position: relative;
        aspect-ratio: 1;
        overflow: hidden;
        border-radius: 4px;
        background-color: var(--card-hover-bg, #f5f5f5);
        cursor: pointer;
      }
  
      .media-item img,
      .media-item video {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
      }
  
      /* 视频播放按钮 */
      .media-item.video-item::before {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 36px;
        height: 36px;
        background: rgba(0, 0, 0, 0.6);
        border-radius: 50%;
        z-index: 1;
      }
  
      .media-item.video-item::after {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-35%, -50%);
        width: 0;
        height: 0;
        border-left: 10px solid white;
        border-top: 7px solid transparent;
        border-bottom: 7px solid transparent;
        z-index: 2;
      }
  
      /* 底部信息 */
      .moment-meta {
        display: flex;
        gap: 12px;
        margin-top: 12px;
        padding-top: 12px;
        border-top: 1px solid var(--border-color, rgba(0,0,0,0.1));
        font-size: 0.85rem;
        color: var(--text-muted);
        flex-wrap: wrap;
      }
  
      /* 手机:一列 */
      @media (max-width: 860px) {
        .moment-item,
        .moment-item.has-long-image {
          flex: 1 1 100%;
          max-width: 100%;
          min-width: auto;
        }
      }
    </style>
  </th:block>

  <th:block th:fragment="content">
    <!-- 标签列表 -->
    <div class="">
      <ul class="flex flex-wrap gap-2 justify-center">
        <li th:each="tag : ${tags}">
          <a
            th:href="|/moments?tag=${tag.name}|"
            th:classappend="${#lists.contains(param.tag, tag.name) ? 'active' : ''}"
            class="inline-block px-3 py-1 rounded-full bg-[var(--card-bg)] hover:bg-[var(--card-hover-bg)] transition text-sm"
          >
            <span th:text="${tag.name}"></span>
            <span th:text="'(' + ${tag.momentCount} + ')'" class="ml-1 opacity-60"></span>
          </a>
        </li>
      </ul>
    </div>

    <!-- 瞬间墙:一行最多两个 -->
    <div class="moments-wall">
      <th:block th:each="moment : ${moments.items}" th:with="content=${moment.spec.content}">
        <!-- 关键:检测是否有长图(<img标签),有则添加has-long-image类,扩展到整行 -->
        <article class="moment-item" 
                 th:data-count="${#lists.size(content.medium)}"
                 th:classappend="${not #strings.isEmpty(content.html) and #strings.contains(content.html, '<img') ? 'has-long-image' : ''}"
                 th:if="${not #lists.isEmpty(content.medium)}">
          <div class="moment-card-inner">
            <!-- Markdown内容 -->
            <div th:if="${not #strings.isEmpty(content.html)}" 
                 th:utext="${content.html}" 
                 class="moment-content prose prose-sm">
            </div>
          
            <!-- 九宫格 -->
            <div class="media-grid">
              <th:block th:each="momentItem : ${content.medium}">
                <div th:if="${momentItem.type.name == 'PHOTO'}" class="media-item">
                  <img th:src="${momentItem.url}" loading="lazy" />
                </div>
                <div th:if="${momentItem.type.name == 'VIDEO'}" class="media-item video-item">
                  <video th:src="${momentItem.url}" preload="metadata"></video>
                </div>
              </th:block>
            </div>

            <div class="moment-meta">
              <span class="flex items-center gap-1">
                <span class="icon-[material-symbols--calendar-today-outline-rounded] text-[var(--primary-color)]"></span>
                <span th:text="${#temporals.format(moment.metadata.creationTimestamp, 'MM-dd')}"></span>
              </span>
              <span class="flex items-center gap-1">
                <svg viewBox="0 0 24 24" width="1em" height="1em" class="text-red-500">
                  <path fill="currentColor" d="M16.5 3C19.538 3 22 5.5 22 9c0 7-7.5 11-10 12.5C9.5 20 2 16 2 9c0-3.5 2.5-6 5.5-6C9.36 3 11 4 12 5c1-1 2.64-2 4.5-2z"/>
                </svg>
                <span th:text="${moment.stats.upvote}"></span>
              </span>
              <span class="flex items-center gap-1">
                <svg viewBox="0 0 24 24" width="1em" height="1em">
                  <path fill="currentColor" d="M2 8.994A5.99 5.99 0 0 1 8 3h8c3.313 0 6 2.695 6 5.994V21H8c-3.313 0-6-2.695-6-5.994zM20 19V8.994A4.004 4.004 0 0 0 16 5H8a3.99 3.99 0 0 0-4 3.994v6.012A4.004 4.004 0 0 0 8 19zm-6-8h2v2h-2zm-6 0h2v2H8z"></path>
                </svg>
                <span th:text="${moment.stats.approvedComment}"></span>
              </span>
            </div>
          </div>
        </article>
  
        <!-- 纯文字瞬间(没有九宫格) -->
        <article class="moment-item has-long-image" data-count="0" th:if="${#lists.isEmpty(content.medium)}">
          <div class="moment-card-inner">
            <div th:if="${not #strings.isEmpty(content.html)}" 
                 th:utext="${content.html}" 
                 class="moment-content prose prose-sm">
            </div>
            <div class="moment-meta">
              <span class="flex items-center gap-1">
                <span class="icon-[material-symbols--calendar-today-outline-rounded] text-[var(--primary-color)]"></span>
                <span th:text="${#temporals.format(moment.metadata.creationTimestamp, 'MM-dd')}"></span>
              </span>
              <span class="flex items-center gap-1">
                <svg viewBox="0 0 24 24" width="1em" height="1em" class="text-red-500">
                  <path fill="currentColor" d="M16.5 3C19.538 3 22 5.5 22 9c0 7-7.5 11-10 12.5C9.5 20 2 16 2 9c0-3.5 2.5-6 5.5-6C9.36 3 11 4 12 5c1-1 2.64-2 4.5-2z"/>
                </svg>
                <span th:text="${moment.stats.upvote}"></span>
              </span>
              <span class="flex items-center gap-1">
                <svg viewBox="0 0 24 24" width="1em" height="1em">
                  <path fill="currentColor" d="M2 8.994A5.99 5.99 0 0 1 8 3h8c3.313 0 6 2.695 6 5.994V21H8c-3.313 0-6-2.695-6-5.994zM20 19V8.994A4.004 4.004 0 0 0 16 5H8a3.99 3.99 0 0 0-4 3.994v6.012A4.004 4.004 0 0 0 8 19zm-6-8h2v2h-2zm-6 0h2v2H8z"></path>
                </svg>
                <span th:text="${moment.stats.approvedComment}"></span>
              </span>
            </div>
          </div>
        </article>
      </th:block>
    </div>
  
    <!-- 分页 -->
    <div th:if="${moments.hasPrevious() || moments.hasNext()}" 
         class="flex justify-center gap-4 mt-4 mb-4">
      <a th:href="@{${moments.prevUrl}}" th:if="${#strings.isEmpty(param.tag) ? moments.hasPrevious() : false}" 
         class="px-4 py-2 rounded-lg bg-[var(--card-bg)] hover:bg-[var(--primary-color)] hover:text-white transition-colors">
        上一页
      </a>
      <span th:text="'第 ' + ${moments.page} + ' 页'" class="px-4 py-2 text-sm opacity-60"></span>
      <a th:href="@{${moments.nextUrl}}" th:if="${moments.hasNext()}" 
         class="px-4 py-2 rounded-lg bg-[var(--card-bg)] hover:bg-[var(--primary-color)] hover:text-white transition-colors">
        下一页
      </a>
    </div>
  </th:block>
</html>

2026-03-20T01:13:54-sscdsmhd.png

Mark瞬间动态功能添加
Author
Administrator
Published at
2026-03-17
License
CC BY-NC-SA 4.0

Comment