Compare commits
2 Commits
515fc2158b
...
7eb62ca37f
| Author | SHA1 | Date | |
|---|---|---|---|
| 7eb62ca37f | |||
| cd2f926ba7 |
@@ -90,8 +90,8 @@ export function registerIpcHandlers() {
|
|||||||
|
|
||||||
// ==================== 主题管理 ====================
|
// ==================== 主题管理 ====================
|
||||||
|
|
||||||
ipcMain.handle('mqtt:add-topic', (_, serverId, topic, qos) => {
|
ipcMain.handle('mqtt:add-topic', (_, serverId, topic, qos, comment) => {
|
||||||
return mqttManager.addTopic(serverId, topic, qos);
|
return mqttManager.addTopic(serverId, topic, qos, comment);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('mqtt:remove-topic', (_, serverId, topic) => {
|
ipcMain.handle('mqtt:remove-topic', (_, serverId, topic) => {
|
||||||
@@ -110,8 +110,8 @@ export function registerIpcHandlers() {
|
|||||||
return mqttManager.subscribeAll(serverId);
|
return mqttManager.subscribeAll(serverId);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('mqtt:update-topic', (_, serverId, topicId, topic, qos) => {
|
ipcMain.handle('mqtt:update-topic', (_, serverId, topicId, topic, qos, comment) => {
|
||||||
return mqttManager.updateTopic(serverId, topicId, topic, qos);
|
return mqttManager.updateTopic(serverId, topicId, topic, qos, comment);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 消息发布 ====================
|
// ==================== 消息发布 ====================
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class MqttManager {
|
|||||||
s.topics = (s.topics || []).map((t) => ({
|
s.topics = (s.topics || []).map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
id: t.id || 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6),
|
id: t.id || 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6),
|
||||||
|
comment: t.comment ?? '',
|
||||||
subscribed: false,
|
subscribed: false,
|
||||||
}));
|
}));
|
||||||
this.servers.set(s.id, s);
|
this.servers.set(s.id, s);
|
||||||
@@ -154,8 +155,8 @@ class MqttManager {
|
|||||||
reconnectInterval: 5000,
|
reconnectInterval: 5000,
|
||||||
status: 'disconnected',
|
status: 'disconnected',
|
||||||
topics: [
|
topics: [
|
||||||
{ id: 'topic_default_001', topic: 'sensor/+/temperature', qos: 1, subscribed: false },
|
{ id: 'topic_default_001', topic: 'sensor/+/temperature', qos: 1, comment: '', subscribed: false },
|
||||||
{ id: 'topic_default_002', topic: 'device/status', qos: 2, subscribed: false },
|
{ id: 'topic_default_002', topic: 'device/status', qos: 2, comment: '', subscribed: false },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -272,6 +273,7 @@ class MqttManager {
|
|||||||
id: t.id || 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6),
|
id: t.id || 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6),
|
||||||
topic: t.topic,
|
topic: t.topic,
|
||||||
qos: t.qos ?? 1,
|
qos: t.qos ?? 1,
|
||||||
|
comment: t.comment ?? '',
|
||||||
subscribed: false,
|
subscribed: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -361,6 +363,7 @@ class MqttManager {
|
|||||||
id: t.id,
|
id: t.id,
|
||||||
topic: t.topic,
|
topic: t.topic,
|
||||||
qos: t.qos,
|
qos: t.qos,
|
||||||
|
comment: t.comment ?? '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,9 +578,10 @@ class MqttManager {
|
|||||||
* @param {string} serverId - 服务器 id
|
* @param {string} serverId - 服务器 id
|
||||||
* @param {string} topic - 主题名称
|
* @param {string} topic - 主题名称
|
||||||
* @param {MqttQoS} qos - QoS 等级
|
* @param {MqttQoS} qos - QoS 等级
|
||||||
|
* @param {string} comment - 主题备注
|
||||||
* @returns {MqttTopic | null} 添加后的主题对象或 null
|
* @returns {MqttTopic | null} 添加后的主题对象或 null
|
||||||
*/
|
*/
|
||||||
addTopic(serverId, topic, qos = 1) {
|
addTopic(serverId, topic, qos = 1, comment = '') {
|
||||||
let server = this.servers.get(serverId);
|
let server = this.servers.get(serverId);
|
||||||
if (!server) {
|
if (!server) {
|
||||||
return null;
|
return null;
|
||||||
@@ -588,7 +592,7 @@ class MqttManager {
|
|||||||
|
|
||||||
let topicId = 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6);
|
let topicId = 'topic_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6);
|
||||||
|
|
||||||
let newTopic = { id: topicId, topic, qos, subscribed: false };
|
let newTopic = { id: topicId, topic, qos, comment, subscribed: false };
|
||||||
server.topics.push(newTopic);
|
server.topics.push(newTopic);
|
||||||
|
|
||||||
this.saveData();
|
this.saveData();
|
||||||
@@ -686,9 +690,10 @@ class MqttManager {
|
|||||||
* @param {string} topicId - 主题 id
|
* @param {string} topicId - 主题 id
|
||||||
* @param {string} newTopic - 新的主题名称
|
* @param {string} newTopic - 新的主题名称
|
||||||
* @param {number} newQos - 新的 QoS 等级
|
* @param {number} newQos - 新的 QoS 等级
|
||||||
|
* @param {string} comment - 新的主题备注
|
||||||
* @returns {boolean} 是否更新成功
|
* @returns {boolean} 是否更新成功
|
||||||
*/
|
*/
|
||||||
updateTopic(serverId, topicId, newTopic, newQos) {
|
updateTopic(serverId, topicId, newTopic, newQos, comment = '') {
|
||||||
|
|
||||||
let server = this.servers.get(serverId);
|
let server = this.servers.get(serverId);
|
||||||
|
|
||||||
@@ -721,6 +726,7 @@ class MqttManager {
|
|||||||
|
|
||||||
t.topic = newTopic;
|
t.topic = newTopic;
|
||||||
t.qos = newQos;
|
t.qos = newQos;
|
||||||
|
t.comment = comment;
|
||||||
|
|
||||||
// 如果已连接且此前已订阅,重新订阅新主题
|
// 如果已连接且此前已订阅,重新订阅新主题
|
||||||
if (server.status === 'connected' && wasSubscribed) {
|
if (server.status === 'connected' && wasSubscribed) {
|
||||||
@@ -733,6 +739,7 @@ class MqttManager {
|
|||||||
topic: newTopic,
|
topic: newTopic,
|
||||||
subscribed: t.subscribed,
|
subscribed: t.subscribed,
|
||||||
topicId,
|
topicId,
|
||||||
|
comment: t.comment,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,13 +20,14 @@ const api = {
|
|||||||
disconnect: (id) => ipcRenderer.invoke('mqtt:disconnect', id),
|
disconnect: (id) => ipcRenderer.invoke('mqtt:disconnect', id),
|
||||||
|
|
||||||
// 主题管理
|
// 主题管理
|
||||||
addTopic: (serverId, topic, qos) => ipcRenderer.invoke('mqtt:add-topic', serverId, topic, qos),
|
addTopic: (serverId, topic, qos, comment) =>
|
||||||
|
ipcRenderer.invoke('mqtt:add-topic', serverId, topic, qos, comment),
|
||||||
removeTopic: (serverId, topic) => ipcRenderer.invoke('mqtt:remove-topic', serverId, topic),
|
removeTopic: (serverId, topic) => ipcRenderer.invoke('mqtt:remove-topic', serverId, topic),
|
||||||
subscribe: (serverId, topic) => ipcRenderer.invoke('mqtt:subscribe', serverId, topic),
|
subscribe: (serverId, topic) => ipcRenderer.invoke('mqtt:subscribe', serverId, topic),
|
||||||
unsubscribe: (serverId, topic) => ipcRenderer.invoke('mqtt:unsubscribe', serverId, topic),
|
unsubscribe: (serverId, topic) => ipcRenderer.invoke('mqtt:unsubscribe', serverId, topic),
|
||||||
subscribeAll: (serverId) => ipcRenderer.invoke('mqtt:subscribe-all', serverId),
|
subscribeAll: (serverId) => ipcRenderer.invoke('mqtt:subscribe-all', serverId),
|
||||||
updateTopic: (serverId, topicId, topic, qos) =>
|
updateTopic: (serverId, topicId, topic, qos, comment) =>
|
||||||
ipcRenderer.invoke('mqtt:update-topic', serverId, topicId, topic, qos),
|
ipcRenderer.invoke('mqtt:update-topic', serverId, topicId, topic, qos, comment),
|
||||||
|
|
||||||
// 消息发布
|
// 消息发布
|
||||||
publish: (serverId, topic, payload, opts) =>
|
publish: (serverId, topic, payload, opts) =>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { ref, computed } from 'vue';
|
|||||||
* @property {string} serverId 所属服务器 id
|
* @property {string} serverId 所属服务器 id
|
||||||
* @property {string} topic 主题名称
|
* @property {string} topic 主题名称
|
||||||
* @property {string} [topicId] 主题 id
|
* @property {string} [topicId] 主题 id
|
||||||
|
* @property {string} [comment] 主题备注
|
||||||
* @property {boolean} subscribed 是否已订阅
|
* @property {boolean} subscribed 是否已订阅
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -57,12 +58,12 @@ import { ref, computed } from 'vue';
|
|||||||
* @property {(serverList: ServerConfig[]) => Promise<MqttServer[]>} importServers 导入服务器配置
|
* @property {(serverList: ServerConfig[]) => Promise<MqttServer[]>} importServers 导入服务器配置
|
||||||
* @property {(id: string) => Promise<boolean>} connectServer 连接服务器
|
* @property {(id: string) => Promise<boolean>} connectServer 连接服务器
|
||||||
* @property {(id: string) => Promise<void>} disconnectServer 断开服务器
|
* @property {(id: string) => Promise<void>} disconnectServer 断开服务器
|
||||||
* @property {(serverId: string, topic: string, qos: MqttQoS) => Promise<MqttTopic | null>} addTopic 添加主题
|
* @property {(serverId: string, topic: string, qos: MqttQoS, comment: string) => Promise<MqttTopic | null>} addTopic 添加主题
|
||||||
* @property {(serverId: string, topic: string) => Promise<void>} removeTopic 移除主题
|
* @property {(serverId: string, topic: string) => Promise<void>} removeTopic 移除主题
|
||||||
* @property {(serverId: string, topic: string) => Promise<boolean>} subscribeTopic 订阅主题
|
* @property {(serverId: string, topic: string) => Promise<boolean>} subscribeTopic 订阅主题
|
||||||
* @property {(serverId: string, topic: string) => Promise<boolean>} unsubscribeTopic 取消订阅主题
|
* @property {(serverId: string, topic: string) => Promise<boolean>} unsubscribeTopic 取消订阅主题
|
||||||
* @property {(serverId: string) => Promise<boolean>} subscribeAllTopics 批量订阅所有主题
|
* @property {(serverId: string) => Promise<boolean>} subscribeAllTopics 批量订阅所有主题
|
||||||
* @property {(serverId: string, topicId: string, topic: string, qos: MqttQoS) => Promise<boolean>} updateTopic 更新主题
|
* @property {(serverId: string, topicId: string, topic: string, qos: MqttQoS, comment: string) => Promise<boolean>} updateTopic 更新主题
|
||||||
* @property {(serverId: string, topic: string, payload: string, opts: PublishOptions) => Promise<boolean>} publishMessage 发布消息
|
* @property {(serverId: string, topic: string, payload: string, opts: PublishOptions) => Promise<boolean>} publishMessage 发布消息
|
||||||
* @property {() => Promise<void>} refreshMessages 刷新消息列表
|
* @property {() => Promise<void>} refreshMessages 刷新消息列表
|
||||||
* @property {() => Promise<void>} refreshPublishHistory 刷新发布历史
|
* @property {() => Promise<void>} refreshPublishHistory 刷新发布历史
|
||||||
@@ -181,6 +182,9 @@ function setupListeners() {
|
|||||||
|
|
||||||
if (t) {
|
if (t) {
|
||||||
t.subscribed = data.subscribed;
|
t.subscribed = data.subscribed;
|
||||||
|
if (typeof data.comment === 'string') {
|
||||||
|
t.comment = data.comment;
|
||||||
|
}
|
||||||
if (data.topic && data.topic !== t.topic) {
|
if (data.topic && data.topic !== t.topic) {
|
||||||
t.topic = data.topic;
|
t.topic = data.topic;
|
||||||
sortTopics(srv);
|
sortTopics(srv);
|
||||||
@@ -400,11 +404,12 @@ export function useMqttStore() {
|
|||||||
* @param {string} serverId - 服务器 id
|
* @param {string} serverId - 服务器 id
|
||||||
* @param {string} topic - 主题名称
|
* @param {string} topic - 主题名称
|
||||||
* @param {MqttQoS} qos - QoS 等级
|
* @param {MqttQoS} qos - QoS 等级
|
||||||
|
* @param {string} comment - 主题备注
|
||||||
* @returns {Promise<MqttTopic | null>} 添加后的主题对象或 null
|
* @returns {Promise<MqttTopic | null>} 添加后的主题对象或 null
|
||||||
*/
|
*/
|
||||||
async function addTopic(serverId, topic, qos) {
|
async function addTopic(serverId, topic, qos, comment) {
|
||||||
|
|
||||||
let result = await window.api.addTopic(serverId, topic, qos);
|
let result = await window.api.addTopic(serverId, topic, qos, comment);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
let srv = servers.value.find((s) => s.id === serverId);
|
let srv = servers.value.find((s) => s.id === serverId);
|
||||||
@@ -472,10 +477,11 @@ export function useMqttStore() {
|
|||||||
* @param {string} topicId - 主题 id
|
* @param {string} topicId - 主题 id
|
||||||
* @param {string} topic - 新的主题名称
|
* @param {string} topic - 新的主题名称
|
||||||
* @param {MqttQoS} qos - 新的 QoS 等级
|
* @param {MqttQoS} qos - 新的 QoS 等级
|
||||||
|
* @param {string} comment - 新的主题备注
|
||||||
* @returns {Promise<boolean>} 是否更新成功
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
*/
|
*/
|
||||||
async function updateTopic(serverId, topicId, topic, qos) {
|
async function updateTopic(serverId, topicId, topic, qos, comment) {
|
||||||
return await window.api.updateTopic(serverId, topicId, topic, qos);
|
return await window.api.updateTopic(serverId, topicId, topic, qos, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 消息发布 ====================
|
// ==================== 消息发布 ====================
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useMqttStore } from '../stores/mqtt.js';
|
import { useMqttStore } from '../stores/mqtt.js';
|
||||||
import { NCard, NSpace, NFormItem, NInput, NSelect, NButton, NEmpty, NList, NListItem, NTag, NEllipsis, useMessage } from 'naive-ui';
|
import { NCard, NSpace, NFormItem, NInput, NSelect, NButton, NEmpty, NList, NListItem, NTag, useMessage } from 'naive-ui';
|
||||||
|
|
||||||
// 全局状态与消息 API
|
// 全局状态与消息 API
|
||||||
const store = useMqttStore();
|
const store = useMqttStore();
|
||||||
@@ -10,11 +10,13 @@ const msg = useMessage();
|
|||||||
// 主题输入与 QoS 选择
|
// 主题输入与 QoS 选择
|
||||||
const topicInput = ref('');
|
const topicInput = ref('');
|
||||||
const qos = ref(2);
|
const qos = ref(2);
|
||||||
|
const comment = ref('');
|
||||||
|
|
||||||
// 编辑状态
|
// 编辑状态
|
||||||
const editingTopicId = ref(null);
|
const editingTopicId = ref(null);
|
||||||
const editingTopic = ref('');
|
const editingTopic = ref('');
|
||||||
const editingQos = ref(2);
|
const editingQos = ref(2);
|
||||||
|
const editingComment = ref('');
|
||||||
|
|
||||||
// QoS 下拉选项
|
// QoS 下拉选项
|
||||||
const qosOptions = [
|
const qosOptions = [
|
||||||
@@ -84,11 +86,12 @@ async function handleAddTopic() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await store.addTopic(srv.id, topic, qos.value);
|
let result = await store.addTopic(srv.id, topic, qos.value, comment.value.trim());
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
msg.success('主题已添加' + (srv.status === 'connected' ? '并已订阅' : ''));
|
msg.success('主题已添加' + (srv.status === 'connected' ? '并已订阅' : ''));
|
||||||
topicInput.value = '';
|
topicInput.value = '';
|
||||||
|
comment.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -173,6 +176,7 @@ function startEdit(t) {
|
|||||||
editingTopicId.value = t.id || t.topic;
|
editingTopicId.value = t.id || t.topic;
|
||||||
editingTopic.value = t.topic;
|
editingTopic.value = t.topic;
|
||||||
editingQos.value = t.qos ?? 2;
|
editingQos.value = t.qos ?? 2;
|
||||||
|
editingComment.value = t.comment ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,6 +186,7 @@ function cancelEdit() {
|
|||||||
editingTopicId.value = null;
|
editingTopicId.value = null;
|
||||||
editingTopic.value = '';
|
editingTopic.value = '';
|
||||||
editingQos.value = 2;
|
editingQos.value = 2;
|
||||||
|
editingComment.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +218,13 @@ async function handleEditSave(t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let topicId = t.id || t.topic;
|
let topicId = t.id || t.topic;
|
||||||
let result = await store.updateTopic(srv.id, topicId, topicName, editingQos.value);
|
let result = await store.updateTopic(
|
||||||
|
srv.id,
|
||||||
|
topicId,
|
||||||
|
topicName,
|
||||||
|
editingQos.value,
|
||||||
|
editingComment.value.trim()
|
||||||
|
);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
msg.success('主题已更新');
|
msg.success('主题已更新');
|
||||||
@@ -258,6 +269,16 @@ async function handleEditSave(t) {
|
|||||||
:options="qosOptions"
|
:options="qosOptions"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
<n-form-item
|
||||||
|
class="comment-field"
|
||||||
|
label="备注"
|
||||||
|
>
|
||||||
|
<n-input
|
||||||
|
v-model:value="comment"
|
||||||
|
placeholder="可选备注"
|
||||||
|
@keydown.enter="handleAddTopic"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
<n-button
|
<n-button
|
||||||
class="topic-submit"
|
class="topic-submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -329,6 +350,11 @@ async function handleEditSave(t) {
|
|||||||
:options="qosOptions"
|
:options="qosOptions"
|
||||||
style="width: 160px; flex-shrink: 0"
|
style="width: 160px; flex-shrink: 0"
|
||||||
/>
|
/>
|
||||||
|
<n-input
|
||||||
|
v-model:value="editingComment"
|
||||||
|
size="small"
|
||||||
|
placeholder="备注"
|
||||||
|
/>
|
||||||
<n-space
|
<n-space
|
||||||
:size="4"
|
:size="4"
|
||||||
:wrap="false"
|
:wrap="false"
|
||||||
@@ -352,7 +378,20 @@ async function handleEditSave(t) {
|
|||||||
v-else
|
v-else
|
||||||
class="topic-name"
|
class="topic-name"
|
||||||
>
|
>
|
||||||
<n-ellipsis>{{ t.topic }}</n-ellipsis>
|
<span
|
||||||
|
class="topic-label"
|
||||||
|
:title="t.topic"
|
||||||
|
>{{ t.topic }}</span>
|
||||||
|
<div
|
||||||
|
v-if="t.comment"
|
||||||
|
class="topic-comment"
|
||||||
|
>
|
||||||
|
<span class="topic-comment-prefix">备注</span>
|
||||||
|
<span
|
||||||
|
class="topic-comment-text"
|
||||||
|
:title="t.comment"
|
||||||
|
>{{ t.comment }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n-space
|
<n-space
|
||||||
@@ -443,12 +482,18 @@ async function handleEditSave(t) {
|
|||||||
width: 160px;
|
width: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-field {
|
||||||
|
flex: 1 1 180px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.topic-submit {
|
.topic-submit {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-field :deep(.n-form-item-blank),
|
.topic-field :deep(.n-form-item-blank),
|
||||||
.qos-field :deep(.n-form-item-blank) {
|
.qos-field :deep(.n-form-item-blank),
|
||||||
|
.comment-field :deep(.n-form-item-blank) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,12 +505,51 @@ async function handleEditSave(t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.topic-name {
|
.topic-name {
|
||||||
min-width: 0;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-family: monospace;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 5px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-label {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: monospace;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-comment {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 2px 6px;
|
||||||
|
gap: 5px;
|
||||||
|
border: 1px solid rgba(14, 165, 160, 0.16);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(20, 184, 166, 0.06);
|
||||||
|
color: var(--n-text-color-3);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-comment-prefix {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-comment-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.topic-actions {
|
.topic-actions {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
@@ -17,6 +17,8 @@ export interface MqttTopic {
|
|||||||
topic: string;
|
topic: string;
|
||||||
/** 订阅 QoS 等级 */
|
/** 订阅 QoS 等级 */
|
||||||
qos: MqttQoS;
|
qos: MqttQoS;
|
||||||
|
/** 主题备注,空字符串表示未备注 */
|
||||||
|
comment: string;
|
||||||
/** 当前是否已订阅 */
|
/** 当前是否已订阅 */
|
||||||
subscribed: boolean;
|
subscribed: boolean;
|
||||||
}
|
}
|
||||||
@@ -29,6 +31,8 @@ export interface TopicConfig {
|
|||||||
topic: string;
|
topic: string;
|
||||||
/** 订阅 QoS 等级,默认 1 */
|
/** 订阅 QoS 等级,默认 1 */
|
||||||
qos?: MqttQoS;
|
qos?: MqttQoS;
|
||||||
|
/** 主题备注,默认空字符串 */
|
||||||
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** MQTT 服务器配置(不含运行时状态) */
|
/** MQTT 服务器配置(不含运行时状态) */
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# 代码风格
|
||||||
|
|
||||||
|
## 基础格式
|
||||||
|
|
||||||
|
- 使用 UTF-8、LF 换行、2 个空格缩进;文件末尾保留换行,删除行尾空白。
|
||||||
|
- JavaScript、Vue SFC 使用分号结尾。
|
||||||
|
- 字符串通常使用单引号;模板属性和值使用双引号。
|
||||||
|
- 对象、数组尾项保留逗号。
|
||||||
|
- 复杂函数的函数体内常使用空行分隔初始化、分支和返回逻辑;保持与所在文件一致即可。
|
||||||
|
|
||||||
|
## JavaScript
|
||||||
|
|
||||||
|
- 使用 ES Module:`import` / `export`。
|
||||||
|
- 导入顺序通常为:框架或第三方依赖、项目内部模块、资源文件;不同分组之间空一行。
|
||||||
|
- 变量优先使用 `const`;需要重新赋值的局部变量使用 `let`;不使用 `var`。
|
||||||
|
- 函数以具名 `function` 声明为主;回调和简单映射使用箭头函数。
|
||||||
|
- 命名使用 camelCase:变量、函数、方法和参数均使用英文语义名称;布尔值常以 `is`、`show`、`has` 等开头。
|
||||||
|
- 常量或配置集合也多使用 camelCase,例如 `protocolOptions`、`themeOverrides`。
|
||||||
|
- 错误处理使用 `try...catch`,错误信息通过中文 `console.error` 或界面消息反馈。
|
||||||
|
|
||||||
|
## 注释与类型
|
||||||
|
|
||||||
|
- 注释、JSDoc 说明和面向用户的提示文案使用简体中文。
|
||||||
|
- 按职责使用 `// ==================== 模块名 ====================` 分隔较长的文件。
|
||||||
|
- 公开方法、重要状态和较复杂逻辑使用 JSDoc 描述用途、参数和返回值。
|
||||||
|
- JavaScript 项目通过 JSDoc 引入或声明类型,例如 `@typedef`、`@param`、`@returns`,公共 MQTT 类型集中引用 `types/jsdoc.d.ts`。
|
||||||
|
- 简单实现可配合单行中文注释说明意图,避免重复描述显而易见的代码。
|
||||||
|
|
||||||
|
## Vue 组件
|
||||||
|
|
||||||
|
- 单文件组件按 `<script setup>`、`<template>`、`<style>` 的顺序组织。
|
||||||
|
- 组件文件使用 PascalCase,例如 `ServerModal.vue`;组件在模板中同样使用 PascalCase。
|
||||||
|
- `script setup` 中通常依次放置:导入、Props/Emits、响应式状态、静态配置、侦听或生命周期、事件处理函数。
|
||||||
|
- 事件处理函数以 `handle` 开头,例如 `handleSave`;模板事件名采用 kebab-case,例如 `@edit-server`。
|
||||||
|
- Naive UI 组件使用 `NButton` 等 PascalCase 导入,在模板中写为 `n-button`。
|
||||||
|
- 模板属性一行较长时换行,每个属性单独一行,并保持 2 空格缩进。
|
||||||
|
- 样式采用普通 CSS;类名使用 kebab-case;布局和组件私有样式就近放在对应组件中。
|
||||||
|
|
||||||
|
## Electron 分层
|
||||||
|
|
||||||
|
- `src/main` 处理窗口、IPC 与 MQTT 等主进程逻辑。
|
||||||
|
- `src/preload` 统一封装并暴露 `window.api`,渲染进程不直接使用 Electron IPC。
|
||||||
|
- `src/renderer` 放置 Vue 页面、组件和状态管理;通过 `window.api` 调用主进程能力。
|
||||||
|
|
||||||
|
## ESLint
|
||||||
|
|
||||||
|
- 使用 `@electron-toolkit/eslint-config` 与 Vue 推荐规则作为基础。
|
||||||
|
- Vue 组件允许单词名称;未强制默认 Prop;模板单行或多行内容换行规则已关闭。
|
||||||
|
- 提交前执行 `pnpm lint`,并修复实际报告的问题。
|
||||||
Reference in New Issue
Block a user