feat(工具箱): 添加“JSON 格式化”工具

This commit is contained in:
2025-02-04 17:36:21 +08:00
parent 6fdc510f60
commit 6e60f4a67c
2 changed files with 245 additions and 2 deletions

View File

@@ -90,12 +90,12 @@ export const toolList = [
{ {
id: 'edit-tools', id: 'edit-tools',
title: '编辑', title: '编辑',
enabled: false, enabled: true,
items: [ items: [
{ {
id: 'csv-editor', id: 'csv-editor',
component: 'Edit/CsvEditor', component: 'Edit/CsvEditor',
title: 'CSV 编辑工具', title: 'CSV 编辑',
iconClass: 'mdi mdi-table-edit', iconClass: 'mdi mdi-table-edit',
desc: '查看或编辑 CSV 文件', desc: '查看或编辑 CSV 文件',
createdAt: '', createdAt: '',
@@ -103,6 +103,17 @@ export const toolList = [
version: '0', version: '0',
enabled: false, enabled: false,
}, },
{
id: 'json-formatter',
component: 'Edit/JsonFormatter',
title: 'JSON 格式化',
iconClass: 'mdi mdi-code-json',
desc: '格式化 / 美化 JSON 字符串',
createdAt: '2025-02-04',
updatedAt: '2025-02-04',
version: '1',
enabled: true,
},
], ],
}, },
{ {

View File

@@ -0,0 +1,232 @@
<template>
<div class="tool-detail-page">
<!-- 设置 -->
<n-card size="small" title="设置">
<n-flex>
<!-- 缩进空格 -->
<div class="config-item">
<div class="config-item__label">缩进空格</div>
<div class="config-item__content">
<n-input-number
v-model:value="data.indentSize"
:min="0"
:max="8"
:precision="0"
:step="1"
></n-input-number>
</div>
</div>
<!-- 排序属性 -->
<div class="config-item">
<div class="config-item__label">排序属性</div>
<div class="config-item__content">
<n-switch v-model:value="data.enabledSort" />
</div>
</div>
</n-flex>
</n-card>
<!-- 操作 -->
<n-card size="small" title="操作">
<n-flex>
<n-button
type="primary"
:disabled="!data.jsonInput"
@click="formatJson"
>格式化</n-button>
<n-button
type="primary"
:disabled="!data.jsonOutput"
@click="copyOutputs"
>复制结果</n-button>
<n-button
type="error"
:disabled="!data.jsonInput"
@click="clearInputs"
>清空输入</n-button>
<n-button
type="error"
:disabled="!data.jsonOutput"
@click="clearOutputs"
>清空输出</n-button>
</n-flex>
</n-card>
<!-- 输入内容 -->
<n-card size="small" title="输入内容">
<n-input
v-model:value="data.jsonInput"
class="json-input"
placeholder="请输入 JSON 字符串"
type="textarea"
:rows="8"
@contextmenu.stop
></n-input>
</n-card>
<!-- 输出内容 -->
<n-card size="small" title="输出内容">
<n-code
class="json-output"
language="json"
:code="data.jsonOutput"
:show-line-numbers="true"
@contextmenu.stop
/>
</n-card>
</div>
</template>
<script setup>
import {
NButton, NCard, NCode, NFlex,
NInput, NInputNumber, NSwitch,
} from 'naive-ui';
import {
reactive,
} from 'vue';
import {
compareString, isArray, isObject,
} from '@frost-utils/javascript/common/index';
import {
useClipboard,
} from '@vueuse/core';
import {
$message, $notification,
} from '@/assets/js/naive-ui';
/** 剪贴板 */
const clipboard = useClipboard({
legacy: true,
read: false,
});
/** 数据 */
const data = reactive({
/** 启用排序 */
enabledSort: true,
/** 缩进空格 */
indentSize: 2,
/** 输入内容 */
jsonInput: '',
/** 输出内容 */
jsonOutput: '',
});
/** 清空输入内容 */
function clearInputs() {
data.jsonInput = '';
}
/** 清空输出内容 */
function clearOutputs() {
data.jsonOutput = '';
}
/** 复制格式化结果 */
function copyOutputs() {
if (clipboard.isSupported) {
return clipboard.copy(data.jsonOutput).then(() => {
$message.success('复制成功');
}).catch((error) => {
console.error('复制失败:');
console.error(error);
$message.error('复制失败:异常');
});
} else {
$message.error('复制失败:当前浏览器不支持该操作');
return Promise.resolve();
}
}
/** 格式化输入的 JSON */
function formatJson() {
try {
let obj = JSON.parse(data.jsonInput);
// 排序
if (data.enabledSort) {
obj = sortObjectKeys(obj);
}
data.jsonOutput = JSON.stringify(obj, null, data.indentSize);
} catch (error) {
console.warn('格式化 JSON 失败:');
console.warn(error);
$notification.create({
content: String(error),
duration: 0,
title: '格式化 JSON 失败',
type: 'error',
});
data.jsonOutput = '';
}
}
/** 排序对象的 key */
function sortObjectKeys(obj) {
// 非对象直接返回
if (!isArray(obj) && !isObject(obj)) {
return obj;
}
// 处理数组,递归处理每个元素
if (isArray(obj)) {
return obj.map((item) => sortObjectKeys(item));
}
// 获取并排序 key
let sortedKeys = Object.keys(obj).sort((a, b) => {
return compareString(a, b);
});
// 排序后的对象
let sortedObj = {};
// 按顺序获取值 & 递归处理
for (let key of sortedKeys) {
sortedObj[key] = sortObjectKeys(obj[key]);
}
return sortedObj;
}
</script>
<style lang="less" scoped>
.config-item {
display: flex;
align-items: center;
.n-input-number {
width: 128px;
}
}
.json-input {
font-family: monospace;
}
.json-output {
user-select: text;
:deep(pre) {
overflow-x: auto;
}
}
</style>