<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>Halo - 强大易用的开源建站工具 | 应用市场 | 项目集 | 版本发布</title><link>https://www.halo.run</link><atom:link href="https://www.halo.run/feed/app-store/apps/app-ix3j4n6d/releases.xml" rel="self" type="application/rss+xml"/><description>Halo - 强大易用的开源建站工具 | 应用市场 | 项目集 | 版本发布</description><generator>Halo v2.25.0</generator><language>zh-cn</language><image><url>https://www.halo.run/upload/logo.png</url><title>Halo - 强大易用的开源建站工具 | 应用市场 | 项目集 | 版本发布</title><link>https://www.halo.run</link></image><lastBuildDate>Wed, 17 Jun 2026 14:26:25 GMT</lastBuildDate><follow_challenge><feedId>69597013489248256</feedId><userId>41706424548048896</userId></follow_challenge><item><title><![CDATA[项目集 1.0.0-beta1 发布]]></title><link>https://www.halo.run/store/apps/app-ix3j4n6d/releases/app-release-yxbdrudj</link><description><![CDATA[<img src="https://www.halo.run/plugins/feed/assets/telemetry.gif?title=%E9%A1%B9%E7%9B%AE%E9%9B%86%201.0.0-beta1%20%E5%8F%91%E5%B8%83&amp;url=/store/apps/app-ix3j4n6d/releases/app-release-yxbdrudj" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E9%A1%B9%E7%9B%AE%E9%9B%86%EF%BC%88portfolio%EF%BC%89" tabindex="-1">项目集（Portfolio）</h1>
<p><a href="https://github.com/liuyiwuqing/plugin-portfolio/actions/workflows/ci.yaml"><img src="https://www.halo.run/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fgithub.com%2Fliuyiwuqing%2Fplugin-portfolio%2Factions%2Fworkflows%2Fci.yaml%2Fbadge.svg&amp;size=m" alt="CI"></a>
 <br>
 <a href="https://www.halo.run/./LICENSE"><img src="https://www.halo.run/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.shields.io%2Fgithub%2Flicense%2Fliuyiwuqing%2Fplugin-portfolio&amp;size=m" alt="License"></a>
 <br>
 <a href="https://halo.run"><img src="https://www.halo.run/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimg.shields.io%2Fbadge%2FHalo-%253E%253D2.24.0-blue&amp;size=m" alt="Halo"></a></p>
<blockquote>
 <p>Halo CMS 插件 —— 统一管理和展示 GitHub、Gitee、产品、插件、工具等项目作品。</p>
</blockquote>
<h2 id="%E4%BA%A4%E6%B5%81%E7%BE%A4" tabindex="-1">交流群</h2>
<p><a href="https://qm.qq.com/q/wuC7NZr0sw">点击链接加入群聊【halo博客-lywq插件】</a></p>
<img src="https://www.halo.run/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2Fbf162401-07fd-49ec-b50f-5218c9510937&amp;size=m" style="height: 400px !important; width: auto; object-fit: contain;">
<h2 id="%E5%8A%9F%E8%83%BD%E7%89%B9%E6%80%A7" tabindex="-1">功能特性</h2>
<ul>
 <li><strong>项目管理</strong> — 在 Halo 控制台创建、编辑、删除项目，支持草稿 / 已发布 / 已归档三种状态</li>
 <li><strong>多平台支持</strong> — 内置 GitHub、Gitee、独立站点、私有项目等平台选项，可在设置中自定义扩展</li>
 <li><strong>多类型分类</strong> — 开源项目、产品、插件、网站、工具、类库，同样支持自定义</li>
 <li><strong>技术栈 &amp; 标签</strong> — 每个项目可关联多个技术栈和展示标签，方便筛选和检索</li>
 <li><strong>推荐项目</strong> — 标记重点项目，在首页或侧边栏优先展示</li>
 <li><strong>默认页面</strong> — 开箱即用的 <code>/projects</code> 列表页和 <code>/projects/{slug}</code> 详情页，支持主题覆盖</li>
 <li><strong>Finder API</strong> — 注册 <code>projectFinder</code>，主题模板可直接调用，灵活控制渲染</li>
 <li><strong>编辑器卡片</strong> — 在 Halo 富文本编辑器中插入项目卡片，在文章中嵌入项目展示</li>
 <li><strong>Markdown 内容</strong> — 项目详情支持 Markdown 编写，自动渲染为安全 HTML</li>
 <li><strong>RBAC 权限</strong> — 内置查看 / 管理两级角色模板，支持细粒度权限控制</li>
 <li><strong>SEO 友好</strong> — 自定义页面标题和描述，详情页自动生成 meta 信息</li>
</ul>
<h2 id="%E5%AE%89%E8%A3%85" tabindex="-1">安装</h2>
<h3 id="%E6%96%B9%E5%BC%8F%E4%B8%80%EF%BC%9A%E4%BB%8E-release-%E5%AE%89%E8%A3%85%EF%BC%88%E6%8E%A8%E8%8D%90%EF%BC%89" tabindex="-1">方式一：从 Release 安装（推荐）</h3>
<ol>
 <li>前往 <a href="https://github.com/liuyiwuqing/plugin-portfolio/releases">Releases</a> 下载最新 <code>.jar</code> 文件</li>
 <li>在 Halo 控制台 → 插件管理 → 安装插件，上传 jar 文件</li>
 <li>启用插件</li>
</ol>
<h3 id="%E6%96%B9%E5%BC%8F%E4%BA%8C%EF%BC%9A%E4%BB%8E%E6%BA%90%E7%A0%81%E6%9E%84%E5%BB%BA" tabindex="-1">方式二：从源码构建</h3>
<pre><code class="language-bash">git clone https://github.com/liuyiwuqing/plugin-portfolio.git
cd plugin-portfolio
./gradlew build
</code></pre>
<p>构建产物位于 <code>build/libs/plugin-portfolio-*.jar</code>，在 Halo 控制台上传安装。</p>
<h2 id="%E9%85%8D%E7%BD%AE" tabindex="-1">配置</h2>
<p>插件启用后，在 <strong>控制台 → 插件 → 项目集 → 设置</strong> 中可配置：</p>
<table>
 <thead>
  <tr>
   <th>配置项</th>
   <th>说明</th>
   <th>默认值</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>启用默认展示页</td>
   <td>开启后提供 <code>/projects</code> 和 <code>/projects/{slug}</code> 路由</td>
   <td>开启</td>
  </tr>
  <tr>
   <td>默认每页数量</td>
   <td>列表页每页显示项目数</td>
   <td>12</td>
  </tr>
  <tr>
   <td>默认 SEO 标题</td>
   <td>页面 <code>&lt;title&gt;</code> 和 OG 标题</td>
   <td>项目作品集</td>
  </tr>
  <tr>
   <td>默认 SEO 描述</td>
   <td>页面 <code>&lt;meta description&gt;</code></td>
   <td>集中展示开源项目、产品、插件、工具和其他开发作品。</td>
  </tr>
  <tr>
   <td>平台选项</td>
   <td>项目来源平台下拉选项</td>
   <td>GitHub / Gitee / 独立站点 / 私有项目 / 其他</td>
  </tr>
  <tr>
   <td>类型选项</td>
   <td>项目类型下拉选项</td>
   <td>开源项目 / 产品 / 插件 / 网站 / 工具 / 类库 / 其他</td>
  </tr>
 </tbody>
</table>
<p>平台和类型选项支持自由增删改，<code>value</code> 建议使用小写英文、数字和下划线。</p>
<h2 id="%E4%B8%BB%E9%A2%98%E9%9B%86%E6%88%90" tabindex="-1">主题集成</h2>
<h3 id="finder-api" tabindex="-1">Finder API</h3>
<p>插件注册 <code>projectFinder</code>，在 Thymeleaf 模板中直接注入使用。Finder 只返回已发布项目。</p>
<h4 id="%E6%96%B9%E6%B3%95%E5%88%97%E8%A1%A8" tabindex="-1">方法列表</h4>
<table>
 <thead>
  <tr>
   <th>方法</th>
   <th>返回值</th>
   <th>说明</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>list(page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>分页获取项目</td>
  </tr>
  <tr>
   <td><code>listBy(keyword, platform, type, tag, page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>按关键词、平台、类型、标签组合筛选</td>
  </tr>
  <tr>
   <td><code>listByPlatform(platform, page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>按平台筛选</td>
  </tr>
  <tr>
   <td><code>listByType(type, page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>按类型筛选</td>
  </tr>
  <tr>
   <td><code>listByTechStack(techStack, page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>按技术栈筛选</td>
  </tr>
  <tr>
   <td><code>listByTag(tag, page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>按展示标签筛选</td>
  </tr>
  <tr>
   <td><code>featured(page, size)</code></td>
   <td><code>ListResult&lt;Project&gt;</code></td>
   <td>获取推荐项目</td>
  </tr>
  <tr>
   <td><code>recent(size)</code></td>
   <td><code>List&lt;Project&gt;</code></td>
   <td>获取最近项目（不分页）</td>
  </tr>
  <tr>
   <td><code>count()</code></td>
   <td><code>long</code></td>
   <td>公开项目总数</td>
  </tr>
  <tr>
   <td><code>options()</code></td>
   <td><code>ProjectOptions</code></td>
   <td>获取平台和类型选项列表</td>
  </tr>
  <tr>
   <td><code>setting()</code></td>
   <td><code>PortfolioSetting</code></td>
   <td>获取插件通用设置</td>
  </tr>
  <tr>
   <td><code>platformLabels()</code></td>
   <td><code>Map&lt;String, String&gt;</code></td>
   <td>平台 value → label 映射表</td>
  </tr>
  <tr>
   <td><code>typeLabels()</code></td>
   <td><code>Map&lt;String, String&gt;</code></td>
   <td>类型 value → label 映射表</td>
  </tr>
  <tr>
   <td><code>renderContent(project)</code></td>
   <td><code>String</code></td>
   <td>将项目 Markdown 内容渲染为安全 HTML</td>
  </tr>
  <tr>
   <td><code>listAll()</code></td>
   <td><code>Flux&lt;Project&gt;</code></td>
   <td>获取全部公开项目流</td>
  </tr>
  <tr>
   <td><code>getBySlug(slug)</code></td>
   <td><code>Mono&lt;Project&gt;</code></td>
   <td>按 slug 获取单个项目</td>
  </tr>
 </tbody>
</table>
<h4 id="thymeleaf-%E7%A4%BA%E4%BE%8B" tabindex="-1">Thymeleaf 示例</h4>
<p><strong>展示推荐项目：</strong></p>
<pre><code class="language-html">&lt;th:block th:with="projects=${projectFinder.featured(1, 6)},
                   platformLabels=${projectFinder.platformLabels()},
                   typeLabels=${projectFinder.typeLabels()}"&gt;
  &lt;div class="project-grid"&gt;
    &lt;article th:each="project : ${projects.items}" class="project-card"&gt;
      &lt;a th:href="@{/projects/{slug}(slug=${project.slug})}"&gt;
        &lt;img th:if="${project.cover}" th:src="${project.cover}" th:alt="${project.title}" /&gt;
        &lt;h3 th:text="${project.title}"&gt;项目标题&lt;/h3&gt;
        &lt;p th:text="${project.summary}"&gt;项目简介&lt;/p&gt;
        &lt;span th:text="${platformLabels[project.platform] ?: project.platform}"&gt;平台&lt;/span&gt;
        &lt;span th:text="${typeLabels[project.type] ?: project.type}"&gt;类型&lt;/span&gt;
      &lt;/a&gt;
    &lt;/article&gt;
  &lt;/div&gt;
&lt;/th:block&gt;
</code></pre>
<p><strong>渲染项目详情内容：</strong></p>
<pre><code class="language-html">&lt;div th:utext="${projectFinder.renderContent(project)}"&gt;&lt;/div&gt;
</code></pre>
<p><strong>按技术栈筛选：</strong></p>
<pre><code class="language-html">&lt;th:block th:with="projects=${projectFinder.listByTechStack('Vue', 1, 10)}"&gt;
  &lt;div th:each="project : ${projects.items}"&gt;
    &lt;a th:href="@{/projects/{slug}(slug=${project.slug})}" th:text="${project.title}"&gt;标题&lt;/a&gt;
  &lt;/div&gt;
&lt;/th:block&gt;
</code></pre>
<h3 id="%E9%BB%98%E8%AE%A4%E9%A1%B5%E9%9D%A2" tabindex="-1">默认页面</h3>
<p>插件内置两个 Thymeleaf 模板：</p>
<table>
 <thead>
  <tr>
   <th>路由</th>
   <th>模板</th>
   <th>说明</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>/projects</code></td>
   <td><code>projects.html</code></td>
   <td>项目列表页，响应式网格布局，支持分页</td>
  </tr>
  <tr>
   <td><code>/projects/{slug}</code></td>
   <td><code>project-detail.html</code></td>
   <td>项目详情页，展示封面、元数据和 Markdown 内容</td>
  </tr>
 </tbody>
</table>
<p>主题可通过提供同名模板覆盖默认页面。关闭设置中的「启用默认展示页」后，插件不再注册路由，完全由主题自行渲染。</p>
<h3 id="%E7%BC%96%E8%BE%91%E5%99%A8%E9%A1%B9%E7%9B%AE%E5%8D%A1%E7%89%87" tabindex="-1">编辑器项目卡片</h3>
<p>在 Halo 富文本编辑器中，可通过工具栏或斜杠命令 <code>/</code> 插入「项目卡片」节点。选择一个已发布项目后，文章中会嵌入一个可交互的卡片组件。</p>
<p>卡片在前端渲染为 <code>&lt;portfolio-project-card data-slug="xxx"&gt;</code> 标签，发布时由插件自动替换为带样式的 HTML 卡片，包含封面、标题、摘要和元数据标签。</p>
<h2 id="%E9%A1%B9%E7%9B%AE%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B" tabindex="-1">项目数据模型</h2>
<p>每个项目（<code>Project</code>）包含以下字段：</p>
<table>
 <thead>
  <tr>
   <th>字段</th>
   <th>类型</th>
   <th>必填</th>
   <th>说明</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>title</code></td>
   <td>String</td>
   <td>✅</td>
   <td>项目标题（最长 120 字符）</td>
  </tr>
  <tr>
   <td><code>slug</code></td>
   <td>String</td>
   <td>✅</td>
   <td>URL 友好标识（最长 80 字符）</td>
  </tr>
  <tr>
   <td><code>summary</code></td>
   <td>String</td>
   <td>-</td>
   <td>项目摘要（最长 500 字符）</td>
  </tr>
  <tr>
   <td><code>content</code></td>
   <td>String</td>
   <td>-</td>
   <td>项目详情，支持 Markdown</td>
  </tr>
  <tr>
   <td><code>cover</code></td>
   <td>String</td>
   <td>-</td>
   <td>封面图 URL</td>
  </tr>
  <tr>
   <td><code>platform</code></td>
   <td>String</td>
   <td>-</td>
   <td>来源平台（如 <code>github</code>）</td>
  </tr>
  <tr>
   <td><code>type</code></td>
   <td>String</td>
   <td>-</td>
   <td>项目类型（如 <code>open_source</code>）</td>
  </tr>
  <tr>
   <td><code>techStacks</code></td>
   <td>List&lt;String&gt;</td>
   <td>-</td>
   <td>技术栈标签</td>
  </tr>
  <tr>
   <td><code>tags</code></td>
   <td>List&lt;String&gt;</td>
   <td>-</td>
   <td>展示标签</td>
  </tr>
  <tr>
   <td><code>repoUrl</code></td>
   <td>String</td>
   <td>-</td>
   <td>仓库地址</td>
  </tr>
  <tr>
   <td><code>demoUrl</code></td>
   <td>String</td>
   <td>-</td>
   <td>演示地址</td>
  </tr>
  <tr>
   <td><code>docsUrl</code></td>
   <td>String</td>
   <td>-</td>
   <td>文档地址</td>
  </tr>
  <tr>
   <td><code>priority</code></td>
   <td>Integer</td>
   <td>-</td>
   <td>排序值，越大越靠前</td>
  </tr>
  <tr>
   <td><code>featured</code></td>
   <td>Boolean</td>
   <td>-</td>
   <td>是否推荐</td>
  </tr>
  <tr>
   <td><code>status</code></td>
   <td>Enum</td>
   <td>-</td>
   <td><code>DRAFT</code> / <code>PUBLISHED</code> / <code>ARCHIVED</code></td>
  </tr>
  <tr>
   <td><code>createTime</code></td>
   <td>String</td>
   <td>-</td>
   <td>创建时间</td>
  </tr>
  <tr>
   <td><code>updateTime</code></td>
   <td>String</td>
   <td>-</td>
   <td>更新时间</td>
  </tr>
 </tbody>
</table>
<p>另有 <code>sourceProvider</code>、<code>repoOwner</code>、<code>repoName</code> 字段为后续仓库同步功能预留。</p>
<h2 id="%E5%BC%80%E5%8F%91" tabindex="-1">开发</h2>
<h3 id="%E7%8E%AF%E5%A2%83%E8%A6%81%E6%B1%82" tabindex="-1">环境要求</h3>
<ul>
 <li><strong>Java 21+</strong>（Gradle toolchain 强制）</li>
 <li><strong>Node.js 20+</strong></li>
 <li><strong>pnpm 10+</strong></li>
</ul>
<h3 id="%E5%90%AF%E5%8A%A8%E5%BC%80%E5%8F%91%E6%9C%8D%E5%8A%A1" tabindex="-1">启动开发服务</h3>
<pre><code class="language-bash"># 终端 1：启动 Halo 开发服务器（含后端热重载）
./gradlew haloServer

# 终端 2：启动前端开发服务器（含 HMR）
cd ui
pnpm install
pnpm dev
</code></pre>
<p>Halo 开发服务器默认运行在 <code>http://localhost:8090</code>，前端开发服务器提供模块热替换。</p>
<h3 id="%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84" tabindex="-1">项目结构</h3>
<pre><code class="language-">src/main/java/site/muyin/portfolio/
├── PortfolioPlugin.java          # 插件生命周期，注册 GVK 和索引
├── scheme/Project.java           # 核心数据模型
├── enums/                        # 枚举（项目状态）
├── model/                        # 数据传输对象
├── query/                        # 查询参数构建
├── setting/                      # 插件设置 POJO
├── service/                      # 业务逻辑接口
│   └── impl/                     # 业务逻辑实现
├── endpoint/                     # REST API（Console + Public）
│   └── routes/                   # 路由处理器
├── finders/                      # Finder API 接口
│   └── impl/                     # Finder API 实现
├── content/                      # 内容渲染（Markdown + 项目卡片）
└── router/                       # 默认页面路由

ui/src/
├── index.ts                      # 插件入口，注册路由和编辑器扩展
├── views/ProjectManagerView.vue  # 项目管理页面
├── api/portfolio.ts              # API 客户端和类型定义
├── editor/                       # TipTap 编辑器扩展
└── assets/                       # 静态资源
</code></pre>
<h3 id="%E6%9E%84%E5%BB%BA" tabindex="-1">构建</h3>
<pre><code class="language-bash"># 完整构建（后端 + 前端）
./gradlew build

# 仅构建前端
cd ui &amp;&amp; pnpm build

# 运行后端测试
./gradlew test

# 运行前端检查
cd ui &amp;&amp; pnpm check
</code></pre>
<p>构建产物：<code>build/libs/plugin-portfolio-*.jar</code></p>
<h3 id="ci%2Fcd" tabindex="-1">CI/CD</h3>
<p>项目使用 GitHub Actions 自动化：</p>
<ul>
 <li><strong>CI</strong>（<code>ci.yaml</code>）— 推送或 PR 到 <code>main</code> 分支时触发，执行构建和测试</li>
 <li><strong>CD</strong>（<code>cd.yaml</code>）— 发布 GitHub Release 时触发，构建并产出插件 jar</li>
</ul>
<h2 id="%E6%8A%80%E6%9C%AF%E6%A0%88" tabindex="-1">技术栈</h2>
<table>
 <thead>
  <tr>
   <th>层级</th>
   <th>技术</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>后端语言</td>
   <td>Java 21</td>
  </tr>
  <tr>
   <td>后端框架</td>
   <td>Spring WebFlux（响应式）</td>
  </tr>
  <tr>
   <td>插件平台</td>
   <td>Halo Plugin SDK</td>
  </tr>
  <tr>
   <td>构建工具</td>
   <td>Gradle 9.4</td>
  </tr>
  <tr>
   <td>前端框架</td>
   <td>Vue 3（Composition API）</td>
  </tr>
  <tr>
   <td>前端语言</td>
   <td>TypeScript</td>
  </tr>
  <tr>
   <td>前端构建</td>
   <td>Rsbuild（基于 Rspack）</td>
  </tr>
  <tr>
   <td>包管理</td>
   <td>pnpm</td>
  </tr>
  <tr>
   <td>Markdown</td>
   <td>CommonMark</td>
  </tr>
  <tr>
   <td>测试</td>
   <td>JUnit 5 / Vitest</td>
  </tr>
 </tbody>
</table>
<h2 id="%E8%AE%B8%E5%8F%AF%E8%AF%81" tabindex="-1">许可证</h2>
<p><a href="https://www.halo.run/./LICENSE">GPL-3.0</a> © <a href="https://github.com/liuyiwuqing">Lywq</a></p>
<hr>
<p><a href="https://www.halo.run/store/apps/app-ix3j4n6d/releases/app-release-yxbdrudj">查看版本详情</a></p>]]></description><guid isPermaLink="false">store.halo.run/Release/app-release-yxbdrudj</guid><pubDate>Wed, 17 Jun 2026 02:51:09 GMT</pubDate></item></channel></rss>