插件描述

这是一款专为Halo建站系统设计的个人设备可视化展示插件,支持高效管理并呈现用户的设备收藏与使用场景。 该插件配置了Halo FinderAPI,并内置了个人设备页面,用户也可根据Finder API文档自行设计个人设备展示页。

注意事项

本插件为Halo版本,若需使用wordpress版本,推荐大家使用梅林的WP版个人设备插件

https://www.ryanzm.cn/archives/wo-de-she-bei-cha-jian

核心功能

  • 设备分类管理:可自定义分类标题与描述,构建多层级设备展示体系。

  • 设备详情配置:支持上传设备图片、命名设备名称、添加个性化标签、撰写详细描述,并支持嵌入查看详情链接,可以是文章链接,也可以是商品链接。

  • 内置个人设备展示页: PC端每行最多3个设备,手机端自动切换为单列显示。

  • 实现了自定义评论主体: 方便收集和展示评论来源。

  • 支持Halo FinderAPI:提供标准数据接口,用户可自主对接API自己实现个人设备展示页。

下载地址

版本:V1.0.0

https://github.com/springai/halo-plugin-device/releases/tag/V1.0.0

使用方式

本插件目前暂未上架官方应用市场,仅支持本地上传安装,详细如下:

下载jar包后,进入Halo后台插件页面点击右上角的安装。

进入安装插件页面后,点击本地安装将下载的jar包导入后启动插件即可

功能演示

前端

后台

评论主体

主题适配

路由信息:

  • 模板路径: /templates/devices.html

  • 访问路径:/devices

内置的模板目前是不包含头部和底部组件的,需要用户根据主题去进行适配,调用自己主题的layout组件。用户也可以参考本页最后部分的开发个人设备模板板块,根据提供的Finder API开发适合自己的模板。

以下是适配星度主题的例子,星度用户可直接按照以下内容操作即可:

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
      th:replace="~{modules/layout :: layout(_title = ${singlePage.spec.title},menu_site_title = ${'正在阅读:'+singlePage.spec.title},_content = ~{::content},
      _head = ~{::head},page_js = null,body_class = 'page-template page-template-device page')}">
<th:block th:fragment="head">
    <th:block th:replace="~{modules/page-css}"/>
        <style>
        .device-container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        .device-title {
            font-size: 24px;
            font-weight: bold;
            margin-bottom: 10px;
            text-align: center;
        }
        .device-subtitle {
            font-size: 14px;
            color: #666;
            margin-bottom: 20px;
            text-align: center;
        }
        .device-product-grid {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
        }
        .device-product-card {
            width: 100%;
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            padding: 15px;
            transition: transform 0.3s ease;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
        }
        @media (min-width: 768px) {
            .device-product-card {
                width: 356.33px; /* 最小宽度设置为356.33px */
                min-width: 356.33px; /* 防止元素收缩 */
            }
        }
        @media (min-width: 1024px) {
            .device-product-card {
                width: 356.33px; /* 大屏幕时设置为456.56px */
                min-width: 356.33px;
            }
        }
        .device-product-card:hover {
            transform: scale(1.02);
        }
        .device-product-image {
            width: 100%;
            height: 200px;
            object-fit: contain;
            border-radius: 5px;
            background: #ffffff;
        }
        .device-product-title {
            font-size: 18px;
            font-weight: bold;
            margin-top: 10px;
        }
        .device-product-subtitle {
            font-size: 14px;
            color: #666;
            margin-top: 5px;
        }
        .device-product-description {
            font-size: 14px;
            margin-top: 10px;
            line-height: 1.6;
            flex-grow: 1;
        }
        .device-buttons {
            margin-top: auto;
            text-align: left;
            padding-top: 15px;
        }
        .device-details-button {
            display: inline-flex;
            align-items: center;
            gap: 5px;
            font-size: 12px;
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            color: #495057;
            padding: 8px 16px;
            border: 1px solid #dee2e6;
            border-radius: 25px;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }
        .device-details-button:hover {
            background: linear-gradient(135deg, #007bff 0%, #0062cc 100%);
            color: #fff;
            border-color: #0056b3;
            box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3);
            transform: translateY(-2px);
        }
        .device-details-button:active {
            transform: translateY(0);
            box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3);
        }
        .device-details-button::after {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: linear-gradient(
                    45deg,
                    transparent 25%,
                    rgba(255, 255, 255, 0.2) 50%,
                    transparent 75%
            );
            transform: rotate(45deg);
            transition: all 0.5s ease;
            opacity: 0;
        }
        .device-details-button:hover::after {
            opacity: 1;
            top: 0;
            left: 0;
        }
    </style>
</th:block>
<th:block th:fragment="content">
    <main id="main" class="site-main" role="main">
<div class="device-container" th:each="group : ${deviceFinder.groupBy()}" >
    <h1 class="device-title" th:text="${group.spec.displayName}"></h1>
    <p class="device-subtitle" th:text="${group.spec.description}"></p>
    <div class="device-product-grid">
        <div class="device-product-card"  th:each="device : ${group.devices}">
            <img class="device-product-image" data-fancybox="gallery" th:src="${device.spec.cover}" th:alt="${device.spec.displayName}">
            <h2 class="device-product-title" th:text="${device.spec.displayName}">MacBook Pro</h2>
            <p class="device-product-subtitle" th:text="${device.spec.label}">M1Pro 32G / 1TB</p>
            <p class="device-product-description" th:text="${device.spec.description}">屏幕显示效果好、色彩准确、对比度强、性能强劲、续航优秀。可以用来开发和设计。</p>
            <div class="device-buttons">
                <a th:href="${#strings.startsWith(device.spec.url, 'http://')
        || #strings.startsWith(device.spec.url, 'https://')
            ? device.spec.url
            : 'http://' + device.spec.url}"
                   class="device-details-button">查看详情</a>
            </div>
        </div>
    </div>
</div>

        <th:block th:if="${haloCommentEnabled}">
            <th:block th:replace="~{modules/widgets/comment :: comment(post_name='plugin-device-comment',kind='DeviceComment',group='core.erzip.com')}"></th:block>
        </th:block>
    </main>
</th:block>
</html>

星度用户只需在主题的/templates/目录下新建立一个devices.html,将上面代码粘贴进去,如下图所示:

进入主题的/templates/目录下

001-OALS.avif

创建名为devices.html

002-CbEo.avif

将上面的代码复制进去并保存

003-ADXN.avif

最后进入网站后台管理,清理模板缓存即可

004-NbYh.avif

开发个人设备模板助手

类型定义

DeviceVo

{
    "metadata": {
        "name": "string",                                   // 唯一标识
        "labels": {
            "additionalProp1": "string"
        },
        "annotations": {
            "additionalProp1": "string"
        },
        "creationTimestamp": "2025-05-25T09:48:11.115504917Z"   // 创建时间
    },
    "spec": {
        "displayName": "string",                            // 设备名称
        "label": "string",                                  // 设备标签
        "description": "string",                            // 设备描述
        "cover": "string",                                  // 详情链接
        "url": "string",                                    // 设备封面图片
        "priority": 0,                                      // 优先级
        "groupName": "string"                             // 分组名称,对应分组 metadata.name
    }
}

DeviceGroupVo

{
  "metadata": {
    "name": "string",                                   // 唯一标识
    "labels": {
      "additionalProp1": "string"
    },
    "annotations": {
      "additionalProp1": "string"
    },
    "creationTimestamp": "2025-05-25T09:45:44.978360237Z"    // 创建时间
  },
  "spec": {
    "displayName": "string",                            // 分组名称
    "description": "string",                            // 分组描述
    "priority": 0                                    // 分组优先级
  },
  "status": {
    "deviceCount": 0                                    // 分组下设备数量
  },
  "devices": "List<#DeviceVo>"                           // 分组下所有设备列表
}

DeviceComment

{
    "apiVersion": "core.erzip.com/v1alpha1",
    "kind": "DeviceComment",
    "metadata": {
        "name": "plugin-device-comment",                                   // 唯一标识
        "labels": {
            "plugin.halo.run/plugin-name": "plugin-device-comment"
        },
        "version": 0,
        "creationTimestamp": "2025-05-25T10:49:07.927867444Z"    // 创建时间
    }
}

Finder API

groupBy()

描述

获取全部分组列表

参数

返回值

List<#DeviceGroupVo>

<div class="device-container" th:each="group : ${deviceFinder.groupBy()}" >
    <h1 class="device-title" th:text="${group.spec.displayName}"></h1>
    <p class="device-subtitle" th:text="${group.spec.description}"></p>
    <div class="device-product-grid">
        <div class="device-product-card"  th:each="device : ${group.devices}">
            <img class="device-product-image" data-fancybox="gallery" th:src="${device.spec.cover}" th:alt="${device.spec.displayName}">
            <h2 class="device-product-title" th:text="${device.spec.displayName}"></h2>
            <p class="device-product-subtitle" th:text="${device.spec.label}"></p>
            <p class="device-product-description" th:text="${device.spec.description}"></p>
            <div class="device-buttons">
                <a th:href="${#strings.startsWith(device.spec.url, 'http://')
        || #strings.startsWith(device.spec.url, 'https://')
            ? device.spec.url
            : 'http://' + device.spec.url}"
                   class="device-details-button">查看详情</a>
            </div>
        </div>
    </div>
</div>

listAll()

描述

获取全部设备内容

参数

返回值

List<#DeviceVo>

示例

<ul>
    <li th:each="device : ${deviceFinder.listAll()}">
        <img th:src="${device.spec.url}" th:alt="${device.spec.displayName}" width="280">
        <h2 th:text="${device.spec.displayName}"></h2>
        <p th:text="${device.spec.description}"></p>
        <p th:text="${device.spec.label}"></p>
        <a th:href="${device.spec.url}">查看详情</a>
    </li>
</ul>

listBy(group)

描述

根据分组获取设备列表

参数

group: string - 设备分组名称, 对应 DeviceGroupVo.metadata.name

返回值

List<#DeviceVo>

示例

<ul>
    <li th:each="device : ${deviceFinder.listBy('device-group-05lytpbm')}">
        <img th:src="${device.spec.url}" th:alt="${device.spec.displayName}" width="280">
        <h2 th:text="${device.spec.displayName}"></h2>
        <p th:text="${device.spec.description}"></p>
        <p th:text="${device.spec.label}"></p>
        <a th:href="${device.spec.url}">查看详情</a>
    </li>
</ul>

自定义评论主体

描述

后台自定义评论主体

示例

<div th:if="${haloCommentEnabled}">
    <halo:comment
            group="core.erzip.com"
            kind="DeviceComment"
            name="plugin-device-comment"
    />
</div>