perf: create "content-pane" component for each tab to maintain data and status easily
This commit is contained in:
parent
54864fe7d5
commit
297286f150
|
@ -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 })
|
||||
|
|
Loading…
Reference in New Issue