replace el-tree with vue-json-pretty

This commit is contained in:
pa
2026-01-10 21:20:45 +09:00
committed by Natsumi
parent c2e34e3395
commit 820b86be28
10 changed files with 244 additions and 1153 deletions
+161 -1066
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -47,7 +47,7 @@
"@tanstack/vue-table": "^8.21.3", "@tanstack/vue-table": "^8.21.3",
"@tanstack/vue-virtual": "^3.13.18", "@tanstack/vue-virtual": "^3.13.18",
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
"@types/node": "^25.0.3", "@types/node": "^25.0.5",
"@vitejs/plugin-vue": "^6.0.3", "@vitejs/plugin-vue": "^6.0.3",
"@vitejs/plugin-vue-jsx": "^5.1.3", "@vitejs/plugin-vue-jsx": "^5.1.3",
"@vueuse/core": "^14.1.0", "@vueuse/core": "^14.1.0",
@@ -60,8 +60,8 @@
"dayjs": "^1.11.19", "dayjs": "^1.11.19",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"electron": "^39.2.7", "electron": "^39.2.7",
"electron-builder": "^26.0.12", "electron-builder": "^26.4.0",
"element-plus": "^2.13.0", "element-plus": "^2.13.1",
"esbuild-jest": "^0.5.0", "esbuild-jest": "^0.5.0",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
@@ -79,13 +79,14 @@
"tailwind-merge": "^3.4.0", "tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"vite": "^7.3.0", "vite": "^7.3.1",
"vue": "^3.5.26", "vue": "^3.5.26",
"vue-i18n": "^11.2.8", "vue-i18n": "^11.2.8",
"vue-json-pretty": "^2.6.0",
"vue-marquee-text-component": "^2.0.1", "vue-marquee-text-component": "^2.0.1",
"vue-router": "^4.6.4", "vue-router": "^4.6.4",
"vue-showdown": "^4.2.0", "vue-showdown": "^4.2.0",
"worker-timers": "^8.0.27", "worker-timers": "^8.0.28",
"yargs": "^18.0.0" "yargs": "^18.0.0"
}, },
"build": { "build": {
+1
View File
@@ -13,6 +13,7 @@
@import 'noty/lib/noty.css'; @import 'noty/lib/noty.css';
@import 'remixicon/fonts/remixicon.css'; @import 'remixicon/fonts/remixicon.css';
@import 'vue-sonner/style.css'; @import 'vue-sonner/style.css';
@import 'vue-json-pretty/lib/styles.css';
@import './styles/flags.css'; @import './styles/flags.css';
@import './styles/animated-emoji.css'; @import './styles/animated-emoji.css';
@@ -523,26 +523,14 @@
circle circle
style="margin-left: 5px" style="margin-left: 5px"
@click="downloadAndSaveJson(avatarDialog.id, avatarDialog.ref)"></el-button> @click="downloadAndSaveJson(avatarDialog.id, avatarDialog.ref)"></el-button>
<el-tree :data="treeData" style="margin-top: 5px; font-size: 12px"> <vue-json-pretty :data="treeData" :deep="2" :theme="isDarkMode ? 'dark' : 'light'" show-icon />
<template #default="scope">
<span>
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span>
</span>
</template>
</el-tree>
<br /> <br />
<el-tree <vue-json-pretty
v-if="avatarDialog.fileAnalysis.length > 0" v-if="avatarDialog.fileAnalysis.length > 0"
:data="avatarDialog.fileAnalysis" :data="avatarDialog.fileAnalysis"
style="margin-top: 5px; font-size: 12px"> :deep="2"
<template #default="scope"> :theme="isDarkMode ? 'dark' : 'light'"
<span> show-icon />
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span>
</span>
</template>
</el-tree>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -583,8 +571,9 @@
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VueJsonPretty from 'vue-json-pretty';
import { import {
buildTreeData,
commaNumber, commaNumber,
copyToClipboard, copyToClipboard,
downloadAndSaveJson, downloadAndSaveJson,
@@ -597,6 +586,14 @@
textToHex, textToHex,
timeToText timeToText
} from '../../../shared/utils'; } from '../../../shared/utils';
import {
useAppearanceSettingsStore,
useAvatarStore,
useFavoriteStore,
useGalleryStore,
useGameStore,
useUserStore
} from '../../../stores';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -604,7 +601,6 @@
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger DropdownMenuTrigger
} from '../../ui/dropdown-menu'; } from '../../ui/dropdown-menu';
import { useAvatarStore, useFavoriteStore, useGalleryStore, useGameStore, useUserStore } from '../../../stores';
import { avatarModerationRequest, avatarRequest, favoriteRequest, miscRequest } from '../../../api'; import { avatarModerationRequest, avatarRequest, favoriteRequest, miscRequest } from '../../../api';
import { AppDebug } from '../../../service/appConfig.js'; import { AppDebug } from '../../../service/appConfig.js';
import { Badge } from '../../ui/badge'; import { Badge } from '../../ui/badge';
@@ -627,6 +623,7 @@
const { isGameRunning } = storeToRefs(useGameStore()); const { isGameRunning } = storeToRefs(useGameStore());
const { deleteVRChatCache } = useGameStore(); const { deleteVRChatCache } = useGameStore();
const { showFullscreenImageDialog } = useGalleryStore(); const { showFullscreenImageDialog } = useGalleryStore();
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const { t } = useI18n(); const { t } = useI18n();
@@ -1018,10 +1015,10 @@
} }
function refreshAvatarDialogTreeData() { function refreshAvatarDialogTreeData() {
treeData.value = buildTreeData({ treeData.value = {
...avatarDialog.value.ref, ...avatarDialog.value.ref,
_hexDisplayName: textToHex(avatarDialog.value.ref?.displayName) _hexDisplayName: textToHex(avatarDialog.value.ref?.displayName)
}); };
} }
function showSetAvatarTagsDialog(avatarId) { function showSetAvatarTagsDialog(avatarId) {
@@ -1120,14 +1120,11 @@
circle circle
style="margin-left: 5px" style="margin-left: 5px"
@click="downloadAndSaveJson(groupDialog.id, groupDialog.ref)" /> @click="downloadAndSaveJson(groupDialog.id, groupDialog.ref)" />
<el-tree :data="groupDialog.treeData" style="margin-top: 5px; font-size: 12px"> <vue-json-pretty
<template #default="scope"> :data="groupDialog.treeData"
<span> :deep="2"
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key" /> :theme="isDarkMode ? 'dark' : 'light'"
<span v-if="!scope.data.children" v-text="scope.data.value" /> show-icon />
</span>
</template>
</el-tree>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -1171,8 +1168,9 @@
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VueJsonPretty from 'vue-json-pretty';
import { import {
buildTreeData,
copyToClipboard, copyToClipboard,
debounce, debounce,
downloadAndSaveJson, downloadAndSaveJson,
@@ -1195,7 +1193,13 @@
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger DropdownMenuTrigger
} from '../../ui/dropdown-menu'; } from '../../ui/dropdown-menu';
import { useGalleryStore, useGroupStore, useLocationStore, useUserStore } from '../../../stores'; import {
useAppearanceSettingsStore,
useGalleryStore,
useGroupStore,
useLocationStore,
useUserStore
} from '../../../stores';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants'; import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { Badge } from '../../ui/badge'; import { Badge } from '../../ui/badge';
import { getNextDialogIndex } from '../../../shared/utils/base/ui'; import { getNextDialogIndex } from '../../../shared/utils/base/ui';
@@ -1227,6 +1231,8 @@
const { lastLocation } = storeToRefs(useLocationStore()); const { lastLocation } = storeToRefs(useLocationStore());
const { showFullscreenImageDialog } = useGalleryStore(); const { showFullscreenImageDialog } = useGalleryStore();
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const groupDialogLastActiveTab = ref('Info'); const groupDialogLastActiveTab = ref('Info');
const groupDialogIndex = ref(2000); const groupDialogIndex = ref(2000);
const isGroupMembersDone = ref(false); const isGroupMembersDone = ref(false);
@@ -1757,14 +1763,14 @@
function refreshGroupDialogTreeData() { function refreshGroupDialogTreeData() {
const D = groupDialog.value; const D = groupDialog.value;
const treeData = buildTreeData({ const treeData = {
_hexDisplayName: textToHex(D.ref.displayName), _hexDisplayName: textToHex(D.ref.displayName),
group: D.ref, group: D.ref,
posts: D.posts, posts: D.posts,
instances: D.instances, instances: D.instances,
members: D.members, members: D.members,
galleries: D.galleries galleries: D.galleries
}); };
updateGroupDialogData({ updateGroupDialogData({
...groupDialog.value, ...groupDialog.value,
treeData treeData
@@ -1274,14 +1274,11 @@
style="margin-left: 5px" style="margin-left: 5px"
@click="downloadAndSaveJson(userDialog.id, userDialog.ref)"> @click="downloadAndSaveJson(userDialog.id, userDialog.ref)">
</el-button> </el-button>
<el-tree :data="userDialog.treeData" style="margin-top: 5px; font-size: 12px"> <vue-json-pretty
<template #default="scope"> :data="userDialog.treeData"
<span> :deep="2"
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span> :theme="isDarkMode ? 'dark' : 'light'"
<span v-if="!scope.data.children" v-text="scope.data.value"></span> show-icon />
</span>
</template>
</el-tree>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -1330,6 +1327,8 @@
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VueJsonPretty from 'vue-json-pretty';
import { import {
compareByDisplayName, compareByDisplayName,
compareByFriendOrder, compareByFriendOrder,
@@ -1404,7 +1403,7 @@
const { t } = useI18n(); const { t } = useI18n();
const { hideUserNotes, hideUserMemos } = storeToRefs(useAppearanceSettingsStore()); const { hideUserNotes, hideUserMemos, isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const { bioLanguage, avatarRemoteDatabase, translationApi, translationApiType } = const { bioLanguage, avatarRemoteDatabase, translationApi, translationApiType } =
storeToRefs(useAdvancedSettingsStore()); storeToRefs(useAdvancedSettingsStore());
const { translateText } = useAdvancedSettingsStore(); const { translateText } = useAdvancedSettingsStore();
@@ -698,26 +698,14 @@
circle circle
style="margin-left: 5px" style="margin-left: 5px"
@click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button> @click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button>
<el-tree :data="treeData" style="margin-top: 5px; font-size: 12px"> <vue-json-pretty :data="treeData" :deep="2" :theme="isDarkMode ? 'dark' : 'light'" show-icon />
<template #default="{ data }">
<span>
<span style="font-weight: bold; margin-right: 5px" v-text="data.key"></span>
<span v-if="!data.children" v-text="data.value"></span>
</span>
</template>
</el-tree>
<br /> <br />
<el-tree <vue-json-pretty
v-if="worldDialog.fileAnalysis.length > 0" v-if="worldDialog.fileAnalysis.length > 0"
:data="worldDialog.fileAnalysis" :data="worldDialog.fileAnalysis"
style="margin-top: 5px; font-size: 12px"> :deep="2"
<template #default="scope"> :theme="isDarkMode ? 'dark' : 'light'"
<span> show-icon />
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span>
</span>
</template>
</el-tree>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@@ -772,10 +760,10 @@
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VueJsonPretty from 'vue-json-pretty';
import { import {
buildTreeData,
commaNumber, commaNumber,
copyToClipboard,
deleteVRChatCache, deleteVRChatCache,
downloadAndSaveJson, downloadAndSaveJson,
formatDateFilter, formatDateFilter,
@@ -819,7 +807,7 @@
const SetWorldTagsDialog = defineAsyncComponent(() => import('./SetWorldTagsDialog.vue')); const SetWorldTagsDialog = defineAsyncComponent(() => import('./SetWorldTagsDialog.vue'));
const WorldAllowedDomainsDialog = defineAsyncComponent(() => import('./WorldAllowedDomainsDialog.vue')); const WorldAllowedDomainsDialog = defineAsyncComponent(() => import('./WorldAllowedDomainsDialog.vue'));
const { isAgeGatedInstancesVisible } = storeToRefs(useAppearanceSettingsStore()); const { isAgeGatedInstancesVisible, isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const { showUserDialog } = useUserStore(); const { showUserDialog } = useUserStore();
const { currentUser, userDialog } = storeToRefs(useUserStore()); const { currentUser, userDialog } = storeToRefs(useUserStore());
const { worldDialog } = storeToRefs(useWorldStore()); const { worldDialog } = storeToRefs(useWorldStore());
@@ -1303,10 +1291,10 @@
nextTick(() => (D.openFlg = false)); nextTick(() => (D.openFlg = false));
} }
function refreshWorldDialogTreeData() { function refreshWorldDialogTreeData() {
treeData.value = buildTreeData({ treeData.value = {
...worldDialog.value.ref, ...worldDialog.value.ref,
_hexDisplayName: textToHex(worldDialog.value.ref?.displayName) _hexDisplayName: textToHex(worldDialog.value.ref?.displayName)
}); };
} }
function copyWorldId() { function copyWorldId() {
navigator.clipboard navigator.clipboard
+2 -2
View File
@@ -493,13 +493,13 @@ async function getBundleDateSize(ref) {
// update avatar dialog // update avatar dialog
avatarDialog.value.bundleSizes[platform] = bundleSizes[platform]; avatarDialog.value.bundleSizes[platform] = bundleSizes[platform];
avatarDialog.value.lastUpdated = createdAt; avatarDialog.value.lastUpdated = createdAt;
avatarDialog.value.fileAnalysis = buildTreeData(bundleJson); avatarDialog.value.fileAnalysis = bundleJson;
} }
// update world dialog // update world dialog
if (worldDialog.value.id === ref.id) { if (worldDialog.value.id === ref.id) {
worldDialog.value.bundleSizes[platform] = bundleSizes[platform]; worldDialog.value.bundleSizes[platform] = bundleSizes[platform];
worldDialog.value.lastUpdated = createdAt; worldDialog.value.lastUpdated = createdAt;
worldDialog.value.fileAnalysis = buildTreeData(bundleJson); worldDialog.value.fileAnalysis = bundleJson;
} }
// update player list // update player list
if (currentInstanceLocation.value.worldId === ref.id) { if (currentInstanceLocation.value.worldId === ref.id) {
+3 -4
View File
@@ -6,7 +6,6 @@ import Noty from 'noty';
import { import {
arraysMatch, arraysMatch,
buildTreeData,
compareByDisplayName, compareByDisplayName,
compareByLocationAt, compareByLocationAt,
compareByName, compareByName,
@@ -1230,13 +1229,13 @@ export const useUserStore = defineStore('User', () => {
...D.ref, ...D.ref,
_hexDisplayName: textToHex(D.ref?.displayName) _hexDisplayName: textToHex(D.ref?.displayName)
}; };
D.treeData = buildTreeData(treeData); D.treeData = treeData;
return; return;
} }
D.treeData = buildTreeData({ D.treeData = {
...D.ref, ...D.ref,
_hexDisplayName: textToHex(D.ref?.displayName) _hexDisplayName: textToHex(D.ref?.displayName)
}); };
} }
async function lookupUser(ref) { async function lookupUser(ref) {
@@ -368,17 +368,18 @@
:icon="Delete" :icon="Delete"
circle circle
style="margin-left: 5px" style="margin-left: 5px"
@click="configTreeData = []"></el-button> @click="configTreeData = {}"></el-button>
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<el-tree v-if="configTreeData.length > 0" :data="configTreeData" style="margin-top: 10px; font-size: 12px"> <vue-json-pretty
<template #default="scope"> v-if="Object.keys(configTreeData).length > 0"
<span> :data="configTreeData"
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span> :deep="2"
<span v-if="!scope.data.children" v-text="scope.data.value"></span> :theme="isDarkMode ? 'dark' : 'light'"
</span> :height="800"
</template> :dynamic-height="false"
</el-tree> virtual
show-icon />
</div> </div>
<RegistryBackupDialog /> <RegistryBackupDialog />
@@ -410,8 +411,11 @@
import { toast } from 'vue-sonner'; import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VueJsonPretty from 'vue-json-pretty';
import { import {
useAdvancedSettingsStore, useAdvancedSettingsStore,
useAppearanceSettingsStore,
useAuthStore, useAuthStore,
useAvatarProviderStore, useAvatarProviderStore,
useAvatarStore, useAvatarStore,
@@ -429,7 +433,6 @@
useWorldStore useWorldStore
} from '../../../../stores'; } from '../../../../stores';
import { authRequest, miscRequest } from '../../../../api'; import { authRequest, miscRequest } from '../../../../api';
import { buildTreeData } from '../../../../shared/utils/common';
import { openExternalLink } from '../../../../shared/utils'; import { openExternalLink } from '../../../../shared/utils';
import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue'; import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue';
@@ -461,6 +464,8 @@
const { branch } = storeToRefs(useVRCXUpdaterStore()); const { branch } = storeToRefs(useVRCXUpdaterStore());
const { openVR } = storeToRefs(notificationsSettingsStore); const { openVR } = storeToRefs(notificationsSettingsStore);
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const { const {
enablePrimaryPassword, enablePrimaryPassword,
relaunchVRChatAfterCrash, relaunchVRChatAfterCrash,
@@ -502,7 +507,7 @@
const isYouTubeApiDialogVisible = ref(false); const isYouTubeApiDialogVisible = ref(false);
const isTranslationApiDialogVisible = ref(false); const isTranslationApiDialogVisible = ref(false);
const configTreeData = ref([]); const configTreeData = ref({});
const visits = ref(0); const visits = ref(0);
const cacheSize = reactive({ const cacheSize = reactive({
@@ -587,7 +592,7 @@
async function refreshConfigTreeData() { async function refreshConfigTreeData() {
await authRequest.getConfig(); await authRequest.getConfig();
configTreeData.value = buildTreeData(cachedConfig.value); configTreeData.value = cachedConfig.value;
} }
function getVisits() { function getVisits() {