From f71094e87847a35f43be262941f5d7968506d76f Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Wed, 20 May 2026 14:39:17 +0800 Subject: [PATCH] refactor: migrate json-viewer from vue-json-viewer to vue-json-pretty - Replace vue-json-viewer with vue-json-pretty (actively maintained, Vue 3 native) - Map original props to vue-json-pretty API in bindProps for backward compatibility - Implement copy functionality via renderNodeActions slot with i18n support - Update style.scss from .jv-* to .vjs-* class names --- packages/effects/common-ui/package.json | 2 +- .../src/components/json-viewer/index.vue | 104 +++++++------ .../src/components/json-viewer/style.scss | 147 +++++++++--------- pnpm-lock.yaml | 49 ++---- pnpm-workspace.yaml | 2 +- 5 files changed, 139 insertions(+), 165 deletions(-) diff --git a/packages/effects/common-ui/package.json b/packages/effects/common-ui/package.json index 542b6a979..e81687834 100644 --- a/packages/effects/common-ui/package.json +++ b/packages/effects/common-ui/package.json @@ -45,7 +45,7 @@ "qrcode": "catalog:", "tippy.js": "catalog:", "vue": "catalog:", - "vue-json-viewer": "catalog:", + "vue-json-pretty": "catalog:", "vue-router": "catalog:", "vue-tippy": "catalog:" }, diff --git a/packages/effects/common-ui/src/components/json-viewer/index.vue b/packages/effects/common-ui/src/components/json-viewer/index.vue index 97a69ad89..72618f5c9 100644 --- a/packages/effects/common-ui/src/components/json-viewer/index.vue +++ b/packages/effects/common-ui/src/components/json-viewer/index.vue @@ -10,14 +10,12 @@ import type { JsonViewerValue, } from './types'; -import { computed, useAttrs } from 'vue'; -// @ts-expect-error - vue-json-viewer does not expose compatible typings for this import path -import VueJsonViewerImport from 'vue-json-viewer'; +import { computed, ref, useAttrs } from 'vue'; +import VueJsonPretty from 'vue-json-pretty'; +import 'vue-json-pretty/lib/styles.css'; import { $t } from '@vben/locales'; -import { isBoolean } from '@vben-core/shared/utils'; - import JsonBigint from 'json-bigint'; defineOptions({ name: 'JsonViewer' }); @@ -42,33 +40,31 @@ const emit = defineEmits<{ valueClick: [value: JsonViewerValue]; }>(); -/** CJS/UMD 在 Vite 下解析为 { default: Component },需解包否则会出现 missing template or render */ -const VueJsonViewer = - (VueJsonViewerImport as { default?: typeof VueJsonViewerImport }).default ?? - VueJsonViewerImport; - 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 = { - el: event.target, - path: pathNode.getAttribute('path') || '', - depth: Number(pathNode.getAttribute('depth')) || 0, - value: event.target.textContent || undefined, - }; +const copiedPath = ref(null); - param.value = JSON.parse(param.value); - emit('valueClick', param); - } - emit('click', event); +const copyConfig = computed(() => { + return { + copiedText: $t('ui.jsonViewer.copied'), + copyText: $t('ui.jsonViewer.copy'), + timeout: 2000, + }; +}); + +function handleCopy(node: any, defaultCopy: () => void) { + defaultCopy(); + copiedPath.value = node.path; + emit('copied', { + action: 'copy', + text: JSON.stringify(node.content), + trigger: node.el ?? document.body, + }); + setTimeout(() => { + if (copiedPath.value === node.path) { + copiedPath.value = null; + } + }, copyConfig.value.timeout ?? 2000); } // 支持显示 bigint 数据,如较长的订单号 @@ -86,30 +82,46 @@ const jsonData = computed>(() => { }); const bindProps = computed>(() => { - const copyable = { - copyText: $t('ui.jsonViewer.copy'), - copiedText: $t('ui.jsonViewer.copied'), - timeout: 2000, - ...(isBoolean(props.copyable) ? {} : props.copyable), - }; + const prettyTheme = + props.theme === 'dark' || props.theme === 'dark-json-theme' + ? 'dark' + : 'light'; return { - ...props, ...attrs, - value: jsonData.value, - onCopied: (event: JsonViewerAction) => emit('copied', event), - onKeyclick: (key: string) => emit('keyClick', key), - onClick: (event: MouseEvent) => handleClick(event), - copyable: props.copyable ? copyable : false, + data: jsonData.value, + deep: props.expanded ? Infinity : props.expandDepth, + showDoubleQuotes: props.showDoubleQuotes, + showLine: props.boxed, + showLength: true, + showIcon: true, + theme: prettyTheme, + collapsedNodeLength: props.previewMode ? 0 : Infinity, + renderNodeActions: !!props.copyable, }; });