Compare commits

...

3 Commits

Author SHA1 Message Date
e4122162ba feat: 增加自动重连功能 2026-07-19 18:36:03 +08:00
974dc89292 docs: 更新项目说明 2026-07-19 18:11:43 +08:00
611cb0c6d7 chore: 修改标题为“多功能 MQTT 客户端” 2026-07-19 18:07:49 +08:00
6 changed files with 169 additions and 38 deletions

108
README.md Normal file
View File

@@ -0,0 +1,108 @@
# frost-mqtt-client
一个使用 AI 开发的多功能 MQTT 桌面客户端,基于 Electron + Vue 3 + Naive UI 构建。
## 功能特性
- **多服务器管理**:支持添加、编辑、删除多个 MQTT 服务器配置,数据持久化到本地。
- **灵活连接**:支持 `mqtt://``mqtts://``ws://``wss://` 多种协议,可配置用户名、密码、客户端 ID、Keep Alive、Clean Session 等。
- **主题订阅**:支持 QoS 0/1/2 多级订阅,通配符主题(如 `sensor/+/temperature`),可单个或批量订阅/取消订阅。
- **消息发布**:向指定主题发布消息,支持设置 QoS 和 Retain 标志,内置 JSON 格式化。
- **消息中心**:实时显示接收和发送的消息,支持按主题过滤、清空日志,自动 JSON 美化。
- **仪表盘概览**:展示服务器总数、连接数、订阅主题数、消息总数等关键指标,以及当前服务器状态。
- **本地持久化**:服务器配置、订阅主题、消息记录、发布历史均自动保存到本地用户数据目录。
## 技术栈
- [Electron](https://www.electronjs.org/)
- [Vue 3](https://vuejs.org/)
- [Vite](https://vitejs.dev/)
- [electron-vite](https://electron-vite.org/)
- [Naive UI](https://www.naiveui.com/)
- [MQTT.js](https://github.com/mqttjs/MQTT.js)
## 项目结构
```
frost-mqtt-client/
├── app/ # Electron 应用源码
│ ├── src/
│ │ ├── main/ # 主进程
│ │ │ ├── index.js # 应用入口
│ │ │ ├── ipc-handlers.js # IPC 通信处理
│ │ │ └── mqtt-manager.js # MQTT 连接与数据管理
│ │ ├── preload/ # 预加载脚本
│ │ └── renderer/ # 渲染进程
│ │ └── src/
│ │ ├── components/ # 公共组件
│ │ ├── stores/ # Pinia 状态管理
│ │ ├── views/ # 页面视图
│ │ └── App.vue # 根组件
│ ├── package.json
│ └── electron.vite.config.mjs
├── design/ # 设计稿页面
└── README.md
```
## 环境要求
- [Node.js](https://nodejs.org/) 18+
- [pnpm](https://pnpm.io/) 8+
## 安装依赖
```bash
$ cd app
$ pnpm install
```
## 开发运行
```bash
$ cd app
$ pnpm dev
```
## 构建打包
```bash
$ cd app
# Windows
$ pnpm build:win
# macOS
$ pnpm build:mac
# Linux
$ pnpm build:linux
```
打包后的安装包会输出到 `app/dist` 目录。
## 默认配置
首次启动时,应用会自动创建一个示例服务器:
- **名称**EMQX 公共测试
- **地址**`broker.emqx.io`
- **端口**`1883`
- **协议**`mqtt`
你也可以在左侧服务器列表中点击「添加服务器」按钮,配置自己的 MQTT Broker。
## 使用说明
1. 在左侧边栏选择或添加 MQTT 服务器。
2. 在「仪表盘」页面点击「连接服务器」。
3. 切换到「主题订阅」页面,添加并订阅感兴趣的主题。
4. 在「消息中心」页面可发布消息并实时查看收发日志。
5. 所有配置和消息记录会自动保存,下次打开即可继续使用。
## 数据存储
应用数据(服务器配置、订阅主题、消息记录等)存储在系统的用户数据目录下:
- **Windows**`%APPDATA%\frost-mqtt-client\frost-mqtt-data.json`
- **macOS**`~/Library/Application Support/frost-mqtt-client/frost-mqtt-data.json`
- **Linux**`~/.config/frost-mqtt-client/frost-mqtt-data.json`

View File

@@ -1,30 +0,0 @@
# frost-mqtt-client
An Electron application with Vue
## Project Setup
### Install
```bash
$ pnpm install
```
### Development
```bash
$ pnpm dev
```
### Build
```bash
# For windows
$ pnpm build:win
# For macOS
$ pnpm build:mac
# For Linux
$ pnpm build:linux
```

View File

@@ -110,6 +110,9 @@ class MqttManager {
password: '',
keepAlive: 60,
cleanSession: true,
connectTimeout: 10000,
reconnect: true,
reconnectInterval: 5000,
status: 'disconnected',
topics: [
{ id: 'topic_default_001', topic: 'sensor/+/temperature', qos: 1, subscribed: false },
@@ -175,6 +178,9 @@ class MqttManager {
password: config.password || '',
keepAlive: config.keepAlive || 60,
cleanSession: config.cleanSession !== false,
connectTimeout: config.connectTimeout ?? 10000,
reconnect: config.reconnect !== false,
reconnectInterval: config.reconnectInterval ?? 5000,
status: 'disconnected',
topics: [],
};
@@ -205,6 +211,9 @@ class MqttManager {
username: config.username || '',
keepAlive: config.keepAlive || 60,
cleanSession: config.cleanSession !== false,
connectTimeout: config.connectTimeout ?? server.connectTimeout ?? 10000,
reconnect: config.reconnect !== false,
reconnectInterval: config.reconnectInterval ?? server.reconnectInterval ?? 5000,
});
if (config.password) {
@@ -254,8 +263,8 @@ class MqttManager {
password: server.password || undefined,
keepalive: server.keepAlive,
clean: server.cleanSession,
reconnectPeriod: 5000,
connectTimeout: 10000,
reconnectPeriod: 0,
connectTimeout: server.connectTimeout,
};
try {
@@ -265,6 +274,8 @@ class MqttManager {
client.on('connect', () => {
server.status = 'connected';
this.notify('status-change', { id, status: 'connected' });
// 只在首次连接成功后,再根据配置启用自动重连
client.options.reconnectPeriod = server.reconnect ? server.reconnectInterval : 0;
// 自动订阅已有主题
server.topics.forEach((t) => {
this._doSubscribe(id, t.topic, { qos: t.qos });

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, watch } from 'vue';
import { NModal, NForm, NFormItem, NGrid, NFormItemGi, NInput, NInputNumber, NSelect, NSwitch, NButton, useMessage } from 'naive-ui';
import { NModal, NForm, NGrid, NFormItemGi, NInput, NInputNumber, NSelect, NSwitch, NButton, useMessage } from 'naive-ui';
import { useMqttStore } from '../stores/mqtt.js';
const props = defineProps({
@@ -24,6 +24,9 @@ const form = ref({
password: '',
keepAlive: 60,
cleanSession: true,
connectTimeout: 10000,
reconnect: true,
reconnectInterval: 5000,
});
const rules = {
@@ -57,6 +60,9 @@ watch(
password: '',
keepAlive: props.editServer.keepAlive || 60,
cleanSession: props.editServer.cleanSession !== false,
connectTimeout: props.editServer.connectTimeout ?? 10000,
reconnect: props.editServer.reconnect !== false,
reconnectInterval: props.editServer.reconnectInterval ?? 5000,
};
} else {
isEdit.value = false;
@@ -70,6 +76,9 @@ watch(
password: '',
keepAlive: 60,
cleanSession: true,
connectTimeout: 10000,
reconnect: true,
reconnectInterval: 5000,
};
}
}
@@ -187,9 +196,42 @@ async function handleSave() {
/>
</n-form-item-gi>
</n-grid>
<n-form-item label="Clean Session">
<n-switch v-model:value="form.cleanSession" />
</n-form-item>
<n-grid
:cols="2"
:x-gap="12"
>
<n-form-item-gi label="Clean Session">
<n-switch v-model:value="form.cleanSession" />
</n-form-item-gi>
<n-form-item-gi label="自动重连">
<n-switch v-model:value="form.reconnect" />
</n-form-item-gi>
<n-form-item-gi
label="连接超时 (毫秒)"
path="connectTimeout"
>
<n-input-number
v-model:value="form.connectTimeout"
:min="1000"
:max="120000"
:step="1000"
style="width: 100%"
/>
</n-form-item-gi>
<n-form-item-gi
label="重连间隔 (毫秒)"
path="reconnectInterval"
>
<n-input-number
v-model:value="form.reconnectInterval"
:min="1000"
:max="300000"
:step="1000"
:disabled="!form.reconnect"
style="width: 100%"
/>
</n-form-item-gi>
</n-grid>
</n-form>
<template #footer>
<div style="display: flex; justify-content: flex-end; gap: 8px">

View File

@@ -54,7 +54,7 @@ function handleDelete(server) {
<div class="sidebar-logo">F</div>
<div>
<div class="sidebar-title">Frost MQTT Client</div>
<div class="sidebar-subtitle">服务器 MQTT 客户端</div>
<div class="sidebar-subtitle">功能 MQTT 客户端</div>
</div>
</div>

View File

@@ -813,7 +813,7 @@
<div class="sidebar-logo">F</div>
<div>
<div class="sidebar-title">Frost MQTT Client</div>
<div class="sidebar-subtitle">服务器管理客户端</div>
<div class="sidebar-subtitle">功能 MQTT 客户端</div>
</div>
</div>