feat: 优化过滤主题功能,支持下拉选择
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useMqttStore } from '../stores/mqtt.js';
|
||||
import { NCard, NSpace, NInput, NButton, NEmpty, useMessage } from 'naive-ui';
|
||||
import { NCard, NSelect, NButton, NEmpty, useMessage } from 'naive-ui';
|
||||
|
||||
// 全局状态与消息 API
|
||||
const store = useMqttStore();
|
||||
const message = useMessage();
|
||||
|
||||
// 主题过滤关键字
|
||||
const filter = ref('');
|
||||
// 已选中的过滤主题列表
|
||||
const filter = ref([]);
|
||||
|
||||
/**
|
||||
* 复制主题到剪贴板
|
||||
@@ -24,22 +24,65 @@ async function copyTopic(topic) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过滤关键字筛选后的消息列表
|
||||
* 可过滤的主题选项(来自已配置主题和已接收消息)
|
||||
* @type {import('vue').ComputedRef<Array<{label: string, value: string}>>}
|
||||
*/
|
||||
const topicOptions = computed(() => {
|
||||
|
||||
let server = store.activeServer.value;
|
||||
let serverTopics = server?.topics?.map((t) => t.topic) || [];
|
||||
let messageTopics = store.messages.value.map((m) => m.topic);
|
||||
let all = [...new Set([...serverTopics, ...messageTopics])];
|
||||
|
||||
all.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
return all.map((topic) => ({ label: topic, value: topic }));
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* 判断消息主题是否匹配指定的过滤主题(支持 + 单层通配符与 # 多层通配符)
|
||||
* @param {string} filterTopic - 过滤主题,可包含 + 或 #
|
||||
* @param {string} messageTopic - 消息主题
|
||||
* @returns {boolean} 是否匹配
|
||||
*/
|
||||
function topicMatchesFilter(filterTopic, messageTopic) {
|
||||
|
||||
if (filterTopic === '#') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filterTopic.endsWith('/#')) {
|
||||
let prefix = filterTopic.slice(0, -2);
|
||||
return messageTopic === prefix || messageTopic.startsWith(prefix + '/');
|
||||
}
|
||||
|
||||
let filterParts = filterTopic.split('/');
|
||||
let messageParts = messageTopic.split('/');
|
||||
|
||||
if (filterParts.length !== messageParts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filterParts.every((part, index) => part === '+' || part === messageParts[index]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据选中主题筛选后的消息列表(支持 + / # 通配符)
|
||||
* @type {import('vue').ComputedRef<Array<Object>>}
|
||||
*/
|
||||
const filteredMessages = computed(() => {
|
||||
|
||||
if (!filter.value.trim()) {
|
||||
if (!filter.value || filter.value.length === 0) {
|
||||
return store.messages.value;
|
||||
}
|
||||
|
||||
let f = filter.value.trim().toLowerCase();
|
||||
return store.messages.value.filter((m) =>
|
||||
filter.value.some((f) => topicMatchesFilter(f, m.topic))
|
||||
);
|
||||
|
||||
return store.messages.value.filter((m) => {
|
||||
return m.topic.toLowerCase().includes(f);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* 清空当前活动服务器的消息列表
|
||||
@@ -79,10 +122,8 @@ function formatPayload(payload) {
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<n-space
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
<div
|
||||
style="display: flex; flex-direction: column; gap: 8px; width: 100%"
|
||||
>
|
||||
<div>
|
||||
<span>消息监控</span>
|
||||
@@ -90,20 +131,26 @@ function formatPayload(payload) {
|
||||
style="font-size: 12px; opacity: 0.55; margin-left: 8px"
|
||||
>实时查看接收和发送的 MQTT 消息</span>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-input
|
||||
<div
|
||||
style="display: flex; align-items: center; gap: 8px; width: 100%"
|
||||
>
|
||||
<n-select
|
||||
v-model:value="filter"
|
||||
size="small"
|
||||
placeholder="过滤主题..."
|
||||
style="width: 180px"
|
||||
style="flex: 1 1 auto; max-width: 480px; min-width: 0"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
max-tag-count="responsive"
|
||||
:options="topicOptions"
|
||||
/>
|
||||
<n-button
|
||||
size="small"
|
||||
@click="handleClear"
|
||||
>清空消息</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<n-empty
|
||||
v-if="filteredMessages.length === 0"
|
||||
|
||||
Reference in New Issue
Block a user