mirror of
				https://github.com/tiny-craft/tiny-rdm.git
				synced 2025-10-31 18:42:33 +08:00 
			
		
		
		
	perf: create "content-pane" component for each tab to maintain data and status easily
This commit is contained in:
		| @@ -208,7 +208,12 @@ onMounted(async () => { | ||||
|                             @mouseout="data.hoverResize = false" | ||||
|                             @mouseover="data.hoverResize = true" /> | ||||
|                     </div> | ||||
|                     <content-pane class="flex-item-expand" /> | ||||
|                     <content-pane | ||||
|                         v-for="t in tabStore.tabs" | ||||
|                         v-show="get(tabStore.currentTab, 'name') === t.name" | ||||
|                         :key="t.name" | ||||
|                         :server="t.name" | ||||
|                         class="flex-item-expand" /> | ||||
|                 </div> | ||||
|  | ||||
|                 <!-- server list page --> | ||||
|   | ||||
| @@ -73,10 +73,10 @@ defineExpose({ | ||||
| <template> | ||||
|     <n-card | ||||
|         :bordered="false" | ||||
|         :theme-overrides="{ borderRadius: '0px' }" | ||||
|         :title="$t('log.launch_log')" | ||||
|         class="content-container flex-box-v" | ||||
|         content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray" | ||||
|         :theme-overrides="{ borderRadius: '0px' }"> | ||||
|         content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray"> | ||||
|         <n-form :disabled="data.loading" class="flex-item" inline> | ||||
|             <n-form-item :label="$t('log.filter_server')"> | ||||
|                 <n-select | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| <script setup> | ||||
| import { computed, onMounted, onUnmounted, reactive, watch } from 'vue' | ||||
| import { get, isEmpty, keyBy, map, size, toUpper } from 'lodash' | ||||
| import { computed, nextTick, ref, watch } from 'vue' | ||||
| import { find, map, toUpper } from 'lodash' | ||||
| import useTabStore from 'stores/tab.js' | ||||
| import useConnectionStore from 'stores/connections.js' | ||||
| import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue' | ||||
| import Status from '@/components/icons/Status.vue' | ||||
| import { useThemeVars } from 'naive-ui' | ||||
| @@ -24,98 +23,10 @@ const themeVars = useThemeVars() | ||||
|  * @property {boolean} autoLoading loading status for auto refresh | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @type {UnwrapNestedRefs<Object.<string, ServerStatusItem>>} | ||||
|  */ | ||||
| const serverStatusTab = reactive({}) | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @param {string} serverName | ||||
|  * @return {UnwrapRef<ServerStatusItem>} | ||||
|  */ | ||||
| const getServerInfo = (serverName) => { | ||||
|     if (isEmpty(serverName)) { | ||||
|         return { | ||||
|             name: serverName, | ||||
|             info: {}, | ||||
|             autoRefresh: false, | ||||
|             autoLoading: false, | ||||
|             loading: false, | ||||
|         } | ||||
|     } | ||||
|     if (!serverStatusTab.hasOwnProperty(serverName)) { | ||||
|         serverStatusTab[serverName] = { | ||||
|             name: serverName, | ||||
|             info: {}, | ||||
|             autoRefresh: false, | ||||
|             autoLoading: false, | ||||
|             loading: false, | ||||
|         } | ||||
|     } | ||||
|     return serverStatusTab[serverName] | ||||
| } | ||||
| const serverName = computed(() => { | ||||
|     if (tabContent.value != null) { | ||||
|         return tabContent.value.name | ||||
|     } | ||||
|     return '' | ||||
| }) | ||||
| /** | ||||
|  * | ||||
|  * @type {ComputedRef<ServerStatusItem>} | ||||
|  */ | ||||
| const currentServer = computed(() => { | ||||
|     return getServerInfo(serverName.value) | ||||
| const props = defineProps({ | ||||
|     server: String, | ||||
| }) | ||||
|  | ||||
| /** | ||||
|  * refresh server status info | ||||
|  * @param {string} serverName | ||||
|  * @param {boolean} [force] force refresh will show loading indicator | ||||
|  * @returns {Promise<void>} | ||||
|  */ | ||||
| const refreshInfo = async (serverName, force) => { | ||||
|     const info = getServerInfo(serverName) | ||||
|     if (force) { | ||||
|         info.loading = true | ||||
|     } else { | ||||
|         info.autoLoading = true | ||||
|     } | ||||
|     if (!isEmpty(serverName) && connectionStore.isConnected(serverName)) { | ||||
|         try { | ||||
|             info.info = await connectionStore.getServerInfo(serverName) | ||||
|         } finally { | ||||
|             info.loading = false | ||||
|             info.autoLoading = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| const refreshAllInfo = async (force) => { | ||||
|     for (const serverName in serverStatusTab) { | ||||
|         await refreshInfo(serverName, force) | ||||
|     } | ||||
| } | ||||
|  | ||||
| let intervalId | ||||
| onMounted(() => { | ||||
|     refreshAllInfo(true) | ||||
|     intervalId = setInterval(() => { | ||||
|         for (const serverName in serverStatusTab) { | ||||
|             if (get(serverStatusTab, [serverName, 'autoRefresh'])) { | ||||
|                 refreshInfo(serverName) | ||||
|             } | ||||
|         } | ||||
|     }, 5000) | ||||
| }) | ||||
|  | ||||
| onUnmounted(() => { | ||||
|     clearInterval(intervalId) | ||||
| }) | ||||
|  | ||||
| const connectionStore = useConnectionStore() | ||||
| const tabStore = useTabStore() | ||||
| const tab = computed(() => | ||||
|     map(tabStore.tabs, (item) => ({ | ||||
| @@ -124,35 +35,10 @@ const tab = computed(() => | ||||
|     })), | ||||
| ) | ||||
|  | ||||
| watch( | ||||
|     () => tabStore.nav, | ||||
|     (nav) => { | ||||
|         if (nav === 'browser') { | ||||
|             refreshInfo(serverName.value) | ||||
|         } | ||||
|     }, | ||||
| ) | ||||
|  | ||||
| watch( | ||||
|     () => tabStore.tabList, | ||||
|     (tabs) => { | ||||
|         if (size(tabs) < size(serverStatusTab)) { | ||||
|             const tabMap = keyBy(tabs, 'name') | ||||
|             // remove unused server status tab | ||||
|             for (const t in serverStatusTab) { | ||||
|                 if (!tabMap.hasOwnProperty(t)) { | ||||
|                     delete serverStatusTab[t] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     { deep: true }, | ||||
| ) | ||||
|  | ||||
| const tabContent = computed(() => { | ||||
|     const tab = tabStore.currentTab | ||||
|     const tab = find(tabStore.tabs, { name: props.server }) | ||||
|     if (tab == null) { | ||||
|         return null | ||||
|         return {} | ||||
|     } | ||||
|     return { | ||||
|         name: tab.name, | ||||
| @@ -168,26 +54,10 @@ const tabContent = computed(() => { | ||||
|     } | ||||
| }) | ||||
|  | ||||
| const showServerStatus = computed(() => { | ||||
|     return tabContent.value == null || isEmpty(tabContent.value.keyPath) | ||||
| }) | ||||
|  | ||||
| const isBlankValue = computed(() => { | ||||
|     return tabContent.value.value == null | ||||
| }) | ||||
|  | ||||
| /** | ||||
|  * reload current selection key | ||||
|  * @returns {Promise<null>} | ||||
|  */ | ||||
| const onReloadKey = async () => { | ||||
|     const tab = tabStore.currentTab | ||||
|     if (tab == null || isEmpty(tab.key)) { | ||||
|         return null | ||||
|     } | ||||
|     await connectionStore.loadKeyValue(tab.name, tab.db, tab.key, tab.viewAs) | ||||
| } | ||||
|  | ||||
| const selectedSubTab = computed(() => { | ||||
|     const { subTab = 'status' } = tabStore.currentTab || {} | ||||
|     return subTab | ||||
| @@ -196,11 +66,28 @@ const selectedSubTab = computed(() => { | ||||
| const onSwitchSubTab = (name) => { | ||||
|     tabStore.switchSubTab(name) | ||||
| } | ||||
|  | ||||
| // BUG: naive-ui tabs will set the bottom line to '0px' after switch to another page and back again | ||||
| // watch parent tabs' changing and call 'syncBarPosition' manually | ||||
| const tabsRef = ref(null) | ||||
| const cliRef = ref(null) | ||||
| watch( | ||||
|     () => tabContent.value?.name, | ||||
|     (name) => { | ||||
|         if (name === props.server) { | ||||
|             nextTick().then(() => { | ||||
|                 tabsRef.value?.syncBarPosition() | ||||
|                 cliRef.value?.resizeTerm() | ||||
|             }) | ||||
|         } | ||||
|     }, | ||||
| ) | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|     <div class="content-container flex-box-v"> | ||||
|         <n-tabs | ||||
|             ref="tabsRef" | ||||
|             :tabs-padding="5" | ||||
|             :theme-overrides="{ | ||||
|                 tabFontWeightActive: 'normal', | ||||
| @@ -217,7 +104,7 @@ const onSwitchSubTab = (name) => { | ||||
|             type="line" | ||||
|             @update:value="onSwitchSubTab"> | ||||
|             <!-- server status pane --> | ||||
|             <n-tab-pane :name="BrowserTabType.Status.toString()"> | ||||
|             <n-tab-pane :name="BrowserTabType.Status.toString()" display-directive="show:lazy"> | ||||
|                 <template #tab> | ||||
|                     <n-space :size="5" :wrap-item="false" align="center" inline justify="center"> | ||||
|                         <n-icon size="16"> | ||||
| @@ -226,17 +113,11 @@ const onSwitchSubTab = (name) => { | ||||
|                         <span>{{ $t('interface.sub_tab.status') }}</span> | ||||
|                     </n-space> | ||||
|                 </template> | ||||
|                 <content-server-status | ||||
|                     v-model:auto-refresh="currentServer.autoRefresh" | ||||
|                     :auto-loading="currentServer.autoLoading" | ||||
|                     :info="currentServer.info" | ||||
|                     :loading="currentServer.loading" | ||||
|                     :server="currentServer.name" | ||||
|                     @refresh="refreshInfo(currentServer.name, true)" /> | ||||
|                 <content-server-status :server="props.server" /> | ||||
|             </n-tab-pane> | ||||
|  | ||||
|             <!-- key detail pane --> | ||||
|             <n-tab-pane :name="BrowserTabType.KeyDetail.toString()"> | ||||
|             <n-tab-pane :name="BrowserTabType.KeyDetail.toString()" display-directive="show:lazy"> | ||||
|                 <template #tab> | ||||
|                     <n-space :size="5" :wrap-item="false" align="center" inline justify="center"> | ||||
|                         <n-icon size="16"> | ||||
| @@ -249,20 +130,19 @@ const onSwitchSubTab = (name) => { | ||||
|                 </template> | ||||
|                 <content-value-wrapper | ||||
|                     :blank="isBlankValue" | ||||
|                     :type="tabContent.type" | ||||
|                     :db="tabContent.db" | ||||
|                     :key-code="tabContent.keyCode" | ||||
|                     :key-path="tabContent.keyPath" | ||||
|                     :name="tabContent.name" | ||||
|                     :size="tabContent.size" | ||||
|                     :ttl="tabContent.ttl" | ||||
|                     :type="tabContent.type" | ||||
|                     :value="tabContent.value" | ||||
|                     :view-as="tabContent.viewAs" | ||||
|                     @reload="onReloadKey" /> | ||||
|                     :view-as="tabContent.viewAs" /> | ||||
|             </n-tab-pane> | ||||
|  | ||||
|             <!-- cli pane --> | ||||
|             <n-tab-pane :name="BrowserTabType.Cli.toString()"> | ||||
|             <n-tab-pane :name="BrowserTabType.Cli.toString()" display-directive="show:lazy"> | ||||
|                 <template #tab> | ||||
|                     <n-space :size="5" :wrap-item="false" align="center" inline justify="center"> | ||||
|                         <n-icon size="16"> | ||||
| @@ -271,7 +151,7 @@ const onSwitchSubTab = (name) => { | ||||
|                         <span>{{ $t('interface.sub_tab.cli') }}</span> | ||||
|                     </n-space> | ||||
|                 </template> | ||||
|                 <content-cli :name="currentServer.name" /> | ||||
|                 <content-cli ref="cliRef" :name="props.server" /> | ||||
|             </n-tab-pane> | ||||
|  | ||||
|             <!-- slow log pane --> | ||||
| @@ -312,4 +192,8 @@ const onSwitchSubTab = (name) => { | ||||
|     background-color: v-bind('themeVars.tabColor'); | ||||
|     overflow: hidden; | ||||
| } | ||||
|  | ||||
| .n-tabs .n-tabs-bar { | ||||
|     transition: none !important; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -52,13 +52,13 @@ const tab = computed(() => | ||||
|     <n-tabs | ||||
|         v-model:value="tabStore.activatedIndex" | ||||
|         :closable="true" | ||||
|         :tabs-padding="0" | ||||
|         :tab-style="{ | ||||
|             borderStyle: 'solid', | ||||
|             borderWidth: '1px', | ||||
|             borderLeftColor: themeVars.borderColor, | ||||
|             borderRightColor: themeVars.borderColor, | ||||
|         }" | ||||
|         :tabs-padding="0" | ||||
|         :theme-overrides="{ | ||||
|             tabFontWeightActive: 800, | ||||
|             tabGapSmallCard: 0, | ||||
| @@ -76,10 +76,10 @@ const tab = computed(() => | ||||
|         <n-tab | ||||
|             v-for="(t, i) in tab" | ||||
|             :key="i" | ||||
|             :class="tabClass(i)" | ||||
|             :closable="true" | ||||
|             :name="i" | ||||
|             :style="tabStore.activatedIndex === i ? activeTabStyle : undefined" | ||||
|             :class="tabClass(i)" | ||||
|             @dblclick.stop="() => {}"> | ||||
|             <n-space :size="5" :wrap-item="false" align="center" inline justify="center"> | ||||
|                 <n-icon size="18"> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <script setup> | ||||
| import { Terminal } from 'xterm' | ||||
| import { FitAddon } from 'xterm-addon-fit' | ||||
| import { computed, onMounted, onUnmounted, ref, watch } from 'vue' | ||||
| import { computed, defineExpose, onMounted, onUnmounted, ref, watch } from 'vue' | ||||
| import 'xterm/css/xterm.css' | ||||
| import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js' | ||||
| import { get, isEmpty, set, size } from 'lodash' | ||||
| @@ -54,7 +54,6 @@ const newTerm = () => { | ||||
|     return { term, fitAddon } | ||||
| } | ||||
|  | ||||
| let intervalID | ||||
| onMounted(() => { | ||||
|     const { term, fitAddon } = newTerm() | ||||
|     termInst = term | ||||
| @@ -69,16 +68,9 @@ onMounted(() => { | ||||
|     EventsOn(`cmd:output:${props.name}`, receiveTermOutput) | ||||
|     fitAddon.fit() | ||||
|     term.focus() | ||||
|  | ||||
|     intervalID = setInterval(() => { | ||||
|         if (props.activated) { | ||||
|             resizeTerm() | ||||
|         } | ||||
|     }, 1000) | ||||
| }) | ||||
|  | ||||
| onUnmounted(() => { | ||||
|     clearInterval(intervalID) | ||||
|     // window.removeEventListener('resize', resizeTerm) | ||||
|     EventsOff(`cmd:output:${props.name}`) | ||||
|     termInst.dispose() | ||||
| @@ -92,6 +84,10 @@ const resizeTerm = () => { | ||||
|     } | ||||
| } | ||||
|  | ||||
| defineExpose({ | ||||
|     resizeTerm, | ||||
| }) | ||||
|  | ||||
| watch( | ||||
|     () => prefStore.general.fontSize, | ||||
|     (fontSize) => { | ||||
| @@ -125,7 +121,6 @@ const onTermData = (data) => { | ||||
|  | ||||
|             case 13: // enter | ||||
|                 // try to process local command first | ||||
|                 console.log('enter con', getCurrentInput()) | ||||
|                 switch (getCurrentInput()) { | ||||
|                     case 'clear': | ||||
|                     case 'clr': | ||||
|   | ||||
| @@ -1,36 +1,74 @@ | ||||
| <script setup> | ||||
| import { get, isEmpty, map, mapValues, pickBy, split, sum, toArray, toNumber } from 'lodash' | ||||
| import { computed, ref } from 'vue' | ||||
| import { computed, onMounted, onUnmounted, ref } from 'vue' | ||||
| import IconButton from '@/components/common/IconButton.vue' | ||||
| import Filter from '@/components/icons/Filter.vue' | ||||
| import Refresh from '@/components/icons/Refresh.vue' | ||||
| import useConnectionStore from 'stores/connections.js' | ||||
|  | ||||
| const props = defineProps({ | ||||
|     server: String, | ||||
|     info: Object, | ||||
|     autoRefresh: false, | ||||
|     loading: false, | ||||
|     autoLoading: false, | ||||
| }) | ||||
|  | ||||
| const emit = defineEmits(['update:autoRefresh', 'refresh']) | ||||
|  | ||||
| const connectionStore = useConnectionStore() | ||||
| const serverInfo = ref({}) | ||||
| const autoRefresh = ref(false) | ||||
| const loading = ref(false) // loading status for refresh | ||||
| const autoLoading = ref(false) // loading status for auto refresh | ||||
|  | ||||
| /** | ||||
|  * refresh server status info | ||||
|  * @param {boolean} [force] force refresh will show loading indicator | ||||
|  * @returns {Promise<void>} | ||||
|  */ | ||||
| const refreshInfo = async (force) => { | ||||
|     if (force) { | ||||
|         loading.value = true | ||||
|     } else { | ||||
|         autoLoading.value = true | ||||
|     } | ||||
|     if (!isEmpty(props.server) && connectionStore.isConnected(props.server)) { | ||||
|         try { | ||||
|             serverInfo.value = await connectionStore.getServerInfo(props.server) | ||||
|         } finally { | ||||
|             loading.value = false | ||||
|             autoLoading.value = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| let intervalID | ||||
| onMounted(() => { | ||||
|     refreshInfo() | ||||
|     intervalID = setInterval(() => { | ||||
|         if (autoRefresh.value === true) { | ||||
|             refreshInfo() | ||||
|         } | ||||
|     }, 5000) | ||||
| }) | ||||
|  | ||||
| onUnmounted(() => { | ||||
|     clearInterval(intervalID) | ||||
| }) | ||||
|  | ||||
| const scrollRef = ref(null) | ||||
| const redisVersion = computed(() => { | ||||
|     return get(props.info, 'Server.redis_version', '') | ||||
|     return get(serverInfo.value, 'Server.redis_version', '') | ||||
| }) | ||||
|  | ||||
| const redisMode = computed(() => { | ||||
|     return get(props.info, 'Server.redis_mode', '') | ||||
|     return get(serverInfo.value, 'Server.redis_mode', '') | ||||
| }) | ||||
|  | ||||
| const role = computed(() => { | ||||
|     return get(props.info, 'Replication.role', '') | ||||
|     return get(serverInfo.value, 'Replication.role', '') | ||||
| }) | ||||
|  | ||||
| const timeUnit = ['common.unit_minute', 'common.unit_hour', 'common.unit_day'] | ||||
| const uptime = computed(() => { | ||||
|     let seconds = get(props.info, 'Server.uptime_in_seconds', 0) | ||||
|     let seconds = get(serverInfo.value, 'Server.uptime_in_seconds', 0) | ||||
|     seconds /= 60 | ||||
|     if (seconds < 60) { | ||||
|         // minutes | ||||
| @@ -46,7 +84,7 @@ const uptime = computed(() => { | ||||
|  | ||||
| const units = ['B', 'KB', 'MB', 'GB', 'TB'] | ||||
| const usedMemory = computed(() => { | ||||
|     let size = get(props.info, 'Memory.used_memory', 0) | ||||
|     let size = get(serverInfo.value, 'Memory.used_memory', 0) | ||||
|     let unitIndex = 0 | ||||
|  | ||||
|     while (size >= 1024 && unitIndex < units.length - 1) { | ||||
| @@ -59,7 +97,7 @@ const usedMemory = computed(() => { | ||||
|  | ||||
| const totalKeys = computed(() => { | ||||
|     const regex = /^db\d+$/ | ||||
|     const result = pickBy(props.info['Keyspace'], (value, key) => { | ||||
|     const result = pickBy(serverInfo.value['Keyspace'], (value, key) => { | ||||
|         return regex.test(key) | ||||
|     }) | ||||
|     const nums = mapValues(result, (v) => { | ||||
| @@ -75,7 +113,7 @@ const infoFilter = ref('') | ||||
| <template> | ||||
|     <n-scrollbar ref="scrollRef"> | ||||
|         <n-back-top :listen-to="scrollRef" /> | ||||
|         <n-space vertical :wrap-item="false" :size="5" style="padding: 5px"> | ||||
|         <n-space :size="5" :wrap-item="false" style="padding: 5px" vertical> | ||||
|             <n-card> | ||||
|                 <template #header> | ||||
|                     <n-space :wrap-item="false" align="center" inline size="small"> | ||||
| @@ -103,19 +141,16 @@ const infoFilter = ref('') | ||||
|                 <template #header-extra> | ||||
|                     <n-space align="center" inline> | ||||
|                         {{ $t('status.auto_refresh') }} | ||||
|                         <n-switch | ||||
|                             :loading="props.autoLoading" | ||||
|                             :value="props.autoRefresh" | ||||
|                             @update:value="(v) => emit('update:autoRefresh', v)" /> | ||||
|                         <n-switch v-model:value="autoRefresh" :loading="autoLoading" /> | ||||
|                         <n-tooltip> | ||||
|                             {{ $t('status.refresh') }} | ||||
|                             <template #trigger> | ||||
|                                 <n-button | ||||
|                                     :loading="props.autoLoading" | ||||
|                                     :loading="autoLoading" | ||||
|                                     circle | ||||
|                                     size="small" | ||||
|                                     tertiary | ||||
|                                     @click="emit('refresh')"> | ||||
|                                     @click="refreshInfo(true)"> | ||||
|                                     <template #icon> | ||||
|                                         <n-icon :component="Refresh" /> | ||||
|                                     </template> | ||||
| @@ -124,7 +159,7 @@ const infoFilter = ref('') | ||||
|                         </n-tooltip> | ||||
|                     </n-space> | ||||
|                 </template> | ||||
|                 <n-spin :show="props.loading"> | ||||
|                 <n-spin :show="loading"> | ||||
|                     <n-grid style="min-width: 500px" x-gap="5"> | ||||
|                         <n-gi :span="6"> | ||||
|                             <n-statistic :label="$t('status.uptime')" :value="uptime[0]"> | ||||
| @@ -134,7 +169,7 @@ const infoFilter = ref('') | ||||
|                         <n-gi :span="6"> | ||||
|                             <n-statistic | ||||
|                                 :label="$t('status.connected_clients')" | ||||
|                                 :value="get(props.info, 'Clients.connected_clients', 0)" /> | ||||
|                                 :value="get(serverInfo.value, 'Clients.connected_clients', 0)" /> | ||||
|                         </n-gi> | ||||
|                         <n-gi :span="6"> | ||||
|                             <n-statistic :value="totalKeys"> | ||||
| @@ -159,9 +194,9 @@ const infoFilter = ref('') | ||||
|                         </template> | ||||
|                     </n-input> | ||||
|                 </template> | ||||
|                 <n-spin :show="props.loading"> | ||||
|                 <n-spin :show="loading"> | ||||
|                     <n-tabs default-value="CPU" placement="left" type="line"> | ||||
|                         <n-tab-pane v-for="(v, k) in props.info" :key="k" :disabled="isEmpty(v)" :name="k"> | ||||
|                         <n-tab-pane v-for="(v, k) in serverInfo" :key="k" :disabled="isEmpty(v)" :name="k"> | ||||
|                             <n-data-table | ||||
|                                 :columns="[ | ||||
|                                     { | ||||
|   | ||||
| @@ -8,8 +8,10 @@ import ContentValueSet from '@/components/content_value/ContentValueSet.vue' | ||||
| import ContentValueZset from '@/components/content_value/ContentValueZSet.vue' | ||||
| import ContentValueStream from '@/components/content_value/ContentValueStream.vue' | ||||
| import { useThemeVars } from 'naive-ui' | ||||
| import useConnectionStore from 'stores/connections.js' | ||||
|  | ||||
| const themeVars = useThemeVars() | ||||
| const connectionStore = useConnectionStore() | ||||
|  | ||||
| const props = defineProps({ | ||||
|     blank: Boolean, | ||||
| @@ -33,8 +35,6 @@ const props = defineProps({ | ||||
|     }, | ||||
| }) | ||||
|  | ||||
| const emit = defineEmits(['reload']) | ||||
|  | ||||
| const valueComponents = { | ||||
|     [redisTypes.STRING]: ContentValueString, | ||||
|     [redisTypes.HASH]: ContentValueHash, | ||||
| @@ -43,18 +43,25 @@ const valueComponents = { | ||||
|     [redisTypes.ZSET]: ContentValueZset, | ||||
|     [redisTypes.STREAM]: ContentValueStream, | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * reload current selection key | ||||
|  * @returns {Promise<null>} | ||||
|  */ | ||||
| const onReloadKey = async () => { | ||||
|     await connectionStore.loadKeyValue(props.name, props.db, props.key, props.viewAs) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|     <n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content"> | ||||
|         <template #extra> | ||||
|             <n-button :focusable="false" @click="emit('reload')">{{ $t('interface.reload') }}</n-button> | ||||
|             <n-button :focusable="false" @click="onReloadKey">{{ $t('interface.reload') }}</n-button> | ||||
|         </template> | ||||
|     </n-empty> | ||||
|     <keep-alive v-else> | ||||
|         <component | ||||
|         class="content-value-wrapper" | ||||
|             :is="valueComponents[props.type]" | ||||
|         v-else | ||||
|             :db="props.db" | ||||
|             :key-code="props.keyCode" | ||||
|             :key-path="props.keyPath" | ||||
| @@ -62,10 +69,12 @@ const valueComponents = { | ||||
|             :size="props.size" | ||||
|             :ttl="props.ttl" | ||||
|             :value="props.value" | ||||
|         :view-as="props.viewAs" /> | ||||
|             :view-as="props.viewAs" | ||||
|             class="content-value-wrapper" /> | ||||
|     </keep-alive> | ||||
| </template> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| .content-value-wrapper { | ||||
|     background-color: v-bind('themeVars.bodyColor'); | ||||
| } | ||||
|   | ||||
| @@ -13,9 +13,6 @@ import { types } from '@/consts/support_redis_type.js' | ||||
| import Search from '@/components/icons/Search.vue' | ||||
| import Unlink from '@/components/icons/Unlink.vue' | ||||
| import Status from '@/components/icons/Status.vue' | ||||
| import SwitchButton from '@/components/common/SwitchButton.vue' | ||||
| import ListView from '@/components/icons/ListView.vue' | ||||
| import TreeView from '@/components/icons/TreeView.vue' | ||||
|  | ||||
| const themeVars = useThemeVars() | ||||
| const dialogStore = useDialogStore() | ||||
|   | ||||
| @@ -234,7 +234,7 @@ const useTabStore = defineStore('tab', { | ||||
|         /** | ||||
|          * set selected keys of current display browser tree | ||||
|          * @param {string} server | ||||
|          * @param {string|string[]} keys | ||||
|          * @param {string|string[]} [keys] | ||||
|          */ | ||||
|         setSelectedKeys(server, keys = null) { | ||||
|             let tab = find(this.tabList, { name: server }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 tiny-craft
					tiny-craft