Build lightweight AI agent admin

This commit is contained in:
Codex
2026-06-08 18:14:59 +08:00
commit e164840f43
2530 changed files with 435693 additions and 0 deletions
@@ -0,0 +1,295 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ElAlert, ElCard, ElTabPane, ElTabs } from 'element-plus';
import { JsonEditor } from './index';
// 示例 1: 基础用法
const basicJson = ref(
'{\n "name": "John Doe",\n "age": 30,\n "email": "john@example.com",\n "active": true\n}',
);
const basicIsValid = ref(true);
// 示例 2: 对象初始化
const objectJson = ref({
id: 1,
username: 'alice',
roles: ['admin', 'user'],
metadata: {
lastLogin: '2025-11-13',
loginCount: 42,
},
});
// 示例 3: 只读模式
const readOnlyJson = ref({
status: 'success',
code: 200,
message: 'Operation completed successfully',
data: {
id: 123,
title: 'Example Data',
},
});
// 示例 4: 配置编辑
const configJson = ref({
theme: 'dark',
language: 'zh-CN',
notifications: {
enabled: true,
sound: false,
desktop: true,
},
privacy: {
profilePublic: false,
showOnlineStatus: true,
},
});
// 事件处理
function handleValid() {
basicIsValid.value = true;
}
function handleInvalid(error: string) {
basicIsValid.value = false;
console.error('JSON 无效:', error);
}
</script>
<template>
<div class="json-editor-example">
<h1>JSON Editor 组件示例</h1>
<ElTabs>
<!-- 示例 1: 基础用法 -->
<ElTabPane label="基础用法">
<ElCard class="box-card">
<template #header>
<div class="card-header">
<span>简单的 JSON 编辑器</span>
<span class="status" :class="{ valid: basicIsValid }">
{{ basicIsValid ? '✓ 有效' : '✗ 无效' }}
</span>
</div>
</template>
<JsonEditor
v-model="basicJson"
placeholder="输入或粘贴 JSON 内容"
@valid="handleValid"
@invalid="handleInvalid"
/>
</ElCard>
</ElTabPane>
<!-- 示例 2: 对象初始化 -->
<ElTabPane label="对象初始化">
<ElCard class="box-card">
<template #header>
<span>直接传入对象进行初始化</span>
</template>
<p class="description">
直接传入 JavaScript 对象组件会自动转换为 JSON
字符串并以指定的缩进格式显示
</p>
<JsonEditor
:model-value="objectJson"
:indent="2"
@change="(json) => console.log('Changed:', json)"
/>
</ElCard>
</ElTabPane>
<!-- 示例 3: 只读模式 -->
<ElTabPane label="只读模式">
<ElCard class="box-card">
<template #header>
<span>只读模式 - 用于展示 API 响应</span>
</template>
<p class="description">
设置 readonly 属性为 true可以创建只读的 JSON 查看器适合展示 API
响应或配置信息
</p>
<JsonEditor
:model-value="readOnlyJson"
readonly
:show-format-button="false"
line-numbers
/>
</ElCard>
</ElTabPane>
<!-- 示例 4: 配置编辑 -->
<ElTabPane label="配置编辑">
<ElCard class="box-card">
<template #header>
<span>应用配置编辑</span>
</template>
<p class="description">
用于编辑应用配置文件支持格式化压缩等功能
</p>
<JsonEditor
:model-value="configJson"
:min-height="300"
:max-height="500"
placeholder="编辑配置信息"
highlight-syntax
/>
</ElCard>
</ElTabPane>
<!-- 示例 5: 功能介绍 -->
<ElTabPane label="功能介绍">
<div class="features">
<h3>🎯 主要功能</h3>
<div class="feature-group">
<h4> 编辑功能</h4>
<ul>
<li>📝 实时 JSON 编辑</li>
<li>🔍 JSON 验证与错误提示</li>
<li>🎨 语法高亮显示</li>
<li>📊 行号显示</li>
</ul>
</div>
<div class="feature-group">
<h4>🛠 工具功能</h4>
<ul>
<li> 格式化 JSON (Ctrl+Shift+F)</li>
<li>📦 压缩 JSON</li>
<li>📋 复制到剪贴板</li>
<li>🗑 清空内容</li>
</ul>
</div>
<div class="feature-group">
<h4> 配置选项</h4>
<ul>
<li>🔒 只读模式</li>
<li>🚫 禁用编辑</li>
<li>📏 自定义高度</li>
<li>🎛 缩进控制</li>
</ul>
</div>
<div class="feature-group">
<h4>📡 事件系统</h4>
<ul>
<li> valid - JSON 有效时触发</li>
<li> invalid - JSON 无效时触发</li>
<li>🔄 change - 内容变化时触发</li>
<li>📤 update:modelValue - v-model 更新</li>
</ul>
</div>
<ElAlert
title="快捷键"
type="info"
:closable="false"
description="按下 Ctrl+Shift+F (Windows/Linux) 或 Cmd+Shift+F (Mac) 快速格式化 JSON"
/>
</div>
</ElTabPane>
</ElTabs>
</div>
</template>
<style scoped lang="scss">
.json-editor-example {
max-width: 1200px;
padding: 20px;
margin: 0 auto;
h1 {
margin-bottom: 20px;
color: hsl(var(--foreground));
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.status {
padding: 4px 8px;
font-size: 12px;
font-weight: 600;
color: hsl(var(--destructive));
background-color: hsl(var(--destructive) / 10%);
border-radius: 4px;
transition: all 0.2s ease;
&.valid {
color: hsl(var(--primary));
background-color: hsl(var(--primary) / 10%);
}
}
}
.box-card {
margin-bottom: 20px;
:deep(.el-card__body) {
padding: 20px;
}
}
.description {
margin-bottom: 16px;
font-size: 14px;
color: hsl(var(--muted-foreground));
}
.features {
padding: 20px;
h3 {
margin-bottom: 20px;
font-size: 18px;
color: hsl(var(--foreground));
}
.feature-group {
margin-bottom: 24px;
h4 {
margin-bottom: 12px;
font-size: 14px;
font-weight: 600;
color: hsl(var(--primary));
}
ul {
padding-left: 0;
list-style: none;
li {
padding: 8px 0;
font-size: 14px;
color: hsl(var(--foreground));
&::before {
margin-right: 8px;
content: '';
}
}
}
}
:deep(.el-alert) {
margin-top: 20px;
}
}
}
</style>
@@ -0,0 +1,2 @@
export { default as JsonEditor } from './json-editor.vue';
export * from './types';
@@ -0,0 +1,820 @@
<script lang="ts" setup>
import type {
JsonEditorEmits,
JsonEditorProps,
JsonValidationResult,
} from './types';
import { computed, onMounted, ref, watch } from 'vue';
import { $t } from '@vben/locales';
import { ElButton, ElMessage, ElTooltip } from 'element-plus';
defineOptions({
name: 'JsonEditor',
inheritAttrs: false,
});
const props = withDefaults(defineProps<Props>(), {
readonly: false,
disabled: false,
mode: 'simple',
indent: 4,
placeholder: () => '输入或粘贴 JSON',
lineNumbers: true,
minHeight: 400,
maxHeight: 500,
autoFormat: true,
highlightSyntax: true,
showFormatButton: true,
showValidation: true,
});
const emit = defineEmits<JsonEditorEmits>();
interface Props extends JsonEditorProps {}
// 编辑器内容
const content = ref('');
const textareaRef = ref<HTMLTextAreaElement>();
const lineNumbersRef = ref<HTMLDivElement>();
// 验证结果
const validationResult = ref<JsonValidationResult>({ valid: true });
const isFormatting = ref(false);
// 初始化内容
onMounted(() => {
if (props.modelValue) {
const initialValue =
typeof props.modelValue === 'string'
? props.modelValue
: JSON.stringify(props.modelValue, null, props.indent);
content.value = initialValue;
validateJson(initialValue);
}
syncLineNumbers();
});
// 监听 modelValue 变化
watch(
() => props.modelValue,
(newValue) => {
if (newValue !== undefined) {
const stringValue =
typeof newValue === 'string'
? newValue
: JSON.stringify(newValue, null, props.indent);
if (stringValue !== content.value) {
content.value = stringValue;
validateJson(stringValue);
syncLineNumbers();
}
}
},
);
/**
* 验证 JSON 的有效性
*/
function validateJson(jsonString: string): JsonValidationResult {
try {
if (!jsonString.trim()) {
validationResult.value = { valid: true };
return { valid: true };
}
JSON.parse(jsonString);
validationResult.value = { valid: true };
emit('valid');
return { valid: true };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '格式错误';
validationResult.value = { valid: false, error: errorMessage };
emit('invalid', errorMessage);
return { valid: false, error: errorMessage };
}
}
/**
* 格式化 JSON
*/
function formatJson() {
if (!content.value.trim()) {
ElMessage.warning($t('common.jsonEditor.emptyContent'));
return;
}
try {
isFormatting.value = true;
const parsed = JSON.parse(content.value);
const formatted = JSON.stringify(parsed, null, props.indent);
content.value = formatted;
validateJson(formatted);
syncLineNumbers();
ElMessage.success($t('common.jsonEditor.formatSuccess'));
} catch (error) {
const errorMessage =
error instanceof Error
? error.message
: $t('common.jsonEditor.invalidJson');
ElMessage.error($t('common.jsonEditor.formatFailed', [errorMessage]));
} finally {
isFormatting.value = false;
}
}
/**
* 压缩 JSON
*/
function compressJson() {
if (!content.value.trim()) {
ElMessage.warning($t('common.jsonEditor.emptyContent'));
return;
}
try {
const parsed = JSON.parse(content.value);
const compressed = JSON.stringify(parsed);
content.value = compressed;
validateJson(compressed);
syncLineNumbers();
ElMessage.success($t('common.jsonEditor.compressSuccess'));
} catch (error) {
const errorMessage =
error instanceof Error
? error.message
: $t('common.jsonEditor.invalidJson');
ElMessage.error($t('common.jsonEditor.compressFailed', [errorMessage]));
}
}
/**
* 复制到剪贴板
*/
function copyToClipboard() {
if (!content.value) {
ElMessage.warning($t('common.jsonEditor.noContent'));
return;
}
navigator.clipboard
.writeText(content.value)
.then(() => {
ElMessage.success($t('common.jsonEditor.copiedSuccess'));
})
.catch(() => {
ElMessage.error($t('common.jsonEditor.copyFailed'));
});
}
/**
* 清空内容
*/
function clearContent() {
content.value = '';
validationResult.value = { valid: true };
emit('update:modelValue', '');
emit('change', '');
}
/**
* 同步行号
*/
function syncLineNumbers() {
if (!lineNumbersRef.value || !textareaRef.value) return;
const lines = content.value.split('\n').length;
const lineNumbersHtml = Array.from({ length: lines }, (_, i) => i + 1).join(
'<br>',
);
lineNumbersRef.value.innerHTML = lineNumbersHtml;
}
/**
* 处理输入事件
*/
function handleInput(event: Event) {
const target = event.target as HTMLTextAreaElement;
content.value = target.value;
syncLineNumbers();
validateJson(content.value);
emit('update:modelValue', content.value);
emit('change', content.value);
}
/**
* 处理滚动事件
*/
function handleScroll(event: Event) {
const target = event.target as HTMLTextAreaElement;
if (lineNumbersRef.value) {
lineNumbersRef.value.scrollTop = target.scrollTop;
lineNumbersRef.value.scrollLeft = target.scrollLeft;
}
}
/**
* 处理按键事件
*/
function handleKeydown(event: KeyboardEvent) {
// Ctrl/Cmd + Shift + F 格式化
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === 'F') {
event.preventDefault();
formatJson();
}
}
/**
* 获取行号容器样式
*/
const lineNumbersStyle = computed(() => ({
minHeight: `${props.minHeight}px`,
maxHeight: `${props.maxHeight}px`,
}));
/**
* 获取编辑器容器样式
*/
const editorStyle = computed(() => ({
minHeight: `${props.minHeight}px`,
maxHeight: `${props.maxHeight}px`,
}));
/**
* 获取验证状态类
*/
const validationClass = computed(() => {
if (!props.showValidation) return '';
return validationResult.value.valid ? 'is-valid' : 'is-invalid';
});
/**
* 获取验证错误信息
*/
const errorMessage = computed(() => {
return validationResult.value.error || '';
});
// 暴露方法
defineExpose({
formatJson,
compressJson,
copyToClipboard,
clearContent,
validateJson,
getContent: () => content.value,
getParsedJson: () => JSON.parse(content.value),
});
</script>
<script lang="ts">
/**
* JSON 语法高亮函数
*/
function highlightJsonSyntax(text: string): string {
if (!text) return '';
return (
text
// 转义 HTML 特殊字符
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
// 高亮字符串
.replaceAll(
/("(?:\\.|[^"\\])*"(?=\s*[,:}\]]))/g,
'<span class="json-string">$1</span>',
)
// 高亮数字
.replaceAll(
/(?<=[:[\s,])(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:e[+-]?\d+)?)\b/gi,
'<span class="json-number">$1</span>',
)
// 高亮布尔值
.replaceAll(/\b(true|false)\b/g, '<span class="json-boolean">$1</span>')
// 高亮 null
.replaceAll(/\bnull\b/g, '<span class="json-null">$1</span>')
// 高亮冒号和逗号
.replaceAll(/(:)/g, '<span class="json-colon">$1</span>')
.replaceAll(/(,)/g, '<span class="json-comma">$1</span>')
// 高亮括号
.replaceAll(/([{}[\]])/g, '<span class="json-bracket">$1</span>')
);
}
</script>
<template>
<!-- 简洁模式 -->
<div
v-if="mode === 'simple'"
class="json-editor-simple"
:class="{ 'is-disabled': disabled, 'is-readonly': readonly }"
>
<div class="json-editor-simple-wrapper">
<textarea
ref="textareaRef"
class="json-textarea-simple"
:value="content"
:placeholder="placeholder"
:readonly="readonly"
:disabled="disabled"
spellcheck="false"
@input="handleInput"
@keydown="handleKeydown"
></textarea>
<pre
v-if="highlightSyntax"
class="json-highlight-simple"
aria-hidden="true"
><code v-html="highlightJsonSyntax(content)"></code></pre>
</div>
</div>
<!-- 完整模式 -->
<div
v-else
class="json-editor"
:class="[
validationClass,
{ 'is-readonly': readonly, 'is-disabled': disabled },
]"
>
<!-- 工具栏 -->
<div class="json-editor-toolbar">
<div class="toolbar-left">
<span
v-if="showValidation"
class="status-indicator"
:class="{ 'is-valid': validationResult.valid }"
>
{{
validationResult.valid
? $t('common.jsonEditor.valid')
: $t('common.jsonEditor.invalid')
}}
</span>
<span
v-if="!validationResult.valid && errorMessage"
class="error-message"
>
{{ errorMessage }}
</span>
</div>
<div class="toolbar-right">
<ElTooltip
:content="`${$t('common.jsonEditor.format')} (Ctrl+Shift+F)`"
>
<ElButton
v-if="showFormatButton"
link
type="primary"
size="small"
:disabled="disabled || readonly || isFormatting"
@click="formatJson"
>
{{ $t('common.jsonEditor.format') }}
</ElButton>
</ElTooltip>
<ElTooltip :content="$t('common.jsonEditor.compress')">
<ElButton
link
type="primary"
size="small"
:disabled="disabled || readonly"
@click="compressJson"
>
{{ $t('common.jsonEditor.compress') }}
</ElButton>
</ElTooltip>
<ElTooltip :content="$t('common.jsonEditor.copy')">
<ElButton
link
type="primary"
size="small"
:disabled="disabled"
@click="copyToClipboard"
>
{{ $t('common.jsonEditor.copy') }}
</ElButton>
</ElTooltip>
<ElTooltip :content="$t('common.jsonEditor.clear')">
<ElButton
link
type="danger"
size="small"
:disabled="disabled || readonly"
@click="clearContent"
>
{{ $t('common.jsonEditor.clear') }}
</ElButton>
</ElTooltip>
</div>
</div>
<!-- 编辑器容器 -->
<div class="json-editor-container" :style="editorStyle">
<!-- 行号 -->
<div
v-if="lineNumbers"
ref="lineNumbersRef"
class="line-numbers"
:style="lineNumbersStyle"
></div>
<!-- 文本区域 -->
<textarea
ref="textareaRef"
class="json-textarea"
:value="content"
:placeholder="placeholder"
:readonly="readonly"
:disabled="disabled"
:style="editorStyle"
spellcheck="false"
@input="handleInput"
@scroll="handleScroll"
@keydown="handleKeydown"
></textarea>
<!-- 语法高亮层仅用于视觉效果 -->
<pre
v-if="highlightSyntax"
class="json-highlight"
:style="editorStyle"
aria-hidden="true"
><code v-html="highlightJsonSyntax(content)"></code></pre>
</div>
<!-- 统计信息 -->
<div v-if="showValidation" class="json-editor-footer">
<span class="stats">
{{
$t('common.jsonEditor.stats', [
content.split('\n').length,
content.length,
])
}}
</span>
</div>
</div>
</template>
<style lang="scss" scoped>
.json-editor {
display: flex;
flex-direction: column;
width: 100%;
overflow: hidden;
background-color: hsl(var(--background));
border: 1px solid hsl(var(--border));
border-radius: var(--radius);
transition: all 0.2s ease;
&.is-invalid {
border-color: hsl(var(--destructive));
.json-textarea,
.json-highlight {
border-color: hsl(var(--destructive));
}
}
&.is-valid {
border-color: hsl(var(--primary));
}
&.is-readonly {
background-color: hsl(var(--background-deep) / 50%);
}
&.is-disabled {
cursor: not-allowed;
opacity: 0.6;
.json-textarea {
cursor: not-allowed;
}
}
}
.json-editor-toolbar {
display: flex;
flex-shrink: 0;
gap: 12px;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: linear-gradient(
90deg,
hsl(var(--background)) 0%,
hsl(var(--background-deep) / 30%) 100%
);
border-bottom: 1px solid hsl(var(--border));
.toolbar-left {
display: flex;
flex: 1;
gap: 12px;
align-items: center;
}
.toolbar-right {
display: flex;
flex-shrink: 0;
gap: 4px;
align-items: center;
:deep(.el-button) {
padding: 4px 8px;
font-size: 12px;
}
}
.status-indicator {
padding: 4px 12px;
font-size: 12px;
font-weight: 600;
color: hsl(var(--muted-foreground));
white-space: nowrap;
background-color: hsl(var(--background-deep));
border-radius: 4px;
transition: all 0.2s ease;
&.is-valid {
color: hsl(var(--primary));
background-color: hsl(var(--primary) / 10%);
}
}
.error-message {
margin-left: 8px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
color: hsl(var(--destructive));
white-space: nowrap;
}
}
.json-editor-container {
position: relative;
display: flex;
flex: 1;
overflow: hidden;
border-bottom: 1px solid hsl(var(--border));
}
.line-numbers {
position: relative;
flex-shrink: 0;
width: 50px;
padding: 8px 12px;
overflow: hidden;
font-family: 'Courier New', monospace;
font-size: 12px;
line-height: 1.6;
color: hsl(var(--muted-foreground));
text-align: right;
user-select: none;
background-color: hsl(var(--background-deep) / 50%);
border-right: 1px solid hsl(var(--border));
br {
display: block;
height: 1.6em;
}
}
.json-textarea {
flex: 1;
padding: 8px 12px;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
color: hsl(var(--foreground));
word-wrap: break-word;
tab-size: 2;
white-space: pre;
caret-color: hsl(var(--primary));
resize: none;
outline: none;
background-color: transparent;
border: none;
&::placeholder {
color: hsl(var(--muted-foreground) / 50%);
}
&:disabled {
cursor: not-allowed;
opacity: 0.6;
}
&:read-only {
cursor: default;
}
}
.json-highlight {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 8px 12px;
margin: 0;
overflow: hidden;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
color: transparent;
word-wrap: break-word;
tab-size: 2;
white-space: pre;
pointer-events: none;
background-color: transparent;
border: none;
code {
display: block;
color: transparent;
}
:deep(.json-string) {
color: hsl(var(--primary));
}
:deep(.json-number) {
color: hsl(var(--accent));
}
:deep(.json-boolean) {
color: hsl(var(--chart-2));
}
:deep(.json-null) {
font-weight: 600;
color: hsl(var(--muted-foreground));
}
:deep(.json-colon) {
color: hsl(var(--foreground));
}
:deep(.json-comma) {
color: hsl(var(--foreground));
}
:deep(.json-bracket) {
font-weight: 600;
color: hsl(var(--muted-foreground));
}
}
.json-editor-footer {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
padding: 8px 16px;
background-color: hsl(var(--background-deep) / 30%);
border-top: 1px solid hsl(var(--border));
.stats {
font-size: 12px;
color: hsl(var(--muted-foreground));
}
}
/* ============= 简洁模式样式 ============= */
.json-editor-simple {
position: relative;
width: 100%;
background-color: hsl(var(--background));
border: 1px solid hsl(var(--border));
border-radius: var(--radius);
transition: all 0.2s ease;
&:hover {
border-color: hsl(var(--primary) / 50%);
}
&:focus-within {
border-color: hsl(var(--primary));
box-shadow: 0 0 0 2px hsl(var(--primary) / 10%);
}
&.is-readonly {
cursor: default;
background-color: hsl(var(--background-deep) / 50%);
}
&.is-disabled {
cursor: not-allowed;
background-color: hsl(var(--background-deep));
opacity: 0.6;
}
}
.json-editor-simple-wrapper {
position: relative;
width: 100%;
min-height: 40px;
overflow: hidden;
}
.json-textarea-simple {
position: relative;
z-index: 2;
width: 100%;
padding: 8px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
color: hsl(var(--foreground));
word-break: break-all;
word-wrap: break-word;
tab-size: 2;
white-space: pre-wrap;
caret-color: hsl(var(--primary));
resize: none;
outline: none;
background-color: transparent;
border: none;
&::placeholder {
color: hsl(var(--muted-foreground) / 50%);
}
&:disabled {
cursor: not-allowed;
opacity: 0.6;
}
&:read-only {
cursor: default;
}
}
.json-highlight-simple {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 8px 12px;
margin: 0;
overflow: hidden;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
color: transparent;
word-break: break-all;
word-wrap: break-word;
tab-size: 2;
white-space: pre-wrap;
pointer-events: none;
background-color: transparent;
border: none;
code {
display: inline;
color: transparent;
word-wrap: break-word;
white-space: pre-wrap;
}
:deep(.json-string) {
color: hsl(var(--primary));
}
:deep(.json-number) {
color: hsl(var(--accent));
}
:deep(.json-boolean) {
color: hsl(var(--chart-2));
}
:deep(.json-null) {
font-weight: 600;
color: hsl(var(--muted-foreground));
}
:deep(.json-colon) {
color: hsl(var(--foreground));
}
:deep(.json-comma) {
color: hsl(var(--foreground));
}
:deep(.json-bracket) {
font-weight: 600;
color: hsl(var(--muted-foreground));
}
}
</style>
@@ -0,0 +1,110 @@
/**
* JSON Editor Props and Types
*/
export interface JsonEditorProps {
/**
* JSON 字符串或对象
*/
modelValue?: Record<string, any> | string;
/**
* 是否只读
* @default false
*/
readonly?: boolean;
/**
* 是否禁用
* @default false
*/
disabled?: boolean;
/**
* 编辑器模式: 'full' 或 'simple'
* - full: 完整功能的 JSON 编辑器(有工具栏、行号、页脚等)
* - simple: 简洁模式,外观类似 ElInput,仅有彩色高亮
* @default 'full'
*/
mode?: 'full' | 'simple';
/**
* 缩进空格数
* @default 2
*/
indent?: number;
/**
* 占位符文本
* @default 使用 i18n 的 'jsonEditor.placeholder'
*/
placeholder?: string;
/**
* 是否启用行号
* @default true
*/
lineNumbers?: boolean;
/**
* 最小高度(像素)
* @default 200
*/
minHeight?: number;
/**
* 最大高度(像素)
* @default 600
*/
maxHeight?: number;
/**
* 是否启用自动格式化
* @default true
*/
autoFormat?: boolean;
/**
* 是否启用语法高亮
* @default true
*/
highlightSyntax?: boolean;
/**
* 是否显示格式化按钮
* @default true
*/
showFormatButton?: boolean;
/**
* 是否显示校验结果
* @default true
*/
showValidation?: boolean;
}
export interface JsonEditorEmits {
/**
* 当内容变化时触发
*/
'update:modelValue': [value: string];
/**
* 当内容变化时触发
*/
change: [value: string];
/**
* 当 JSON 有效时触发
*/
valid: [];
/**
* 当 JSON 无效时触发
*/
invalid: [error: string];
}
export interface JsonValidationResult {
valid: boolean;
error?: string;
}