json编辑器组件相关

This commit is contained in:
2026-07-04 17:57:28 +08:00
parent 50f430def2
commit 175d2e47ce
7 changed files with 1329 additions and 16 deletions
+201
View File
@@ -0,0 +1,201 @@
<template>
<div class="json-editor-page layout-padding">
<el-card shadow="hover" class="layout-padding-auto">
<template #header>
<div class="card-header">
<div class="header-left">
<span class="card-title">自描述 JSON 编辑器</span>
<span class="card-desc">每个字段的值自带类型标签约束等元数据</span>
</div>
<div class="header-actions">
<el-button size="small" @click="loadExample">加载示例</el-button>
<el-button size="small" @click="handlePasteJSON">
<el-icon><UploadFilled /></el-icon>
粘贴 JSON
</el-button>
<el-button size="small" type="primary" @click="handleCopyJSON">
<el-icon><CopyDocument /></el-icon>
复制 JSON
</el-button>
<el-button size="small" type="warning" @click="jsonDialogVisible = true">
<el-icon><View /></el-icon>
查看 JSON
</el-button>
</div>
</div>
</template>
<div class="editor-wrapper">
<JsonEditor ref="editorRef" v-model="jsonData" @change="handleChange" />
</div>
<div class="footer-bar">
<span class="change-hint" v-if="changeLog">{{ changeLog }}</span>
<span v-else class="change-hint muted">数据本身即字段定义每个字段为 {key, type, value, ...} 格式</span>
</div>
</el-card>
<el-dialog v-model="jsonDialogVisible" title="JSON 输出" width="800px" :close-on-click-modal="false" destroy-on-close>
<pre class="json-output">{{ formattedJSON }}</pre>
<template #footer>
<el-button @click="jsonDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="handleCopyJSON">
<el-icon><CopyDocument /></el-icon>
复制 JSON
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { CopyDocument, UploadFilled, View } from '@element-plus/icons-vue';
import JsonEditor from '/@/components/JsonEditor/index.vue';
const editorRef = ref<InstanceType<typeof JsonEditor>>();
const jsonDialogVisible = ref(false);
const changeLog = ref('');
const jsonData = ref<any>({
model: {
key: 'model', type: 'string', label: '模型名称',
value: 'doubao-seed-2-0-lite-260428',
required: true,
defaultValue: 'doubao-seed-2-0-lite-260428',
},
max_tokens: {
key: 'max_tokens', type: 'number', label: '最大Token',
value: 131072, defaultValue: 131072,
fieldConstraint: { min: 1, max: 131072, numberType: 'integer' },
},
messages: [
{
role: { key: 'role', type: 'string', value: 'system' },
content: '你是一个智能助手',
},
{
role: { key: 'role', type: 'string', value: 'user' },
content: [
{
type: { key: 'type', value: 'text' },
text: {
key: 'text', type: 'string', label: '用户提示词',
value: '', required: true,
fieldConstraint: { maxLength: 100000 },
},
},
{
type: { key: 'type', value: 'image_url' },
image_url: {
url: {
key: 'url', type: 'upload', label: '参考图片',
role: 'image', value: '',
fieldConstraint: { accept: 'jpg,png,webp', maxCount: 10, maxSize: 10 },
},
},
},
{
type: { key: 'type', value: 'video_url' },
video_url: {
url: {
key: 'url', type: 'upload', label: '参考视频',
role: 'video', value: '',
fieldConstraint: { accept: 'mp4,mov', maxCount: 3, maxSize: 10240 },
},
},
},
{
type: { key: 'type', value: 'input_audio' },
input_audio: {
url: {
key: 'url', type: 'upload', label: '参考音频',
role: 'audio', value: '',
fieldConstraint: { accept: 'mp3,wav', maxCount: 1, maxSize: 200 },
},
},
},
],
},
],
});
const formattedJSON = computed(() => JSON.stringify(jsonData.value, null, 2));
function loadExample() {
jsonData.value = {
model: {
key: 'model', type: 'string', label: '模型名称',
value: 'doubao-seed-2-0-lite-260428',
required: true,
defaultValue: 'doubao-seed-2-0-lite-260428',
},
max_tokens: {
key: 'max_tokens', type: 'number', label: '最大Token',
value: 131072, defaultValue: 131072,
fieldConstraint: { min: 1, max: 131072, numberType: 'integer' },
},
messages: [
{
role: { key: 'role', type: 'string', value: 'system' },
content: '你是一个智能助手',
},
{
role: { key: 'role', type: 'string', value: 'user' },
content: [
{ type: { key: 'type', value: 'text' }, text: { key: 'text', type: 'string', label: '用户提示词', value: '', required: true, fieldConstraint: { maxLength: 100000 } } },
{ type: { key: 'type', value: 'image_url' }, image_url: { url: { key: 'url', type: 'upload', label: '参考图片', role: 'image', value: '', fieldConstraint: { accept: 'jpg,png,webp', maxCount: 10, maxSize: 10 } } } },
{ type: { key: 'type', value: 'video_url' }, video_url: { url: { key: 'url', type: 'upload', label: '参考视频', role: 'video', value: '', fieldConstraint: { accept: 'mp4,mov', maxCount: 3, maxSize: 10240 } } } },
{ type: { key: 'type', value: 'input_audio' }, input_audio: { url: { key: 'url', type: 'upload', label: '参考音频', role: 'audio', value: '', fieldConstraint: { accept: 'mp3,wav', maxCount: 1, maxSize: 200 } } } },
],
},
],
};
changeLog.value = '';
ElMessage.success('已加载示例数据');
}
function handleChange(data: any) {
changeLog.value = `已更新,共 ${JSON.stringify(data).length} 字符`;
}
function handlePasteJSON() {
editorRef.value?.openPasteDialog();
}
async function handleCopyJSON() {
try {
await navigator.clipboard.writeText(formattedJSON.value);
ElMessage.success('JSON 已复制到剪贴板');
} catch { jsonDialogVisible.value = true; }
}
</script>
<style scoped lang="scss">
.json-editor-page {
.card-header {
display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px;
.header-left {
.card-title { font-size: 16px; font-weight: 600; }
.card-desc { font-size: 12px; color: #94a3b8; margin-left: 12px; }
}
.header-actions { display: flex; gap: 6px; }
}
.editor-wrapper {
min-height: 300px; max-height: 600px; overflow: auto; padding: 8px 0;
border: 1px solid #eef2f6; border-radius: 6px; background: #fff;
}
.footer-bar {
margin-top: 12px; padding: 8px 12px; background: #f8fafc; border-radius: 4px;
.change-hint { font-size: 12px; color: #0f172a;
&.muted { color: #94a3b8; }
}
}
}
.json-output {
padding: 16px; background: #1e293b; color: #e2e8f0; border-radius: 6px;
font-size: 13px; line-height: 1.6; font-family: 'Consolas', monospace;
max-height: 450px; overflow: auto; margin: 0;
}
</style>