perf: create "content-pane" component for each tab to maintain data and status easily

This commit is contained in:
tiny-craft 2023-10-27 10:50:22 +08:00
parent 54864fe7d5
commit 297286f150
9 changed files with 132 additions and 207 deletions

View File

@ -208,7 +208,12 @@ onMounted(async () => {
@mouseout="data.hoverResize = false" @mouseout="data.hoverResize = false"
@mouseover="data.hoverResize = true" /> @mouseover="data.hoverResize = true" />
</div> </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> </div>
<!-- server list page --> <!-- server list page -->

View File

@ -73,10 +73,10 @@ defineExpose({
<template> <template>
<n-card <n-card
:bordered="false" :bordered="false"
:theme-overrides="{ borderRadius: '0px' }"
:title="$t('log.launch_log')" :title="$t('log.launch_log')"
class="content-container flex-box-v" class="content-container flex-box-v"
content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray" content-style="display: flex;flex-direction: column; overflow: hidden; backgroundColor: gray">
:theme-overrides="{ borderRadius: '0px' }">
<n-form :disabled="data.loading" class="flex-item" inline> <n-form :disabled="data.loading" class="flex-item" inline>
<n-form-item :label="$t('log.filter_server')"> <n-form-item :label="$t('log.filter_server')">
<n-select <n-select

View File

@ -1,8 +1,7 @@
<script setup> <script setup>
import { computed, onMounted, onUnmounted, reactive, watch } from 'vue' import { computed, nextTick, ref, watch } from 'vue'
import { get, isEmpty, keyBy, map, size, toUpper } from 'lodash' import { find, map, toUpper } from 'lodash'
import useTabStore from 'stores/tab.js' import useTabStore from 'stores/tab.js'
import useConnectionStore from 'stores/connections.js'
import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue' import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue'
import Status from '@/components/icons/Status.vue' import Status from '@/components/icons/Status.vue'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
@ -24,98 +23,10 @@ const themeVars = useThemeVars()
* @property {boolean} autoLoading loading status for auto refresh * @property {boolean} autoLoading loading status for auto refresh
*/ */
/** const props = defineProps({
* server: String,
* @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)
}) })
/**
* 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 tabStore = useTabStore()
const tab = computed(() => const tab = computed(() =>
map(tabStore.tabs, (item) => ({ 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 tabContent = computed(() => {
const tab = tabStore.currentTab const tab = find(tabStore.tabs, { name: props.server })
if (tab == null) { if (tab == null) {
return null return {}
} }
return { return {
name: tab.name, name: tab.name,
@ -168,26 +54,10 @@ const tabContent = computed(() => {
} }
}) })
const showServerStatus = computed(() => {
return tabContent.value == null || isEmpty(tabContent.value.keyPath)
})
const isBlankValue = computed(() => { const isBlankValue = computed(() => {
return tabContent.value.value == null 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 selectedSubTab = computed(() => {
const { subTab = 'status' } = tabStore.currentTab || {} const { subTab = 'status' } = tabStore.currentTab || {}
return subTab return subTab
@ -196,11 +66,28 @@ const selectedSubTab = computed(() => {
const onSwitchSubTab = (name) => { const onSwitchSubTab = (name) => {
tabStore.switchSubTab(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> </script>
<template> <template>
<div class="content-container flex-box-v"> <div class="content-container flex-box-v">
<n-tabs <n-tabs
ref="tabsRef"
:tabs-padding="5" :tabs-padding="5"
:theme-overrides="{ :theme-overrides="{
tabFontWeightActive: 'normal', tabFontWeightActive: 'normal',
@ -217,7 +104,7 @@ const onSwitchSubTab = (name) => {
type="line" type="line"
@update:value="onSwitchSubTab"> @update:value="onSwitchSubTab">
<!-- server status pane --> <!-- server status pane -->
<n-tab-pane :name="BrowserTabType.Status.toString()"> <n-tab-pane :name="BrowserTabType.Status.toString()" display-directive="show:lazy">
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
@ -226,17 +113,11 @@ const onSwitchSubTab = (name) => {
<span>{{ $t('interface.sub_tab.status') }}</span> <span>{{ $t('interface.sub_tab.status') }}</span>
</n-space> </n-space>
</template> </template>
<content-server-status <content-server-status :server="props.server" />
v-model:auto-refresh="currentServer.autoRefresh"
:auto-loading="currentServer.autoLoading"
:info="currentServer.info"
:loading="currentServer.loading"
:server="currentServer.name"
@refresh="refreshInfo(currentServer.name, true)" />
</n-tab-pane> </n-tab-pane>
<!-- key detail pane --> <!-- key detail pane -->
<n-tab-pane :name="BrowserTabType.KeyDetail.toString()"> <n-tab-pane :name="BrowserTabType.KeyDetail.toString()" display-directive="show:lazy">
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
@ -249,20 +130,19 @@ const onSwitchSubTab = (name) => {
</template> </template>
<content-value-wrapper <content-value-wrapper
:blank="isBlankValue" :blank="isBlankValue"
:type="tabContent.type"
:db="tabContent.db" :db="tabContent.db"
:key-code="tabContent.keyCode" :key-code="tabContent.keyCode"
:key-path="tabContent.keyPath" :key-path="tabContent.keyPath"
:name="tabContent.name" :name="tabContent.name"
:size="tabContent.size" :size="tabContent.size"
:ttl="tabContent.ttl" :ttl="tabContent.ttl"
:type="tabContent.type"
:value="tabContent.value" :value="tabContent.value"
:view-as="tabContent.viewAs" :view-as="tabContent.viewAs" />
@reload="onReloadKey" />
</n-tab-pane> </n-tab-pane>
<!-- cli pane --> <!-- cli pane -->
<n-tab-pane :name="BrowserTabType.Cli.toString()"> <n-tab-pane :name="BrowserTabType.Cli.toString()" display-directive="show:lazy">
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
@ -271,7 +151,7 @@ const onSwitchSubTab = (name) => {
<span>{{ $t('interface.sub_tab.cli') }}</span> <span>{{ $t('interface.sub_tab.cli') }}</span>
</n-space> </n-space>
</template> </template>
<content-cli :name="currentServer.name" /> <content-cli ref="cliRef" :name="props.server" />
</n-tab-pane> </n-tab-pane>
<!-- slow log pane --> <!-- slow log pane -->
@ -312,4 +192,8 @@ const onSwitchSubTab = (name) => {
background-color: v-bind('themeVars.tabColor'); background-color: v-bind('themeVars.tabColor');
overflow: hidden; overflow: hidden;
} }
.n-tabs .n-tabs-bar {
transition: none !important;
}
</style> </style>

View File

@ -52,13 +52,13 @@ const tab = computed(() =>
<n-tabs <n-tabs
v-model:value="tabStore.activatedIndex" v-model:value="tabStore.activatedIndex"
:closable="true" :closable="true"
:tabs-padding="0"
:tab-style="{ :tab-style="{
borderStyle: 'solid', borderStyle: 'solid',
borderWidth: '1px', borderWidth: '1px',
borderLeftColor: themeVars.borderColor, borderLeftColor: themeVars.borderColor,
borderRightColor: themeVars.borderColor, borderRightColor: themeVars.borderColor,
}" }"
:tabs-padding="0"
:theme-overrides="{ :theme-overrides="{
tabFontWeightActive: 800, tabFontWeightActive: 800,
tabGapSmallCard: 0, tabGapSmallCard: 0,
@ -76,10 +76,10 @@ const tab = computed(() =>
<n-tab <n-tab
v-for="(t, i) in tab" v-for="(t, i) in tab"
:key="i" :key="i"
:class="tabClass(i)"
:closable="true" :closable="true"
:name="i" :name="i"
:style="tabStore.activatedIndex === i ? activeTabStyle : undefined" :style="tabStore.activatedIndex === i ? activeTabStyle : undefined"
:class="tabClass(i)"
@dblclick.stop="() => {}"> @dblclick.stop="() => {}">
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="18"> <n-icon size="18">

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { Terminal } from 'xterm' import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit' 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 'xterm/css/xterm.css'
import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js' import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
import { get, isEmpty, set, size } from 'lodash' import { get, isEmpty, set, size } from 'lodash'
@ -54,7 +54,6 @@ const newTerm = () => {
return { term, fitAddon } return { term, fitAddon }
} }
let intervalID
onMounted(() => { onMounted(() => {
const { term, fitAddon } = newTerm() const { term, fitAddon } = newTerm()
termInst = term termInst = term
@ -69,16 +68,9 @@ onMounted(() => {
EventsOn(`cmd:output:${props.name}`, receiveTermOutput) EventsOn(`cmd:output:${props.name}`, receiveTermOutput)
fitAddon.fit() fitAddon.fit()
term.focus() term.focus()
intervalID = setInterval(() => {
if (props.activated) {
resizeTerm()
}
}, 1000)
}) })
onUnmounted(() => { onUnmounted(() => {
clearInterval(intervalID)
// window.removeEventListener('resize', resizeTerm) // window.removeEventListener('resize', resizeTerm)
EventsOff(`cmd:output:${props.name}`) EventsOff(`cmd:output:${props.name}`)
termInst.dispose() termInst.dispose()
@ -92,6 +84,10 @@ const resizeTerm = () => {
} }
} }
defineExpose({
resizeTerm,
})
watch( watch(
() => prefStore.general.fontSize, () => prefStore.general.fontSize,
(fontSize) => { (fontSize) => {
@ -125,7 +121,6 @@ const onTermData = (data) => {
case 13: // enter case 13: // enter
// try to process local command first // try to process local command first
console.log('enter con', getCurrentInput())
switch (getCurrentInput()) { switch (getCurrentInput()) {
case 'clear': case 'clear':
case 'clr': case 'clr':

View File

@ -1,36 +1,74 @@
<script setup> <script setup>
import { get, isEmpty, map, mapValues, pickBy, split, sum, toArray, toNumber } from 'lodash' 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 IconButton from '@/components/common/IconButton.vue'
import Filter from '@/components/icons/Filter.vue' import Filter from '@/components/icons/Filter.vue'
import Refresh from '@/components/icons/Refresh.vue' import Refresh from '@/components/icons/Refresh.vue'
import useConnectionStore from 'stores/connections.js'
const props = defineProps({ const props = defineProps({
server: String, server: String,
info: Object,
autoRefresh: false,
loading: false,
autoLoading: false,
}) })
const emit = defineEmits(['update:autoRefresh', 'refresh']) 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 scrollRef = ref(null)
const redisVersion = computed(() => { const redisVersion = computed(() => {
return get(props.info, 'Server.redis_version', '') return get(serverInfo.value, 'Server.redis_version', '')
}) })
const redisMode = computed(() => { const redisMode = computed(() => {
return get(props.info, 'Server.redis_mode', '') return get(serverInfo.value, 'Server.redis_mode', '')
}) })
const role = computed(() => { 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 timeUnit = ['common.unit_minute', 'common.unit_hour', 'common.unit_day']
const uptime = computed(() => { 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 seconds /= 60
if (seconds < 60) { if (seconds < 60) {
// minutes // minutes
@ -46,7 +84,7 @@ const uptime = computed(() => {
const units = ['B', 'KB', 'MB', 'GB', 'TB'] const units = ['B', 'KB', 'MB', 'GB', 'TB']
const usedMemory = computed(() => { const usedMemory = computed(() => {
let size = get(props.info, 'Memory.used_memory', 0) let size = get(serverInfo.value, 'Memory.used_memory', 0)
let unitIndex = 0 let unitIndex = 0
while (size >= 1024 && unitIndex < units.length - 1) { while (size >= 1024 && unitIndex < units.length - 1) {
@ -59,7 +97,7 @@ const usedMemory = computed(() => {
const totalKeys = computed(() => { const totalKeys = computed(() => {
const regex = /^db\d+$/ const regex = /^db\d+$/
const result = pickBy(props.info['Keyspace'], (value, key) => { const result = pickBy(serverInfo.value['Keyspace'], (value, key) => {
return regex.test(key) return regex.test(key)
}) })
const nums = mapValues(result, (v) => { const nums = mapValues(result, (v) => {
@ -75,7 +113,7 @@ const infoFilter = ref('')
<template> <template>
<n-scrollbar ref="scrollRef"> <n-scrollbar ref="scrollRef">
<n-back-top :listen-to="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> <n-card>
<template #header> <template #header>
<n-space :wrap-item="false" align="center" inline size="small"> <n-space :wrap-item="false" align="center" inline size="small">
@ -103,19 +141,16 @@ const infoFilter = ref('')
<template #header-extra> <template #header-extra>
<n-space align="center" inline> <n-space align="center" inline>
{{ $t('status.auto_refresh') }} {{ $t('status.auto_refresh') }}
<n-switch <n-switch v-model:value="autoRefresh" :loading="autoLoading" />
:loading="props.autoLoading"
:value="props.autoRefresh"
@update:value="(v) => emit('update:autoRefresh', v)" />
<n-tooltip> <n-tooltip>
{{ $t('status.refresh') }} {{ $t('status.refresh') }}
<template #trigger> <template #trigger>
<n-button <n-button
:loading="props.autoLoading" :loading="autoLoading"
circle circle
size="small" size="small"
tertiary tertiary
@click="emit('refresh')"> @click="refreshInfo(true)">
<template #icon> <template #icon>
<n-icon :component="Refresh" /> <n-icon :component="Refresh" />
</template> </template>
@ -124,7 +159,7 @@ const infoFilter = ref('')
</n-tooltip> </n-tooltip>
</n-space> </n-space>
</template> </template>
<n-spin :show="props.loading"> <n-spin :show="loading">
<n-grid style="min-width: 500px" x-gap="5"> <n-grid style="min-width: 500px" x-gap="5">
<n-gi :span="6"> <n-gi :span="6">
<n-statistic :label="$t('status.uptime')" :value="uptime[0]"> <n-statistic :label="$t('status.uptime')" :value="uptime[0]">
@ -134,7 +169,7 @@ const infoFilter = ref('')
<n-gi :span="6"> <n-gi :span="6">
<n-statistic <n-statistic
:label="$t('status.connected_clients')" :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>
<n-gi :span="6"> <n-gi :span="6">
<n-statistic :value="totalKeys"> <n-statistic :value="totalKeys">
@ -159,9 +194,9 @@ const infoFilter = ref('')
</template> </template>
</n-input> </n-input>
</template> </template>
<n-spin :show="props.loading"> <n-spin :show="loading">
<n-tabs default-value="CPU" placement="left" type="line"> <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 <n-data-table
:columns="[ :columns="[
{ {

View File

@ -8,8 +8,10 @@ import ContentValueSet from '@/components/content_value/ContentValueSet.vue'
import ContentValueZset from '@/components/content_value/ContentValueZSet.vue' import ContentValueZset from '@/components/content_value/ContentValueZSet.vue'
import ContentValueStream from '@/components/content_value/ContentValueStream.vue' import ContentValueStream from '@/components/content_value/ContentValueStream.vue'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
import useConnectionStore from 'stores/connections.js'
const themeVars = useThemeVars() const themeVars = useThemeVars()
const connectionStore = useConnectionStore()
const props = defineProps({ const props = defineProps({
blank: Boolean, blank: Boolean,
@ -33,8 +35,6 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['reload'])
const valueComponents = { const valueComponents = {
[redisTypes.STRING]: ContentValueString, [redisTypes.STRING]: ContentValueString,
[redisTypes.HASH]: ContentValueHash, [redisTypes.HASH]: ContentValueHash,
@ -43,18 +43,25 @@ const valueComponents = {
[redisTypes.ZSET]: ContentValueZset, [redisTypes.ZSET]: ContentValueZset,
[redisTypes.STREAM]: ContentValueStream, [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> </script>
<template> <template>
<n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content"> <n-empty v-if="props.blank" :description="$t('interface.nonexist_tab_content')" class="empty-content">
<template #extra> <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> </template>
</n-empty> </n-empty>
<keep-alive v-else>
<component <component
class="content-value-wrapper"
:is="valueComponents[props.type]" :is="valueComponents[props.type]"
v-else
:db="props.db" :db="props.db"
:key-code="props.keyCode" :key-code="props.keyCode"
:key-path="props.keyPath" :key-path="props.keyPath"
@ -62,10 +69,12 @@ const valueComponents = {
:size="props.size" :size="props.size"
:ttl="props.ttl" :ttl="props.ttl"
:value="props.value" :value="props.value"
:view-as="props.viewAs" /> :view-as="props.viewAs"
class="content-value-wrapper" />
</keep-alive>
</template> </template>
<style scoped lang="scss"> <style lang="scss" scoped>
.content-value-wrapper { .content-value-wrapper {
background-color: v-bind('themeVars.bodyColor'); background-color: v-bind('themeVars.bodyColor');
} }

View File

@ -13,9 +13,6 @@ import { types } from '@/consts/support_redis_type.js'
import Search from '@/components/icons/Search.vue' import Search from '@/components/icons/Search.vue'
import Unlink from '@/components/icons/Unlink.vue' import Unlink from '@/components/icons/Unlink.vue'
import Status from '@/components/icons/Status.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 themeVars = useThemeVars()
const dialogStore = useDialogStore() const dialogStore = useDialogStore()

View File

@ -234,7 +234,7 @@ const useTabStore = defineStore('tab', {
/** /**
* set selected keys of current display browser tree * set selected keys of current display browser tree
* @param {string} server * @param {string} server
* @param {string|string[]} keys * @param {string|string[]} [keys]
*/ */
setSelectedKeys(server, keys = null) { setSelectedKeys(server, keys = null) {
let tab = find(this.tabList, { name: server }) let tab = find(this.tabList, { name: server })