feat: add more event for jsonViewer (#5546)

* 为JsonViewer添加事件支持
This commit is contained in:
Netfan 2025-02-17 10:41:09 +08:00 committed by GitHub
parent 799934171a
commit b6b97accb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 112 additions and 31 deletions

View File

@ -3,7 +3,12 @@ import type { SetupContext } from 'vue';
import type { Recordable } from '@vben/types'; import type { Recordable } from '@vben/types';
import type { JsonViewerProps } from './types'; import type {
JsonViewerAction,
JsonViewerProps,
JsonViewerToggle,
JsonViewerValue,
} from './types';
import { computed, useAttrs } from 'vue'; import { computed, useAttrs } from 'vue';
// @ts-ignore // @ts-ignore
@ -11,7 +16,7 @@ import VueJsonViewer from 'vue-json-viewer';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { isBoolean, isString } from '@vben-core/shared/utils'; import { isBoolean } from '@vben-core/shared/utils';
defineOptions({ name: 'JsonViewer' }); defineOptions({ name: 'JsonViewer' });
@ -25,15 +30,44 @@ const props = withDefaults(defineProps<JsonViewerProps>(), {
previewMode: false, previewMode: false,
showArrayIndex: true, showArrayIndex: true,
showDoubleQuotes: false, showDoubleQuotes: false,
parseString: true,
}); });
const emit = defineEmits<{ const emit = defineEmits<{
parseError: [error: Error]; click: [event: MouseEvent];
copied: [event: JsonViewerAction];
keyClick: [key: string];
toggle: [param: JsonViewerToggle];
valueClick: [value: JsonViewerValue];
}>(); }>();
const attrs: SetupContext['attrs'] = useAttrs(); const attrs: SetupContext['attrs'] = useAttrs();
function handleClick(event: MouseEvent) {
if (
event.target instanceof HTMLElement &&
event.target.classList.contains('jv-item')
) {
const pathNode = event.target.closest('.jv-push');
if (!pathNode || !pathNode.hasAttribute('path')) {
return;
}
const param: JsonViewerValue = {
path: '',
value: '',
depth: 0,
el: event.target,
};
param.path = pathNode.getAttribute('path') || '';
param.depth = Number(pathNode.getAttribute('depth')) || 0;
param.value = event.target.textContent || undefined;
param.value = JSON.parse(param.value);
emit('valueClick', param);
}
emit('click', event);
}
const bindProps = computed<Recordable<any>>(() => { const bindProps = computed<Recordable<any>>(() => {
const copyable = { const copyable = {
copyText: $t('ui.jsonViewer.copy'), copyText: $t('ui.jsonViewer.copy'),
@ -45,28 +79,15 @@ const bindProps = computed<Recordable<any>>(() => {
return { return {
...props, ...props,
...attrs, ...attrs,
onCopied: (event: JsonViewerAction) => emit('copied', event),
onKeyclick: (key: string) => emit('keyClick', key),
onClick: (event: MouseEvent) => handleClick(event),
copyable: props.copyable ? copyable : false, copyable: props.copyable ? copyable : false,
}; };
}); });
const modelValue = defineModel();
const jsonToShow = computed(() => {
if (props.parseString && isString(modelValue.value)) {
try {
return JSON.parse(modelValue.value);
} catch (error) {
emit('parseError', error as Error);
console.error('Error parsing JSON:', error);
return modelValue.value;
}
} else {
return modelValue.value;
}
});
</script> </script>
<template> <template>
<VueJsonViewer :value="jsonToShow" v-bind="bindProps"> <VueJsonViewer v-bind="bindProps">
<template #copy="slotProps"> <template #copy="slotProps">
<slot name="copy" v-bind="slotProps"></slot> <slot name="copy" v-bind="slotProps"></slot>
</template> </template>

View File

@ -1,4 +1,6 @@
export interface JsonViewerProps { export interface JsonViewerProps {
/** 要展示的结构数据 */
value: any;
/** 展开深度 */ /** 展开深度 */
expandDepth?: number; expandDepth?: number;
/** 是否可复制 */ /** 是否可复制 */
@ -19,6 +21,24 @@ export interface JsonViewerProps {
showArrayIndex?: boolean; showArrayIndex?: boolean;
/** 显示双引号 */ /** 显示双引号 */
showDoubleQuotes?: boolean; showDoubleQuotes?: boolean;
/** 解析字符串 */ }
parseString?: boolean;
export interface JsonViewerAction {
action: string;
text: string;
trigger: HTMLElement;
}
export interface JsonViewerValue {
value: any;
path: string;
depth: number;
el: HTMLElement;
}
export interface JsonViewerToggle {
/** 鼠标事件 */
event: MouseEvent;
/** 当前展开状态 */
open: boolean;
} }

View File

@ -26,8 +26,8 @@ export const json1 = {
], ],
}; };
export const json2 = ` export const json2 = JSON.parse(`
{ {
"id": "chatcmpl-123", "id": "chatcmpl-123",
"object": "chat.completion", "object": "chat.completion",
"created": 1677652288, "created": 1677652288,
@ -46,6 +46,21 @@ export const json2 = `
"completion_tokens": 12, "completion_tokens": 12,
"total_tokens": 21, "total_tokens": 21,
"debug_mode": true "debug_mode": true
},
"debug": {
"startAt": "2021-08-01T00:00:00Z",
"logs": [
{
"timestamp": "2021-08-01T00:00:00Z",
"message": "This is a debug message",
"extra":[ "extra1", "extra2" ]
},
{
"timestamp": "2021-08-01T00:00:01Z",
"message": "This is another debug message",
"extra":[ "extra3", "extra4" ]
}
]
} }
} }
`; `);

View File

@ -1,9 +1,23 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { JsonViewerAction, JsonViewerValue } from '@vben/common-ui';
import { JsonViewer, Page } from '@vben/common-ui'; import { JsonViewer, Page } from '@vben/common-ui';
import { Card } from 'ant-design-vue'; import { Card, message } from 'ant-design-vue';
import { json1, json2 } from './data'; import { json1, json2 } from './data';
function handleKeyClick(key: string) {
message.info(`点击了Key ${key}`);
}
function handleValueClick(value: JsonViewerValue) {
message.info(`点击了Value ${JSON.stringify(value)}`);
}
function handleCopied(_event: JsonViewerAction) {
message.success('已复制JSON');
}
</script> </script>
<template> <template>
<Page <Page
@ -11,16 +25,27 @@ import { json1, json2 } from './data';
description="一个渲染 JSON 结构数据的组件,支持复制、展开等,简单易用" description="一个渲染 JSON 结构数据的组件,支持复制、展开等,简单易用"
> >
<Card title="默认配置"> <Card title="默认配置">
<JsonViewer v-model="json1" /> <JsonViewer :value="json1" />
</Card> </Card>
<Card title="可复制、默认展开3层、显示边框" class="mt-4"> <Card title="可复制、默认展开3层、显示边框、事件处理" class="mt-4">
<JsonViewer <JsonViewer
v-model="json2" :value="json2"
:expand-depth="3" :expand-depth="3"
copyable copyable
:sort="false" :sort="false"
@key-click="handleKeyClick"
@value-click="handleValueClick"
@copied="handleCopied"
boxed boxed
/> />
</Card> </Card>
<Card title="预览模式" class="mt-4">
<JsonViewer
:value="json2"
copyable
preview-mode
:show-array-index="false"
/>
</Card>
</Page> </Page>
</template> </template>