index.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <script lang="ts" setup>
  2. import type { SetupContext } from 'vue';
  3. import type { Recordable } from '@vben/types';
  4. import type {
  5. JsonViewerAction,
  6. JsonViewerProps,
  7. JsonViewerToggle,
  8. JsonViewerValue,
  9. } from './types';
  10. import { computed, ref, useAttrs } from 'vue';
  11. import VueJsonPretty from 'vue-json-pretty';
  12. import 'vue-json-pretty/lib/styles.css';
  13. import { $t } from '@vben/locales';
  14. import JsonBigint from 'json-bigint';
  15. defineOptions({ name: 'JsonViewer' });
  16. const props = withDefaults(defineProps<JsonViewerProps>(), {
  17. expandDepth: 1,
  18. copyable: false,
  19. sort: false,
  20. boxed: false,
  21. theme: 'default-json-theme',
  22. expanded: false,
  23. previewMode: false,
  24. showArrayIndex: true,
  25. showDoubleQuotes: false,
  26. });
  27. const emit = defineEmits<{
  28. click: [event: MouseEvent];
  29. copied: [event: JsonViewerAction];
  30. keyClick: [key: string];
  31. toggle: [param: JsonViewerToggle];
  32. valueClick: [value: JsonViewerValue];
  33. }>();
  34. const attrs: SetupContext['attrs'] = useAttrs();
  35. const copiedPath = ref<null | string>(null);
  36. const copyConfig = computed(() => {
  37. return {
  38. copiedText: $t('ui.jsonViewer.copied'),
  39. copyText: $t('ui.jsonViewer.copy'),
  40. timeout: 2000,
  41. };
  42. });
  43. function handleCopy(node: any, defaultCopy: () => void) {
  44. defaultCopy();
  45. copiedPath.value = node.path;
  46. emit('copied', {
  47. action: 'copy',
  48. text: JSON.stringify(node.content),
  49. trigger: node.el ?? document.body,
  50. });
  51. setTimeout(() => {
  52. if (copiedPath.value === node.path) {
  53. copiedPath.value = null;
  54. }
  55. }, copyConfig.value.timeout ?? 2000);
  56. }
  57. // 支持显示 bigint 数据,如较长的订单号
  58. const jsonData = computed<Record<string, any>>(() => {
  59. if (typeof props.value !== 'string') {
  60. return props.value || {};
  61. }
  62. try {
  63. return JsonBigint({ storeAsString: true }).parse(props.value);
  64. } catch (error) {
  65. console.error('JSON parse error:', error);
  66. return {};
  67. }
  68. });
  69. const bindProps = computed<Recordable<any>>(() => {
  70. const prettyTheme =
  71. props.theme === 'dark' || props.theme === 'dark-json-theme'
  72. ? 'dark'
  73. : 'light';
  74. return {
  75. ...attrs,
  76. data: jsonData.value,
  77. deep: props.expanded ? Infinity : props.expandDepth,
  78. showDoubleQuotes: props.showDoubleQuotes,
  79. showLine: props.boxed,
  80. showLength: true,
  81. showIcon: true,
  82. theme: prettyTheme,
  83. collapsedNodeLength: props.previewMode ? 0 : Infinity,
  84. renderNodeActions: !!props.copyable,
  85. };
  86. });
  87. </script>
  88. <template>
  89. <div :class="[props.theme, { boxed: props.boxed }]" class="vben-json-viewer">
  90. <VueJsonPretty v-bind="bindProps">
  91. <template #renderNodeActions="{ node, defaultActions }">
  92. <slot name="copy" :node="node" :default-actions="defaultActions">
  93. <span
  94. v-if="props.copyable"
  95. class="vben-json-copy-btn"
  96. :class="[{ 'is-copied': copiedPath === node.path }]"
  97. @click.stop="handleCopy(node, defaultActions.copy)"
  98. >
  99. {{
  100. copiedPath === node.path
  101. ? copyConfig.copiedText
  102. : copyConfig.copyText
  103. }}
  104. </span>
  105. </slot>
  106. </template>
  107. </VueJsonPretty>
  108. </div>
  109. </template>
  110. <style lang="scss">
  111. @use './style.scss';
  112. </style>