feat: auto-format JSON display everywhere

- Shared prettyJson/truncate/isJson utility in src/utils/format.ts
- MessageStream: collapsed preview + expanded view both auto-format JSON
- LogPanel: log details with JSON content auto-formatted
- PresetList: payload preview auto-formatted
- Deduplicated isJson/formatPayload logic
This commit is contained in:
Joe Allen
2026-08-18 14:56:00 +08:00
parent 9b7094dcf2
commit 7f6a4f84d8
4 changed files with 43 additions and 19 deletions
+2 -1
View File
@@ -2,6 +2,7 @@
import { ref, computed, watch, nextTick } from 'vue'
import { useLogs } from '../../stores/useLogs'
import { mqttGetLogDir } from '../../stores/useMqttBridge'
import { prettyJson } from '../../utils/format'
import { ElMessage } from 'element-plus'
const { logs, clear } = useLogs()
@@ -121,7 +122,7 @@ function eventIcon(event: string): string {
<span v-if="log.details" class="log-dtl-btn" @click="toggleDetails(log.id)">{{ expandedDetails.has(log.id) ? '收起' : '详情' }}</span>
</div>
<div v-if="log.details && expandedDetails.has(log.id)" class="log-dtl-content">
{{ log.details }}
{{ prettyJson(log.details) }}
</div>
</div>
</div>
+6 -17
View File
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue'
import type { MqttMessage } from '../../types/mqtt'
import { prettyJson, isJson, truncate } from '../../utils/format'
const props = defineProps<{
messages: MqttMessage[]
@@ -50,11 +51,6 @@ function onScroll() {
// ─── Payload Format Helpers ────────────────────────────────────
function isJson(str: string): boolean {
try { JSON.parse(str); return true }
catch { return false }
}
function getEffectiveFormat(msg: MqttMessage): string {
const fmt = messageFormats.value[msg.id] || 'auto'
if (fmt === 'auto') return isJson(msg.payload) ? 'json' : 'plain'
@@ -64,18 +60,11 @@ function getEffectiveFormat(msg: MqttMessage): string {
function formatPayload(msg: MqttMessage): string {
const fmt = getEffectiveFormat(msg)
switch (fmt) {
case 'json':
try {
const parsed = JSON.parse(msg.payload)
return JSON.stringify(parsed, null, 2)
} catch { return msg.payload }
case 'hex':
return bytesToHex(new TextEncoder().encode(msg.payload))
case 'base64':
return btoa(msg.payload)
case 'json': return prettyJson(msg.payload)
case 'hex': return bytesToHex(new TextEncoder().encode(msg.payload))
case 'base64': return btoa(msg.payload)
case 'plain':
default:
return msg.payload
default: return msg.payload
}
}
@@ -180,7 +169,7 @@ function formatTime(ts: number): string {
v-if="expandedPayloads.has(msg.id)"
:class="['payload-full', { 'is-json': getEffectiveFormat(msg) === 'json' }]"
>{{ formatPayload(msg) }}</pre>
<pre v-else class="payload-truncated">{{ msg.payload.substring(0, 200) }}{{ msg.payload.length > 200 ? '...' : '' }}</pre>
<pre v-else class="payload-truncated">{{ truncate(prettyJson(msg.payload), 200) }}</pre>
</div>
</div>
</div>
+2 -1
View File
@@ -3,6 +3,7 @@ import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { Preset } from '../../types/mqtt'
import { usePresets } from '../../stores/usePresets'
import { prettyJson, truncate } from '../../utils/format'
import PresetForm from './PresetForm.vue'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -76,7 +77,7 @@ const qosLabel = (qos: number) => `QoS ${qos}`
</el-table-column>
<el-table-column label="Payload 预览" min-width="200">
<template #default="{ row }">
<span class="payload-preview">{{ row.payloadTemplate.substring(0, 50) }}{{ row.payloadTemplate.length > 50 ? '...' : '' }}</span>
<span class="payload-preview">{{ truncate(prettyJson(row.payloadTemplate), 50) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
+33
View File
@@ -0,0 +1,33 @@
/**
* Pretty-print a JSON string if it's valid JSON.
* Returns the original string if it's not valid JSON.
*/
export function prettyJson(str: string): string {
if (!str || str.trim() === '') return str
try {
const parsed = JSON.parse(str)
return JSON.stringify(parsed, null, 2)
} catch {
return str
}
}
/**
* Check if a string is valid JSON.
*/
export function isJson(str: string): boolean {
try {
JSON.parse(str)
return true
} catch {
return false
}
}
/**
* Truncate a string to a maximum length, adding an ellipsis if truncated.
*/
export function truncate(str: string, maxLen: number): string {
if (str.length <= maxLen) return str
return str.substring(0, maxLen) + '...'
}