feat: adjusted the content pane to accommodate more information (add sub content tabs).
This commit is contained in:
parent
6f5ea748f5
commit
5b4683a735
|
@ -1,16 +1,19 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, onUnmounted, reactive, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, reactive, watch } from 'vue'
|
||||||
import { types } from '@/consts/support_redis_type.js'
|
|
||||||
import ContentValueHash from '@/components/content_value/ContentValueHash.vue'
|
|
||||||
import ContentValueList from '@/components/content_value/ContentValueList.vue'
|
|
||||||
import ContentValueString from '@/components/content_value/ContentValueString.vue'
|
|
||||||
import ContentValueSet from '@/components/content_value/ContentValueSet.vue'
|
|
||||||
import ContentValueZset from '@/components/content_value/ContentValueZSet.vue'
|
|
||||||
import { get, isEmpty, keyBy, map, size, toUpper } from 'lodash'
|
import { get, isEmpty, keyBy, map, size, toUpper } from 'lodash'
|
||||||
import useTabStore from 'stores/tab.js'
|
import useTabStore from 'stores/tab.js'
|
||||||
import useConnectionStore from 'stores/connections.js'
|
import useConnectionStore from 'stores/connections.js'
|
||||||
import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue'
|
import ContentServerStatus from '@/components/content_value/ContentServerStatus.vue'
|
||||||
import ContentValueStream from '@/components/content_value/ContentValueStream.vue'
|
import Status from '@/components/icons/Status.vue'
|
||||||
|
import { useThemeVars } from 'naive-ui'
|
||||||
|
import { BrowserTabType } from '@/consts/browser_tab_type.js'
|
||||||
|
import Terminal from '@/components/icons/Terminal.vue'
|
||||||
|
import Log from '@/components/icons/Log.vue'
|
||||||
|
import Detail from '@/components/icons/Detail.vue'
|
||||||
|
import ContentValueWrapper from '@/components/content_value/ContentValueWrapper.vue'
|
||||||
|
import ContentCli from '@/components/content_value/ContentCli.vue'
|
||||||
|
|
||||||
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ServerStatusItem
|
* @typedef {Object} ServerStatusItem
|
||||||
|
@ -112,15 +115,6 @@ onUnmounted(() => {
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
})
|
})
|
||||||
|
|
||||||
const valueComponents = {
|
|
||||||
[types.STRING]: ContentValueString,
|
|
||||||
[types.HASH]: ContentValueHash,
|
|
||||||
[types.LIST]: ContentValueList,
|
|
||||||
[types.SET]: ContentValueSet,
|
|
||||||
[types.ZSET]: ContentValueZset,
|
|
||||||
[types.STREAM]: ContentValueStream,
|
|
||||||
}
|
|
||||||
|
|
||||||
const connectionStore = useConnectionStore()
|
const connectionStore = useConnectionStore()
|
||||||
const tabStore = useTabStore()
|
const tabStore = useTabStore()
|
||||||
const tab = computed(() =>
|
const tab = computed(() =>
|
||||||
|
@ -162,6 +156,7 @@ const tabContent = computed(() => {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: tab.name,
|
name: tab.name,
|
||||||
|
subTab: tab.subTab,
|
||||||
type: toUpper(tab.type),
|
type: toUpper(tab.type),
|
||||||
db: tab.db,
|
db: tab.db,
|
||||||
keyPath: tab.key,
|
keyPath: tab.key,
|
||||||
|
@ -177,7 +172,7 @@ const showServerStatus = computed(() => {
|
||||||
return tabContent.value == null || isEmpty(tabContent.value.keyPath)
|
return tabContent.value == null || isEmpty(tabContent.value.keyPath)
|
||||||
})
|
})
|
||||||
|
|
||||||
const showNonexists = computed(() => {
|
const isBlankValue = computed(() => {
|
||||||
return tabContent.value.value == null
|
return tabContent.value.value == null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -192,38 +187,104 @@ const onReloadKey = async () => {
|
||||||
}
|
}
|
||||||
await connectionStore.loadKeyValue(tab.name, tab.db, tab.key, tab.viewAs)
|
await connectionStore.loadKeyValue(tab.name, tab.db, tab.key, tab.viewAs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedSubTab = computed(() => {
|
||||||
|
const { subTab = 'status' } = tabStore.currentTab || {}
|
||||||
|
return subTab
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSwitchSubTab = (name) => {
|
||||||
|
tabStore.switchSubTab(name)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="content-container flex-box-v">
|
<div class="content-container flex-box-v">
|
||||||
<div v-if="showServerStatus" class="content-container flex-item-expand flex-box-v">
|
<n-tabs
|
||||||
<!-- select nothing or select server node, display server status -->
|
:tabs-padding="5"
|
||||||
<content-server-status
|
:theme-overrides="{
|
||||||
v-model:auto-refresh="currentServer.autoRefresh"
|
tabGapSmallLine: '10px',
|
||||||
:auto-loading="currentServer.autoLoading"
|
tabGapMediumLine: '10px',
|
||||||
:info="currentServer.info"
|
tabGapLargeLine: '10px',
|
||||||
:loading="currentServer.loading"
|
}"
|
||||||
:server="currentServer.name"
|
:value="selectedSubTab"
|
||||||
@refresh="refreshInfo(currentServer.name, true)" />
|
class="content-sub-tab"
|
||||||
</div>
|
default-value="status"
|
||||||
<div v-else-if="showNonexists" class="content-container flex-item-expand flex-box-v">
|
pane-class="content-sub-tab-pane"
|
||||||
<n-empty :description="$t('interface.nonexist_tab_content')" class="empty-content">
|
placement="top"
|
||||||
<template #extra>
|
tab-style="padding-left: 10px; padding-right: 10px;"
|
||||||
<n-button :focusable="false" @click="onReloadKey">{{ $t('interface.reload') }}</n-button>
|
type="line"
|
||||||
|
@update:value="onSwitchSubTab">
|
||||||
|
<!-- server status pane -->
|
||||||
|
<n-tab-pane :name="BrowserTabType.Status.toString()">
|
||||||
|
<template #tab>
|
||||||
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
|
<n-icon size="16">
|
||||||
|
<status :inverse="selectedSubTab === BrowserTabType.Status.toString()" stroke-width="4" />
|
||||||
|
</n-icon>
|
||||||
|
<span>{{ $t('interface.sub_tab.status') }}</span>
|
||||||
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-empty>
|
<content-server-status
|
||||||
</div>
|
v-model:auto-refresh="currentServer.autoRefresh"
|
||||||
<component
|
:auto-loading="currentServer.autoLoading"
|
||||||
:is="valueComponents[tabContent.type]"
|
:info="currentServer.info"
|
||||||
v-else
|
:loading="currentServer.loading"
|
||||||
:db="tabContent.db"
|
:server="currentServer.name"
|
||||||
:key-code="tabContent.keyCode"
|
@refresh="refreshInfo(currentServer.name, true)" />
|
||||||
:key-path="tabContent.keyPath"
|
</n-tab-pane>
|
||||||
:name="tabContent.name"
|
|
||||||
:size="tabContent.size"
|
<!-- key detail pane -->
|
||||||
:ttl="tabContent.ttl"
|
<n-tab-pane :name="BrowserTabType.KeyDetail.toString()">
|
||||||
:value="tabContent.value"
|
<template #tab>
|
||||||
:view-as="tabContent.viewAs" />
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
|
<n-icon size="16">
|
||||||
|
<detail
|
||||||
|
:inverse="selectedSubTab === BrowserTabType.KeyDetail.toString()"
|
||||||
|
fill-color="none" />
|
||||||
|
</n-icon>
|
||||||
|
<span>{{ $t('interface.sub_tab.key_detail') }}</span>
|
||||||
|
</n-space>
|
||||||
|
</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"
|
||||||
|
:value="tabContent.value"
|
||||||
|
:view-as="tabContent.viewAs"
|
||||||
|
@reload="onReloadKey" />
|
||||||
|
</n-tab-pane>
|
||||||
|
|
||||||
|
<!-- cli pane -->
|
||||||
|
<n-tab-pane :name="BrowserTabType.Cli.toString()">
|
||||||
|
<template #tab>
|
||||||
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
|
<n-icon size="16">
|
||||||
|
<terminal :inverse="selectedSubTab === BrowserTabType.Cli.toString()" />
|
||||||
|
</n-icon>
|
||||||
|
<span>{{ $t('interface.sub_tab.cli') }}</span>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
<content-cli />
|
||||||
|
</n-tab-pane>
|
||||||
|
|
||||||
|
<!-- slow log pane -->
|
||||||
|
<n-tab-pane :name="BrowserTabType.SlowLog.toString()">
|
||||||
|
<template #tab>
|
||||||
|
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
|
||||||
|
<n-icon size="16">
|
||||||
|
<log :inverse="selectedSubTab === BrowserTabType.SlowLog.toString()" />
|
||||||
|
</n-icon>
|
||||||
|
<span>{{ $t('interface.sub_tab.slow_log') }}</span>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -231,27 +292,24 @@ const onReloadKey = async () => {
|
||||||
@import '@/styles/content';
|
@import '@/styles/content';
|
||||||
|
|
||||||
.content-container {
|
.content-container {
|
||||||
padding: 5px;
|
//padding: 5px 5px 0;
|
||||||
|
//padding-top: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
background-color: v-bind('themeVars.tabColor');
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.content-sub-tab {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background-color: v-bind('themeVars.bodyColor');
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
//.tab-item {
|
.content-sub-tab-pane {
|
||||||
// gap: 5px;
|
padding: 0 !important;
|
||||||
// padding: 0 5px 0 10px;
|
height: 100%;
|
||||||
// align-items: center;
|
background-color: v-bind('themeVars.tabColor');
|
||||||
// max-width: 150px;
|
overflow: hidden;
|
||||||
//
|
}
|
||||||
// transition: all var(--transition-duration-fast) var(--transition-function-ease-in-out-bezier);
|
|
||||||
//
|
|
||||||
// &-label {
|
|
||||||
// font-size: 15px;
|
|
||||||
// text-align: center;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// &-close {
|
|
||||||
// &:hover {
|
|
||||||
// background-color: rgb(176, 177, 182, 0.4);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -83,7 +83,7 @@ const tab = computed(() =>
|
||||||
@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">
|
||||||
<server stroke-width="4" :inverse="tabStore.activatedIndex === i" />
|
<server stroke-width="4" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<n-ellipsis style="max-width: 150px">{{ t.label }}</n-ellipsis>
|
<n-ellipsis style="max-width: 150px">{{ t.label }}</n-ellipsis>
|
||||||
</n-space>
|
</n-space>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-empty description="coming soon" class="empty-content"></n-empty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
|
@ -75,7 +75,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>
|
<n-space vertical :wrap-item="false" :size="5" style="padding: 5px">
|
||||||
<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">
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<script setup>
|
||||||
|
import { types } from '@/consts/value_view_type.js'
|
||||||
|
import { types as redisTypes } from '@/consts/support_redis_type.js'
|
||||||
|
import ContentValueString from '@/components/content_value/ContentValueString.vue'
|
||||||
|
import ContentValueHash from '@/components/content_value/ContentValueHash.vue'
|
||||||
|
import ContentValueList from '@/components/content_value/ContentValueList.vue'
|
||||||
|
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'
|
||||||
|
|
||||||
|
const themeVars = useThemeVars()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
blank: Boolean,
|
||||||
|
type: String,
|
||||||
|
name: String,
|
||||||
|
db: Number,
|
||||||
|
keyPath: String,
|
||||||
|
keyCode: {
|
||||||
|
type: Array,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
ttl: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
|
value: [String, Object],
|
||||||
|
size: Number,
|
||||||
|
viewAs: {
|
||||||
|
type: String,
|
||||||
|
default: types.PLAIN_TEXT,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['reload'])
|
||||||
|
|
||||||
|
const valueComponents = {
|
||||||
|
[redisTypes.STRING]: ContentValueString,
|
||||||
|
[redisTypes.HASH]: ContentValueHash,
|
||||||
|
[redisTypes.LIST]: ContentValueList,
|
||||||
|
[redisTypes.SET]: ContentValueSet,
|
||||||
|
[redisTypes.ZSET]: ContentValueZset,
|
||||||
|
[redisTypes.STREAM]: ContentValueStream,
|
||||||
|
}
|
||||||
|
</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>
|
||||||
|
</template>
|
||||||
|
</n-empty>
|
||||||
|
<component
|
||||||
|
class="content-value-wrapper"
|
||||||
|
:is="valueComponents[props.type]"
|
||||||
|
v-else
|
||||||
|
:db="props.db"
|
||||||
|
:key-code="props.keyCode"
|
||||||
|
:key-path="props.keyPath"
|
||||||
|
:name="props.name"
|
||||||
|
:size="props.size"
|
||||||
|
:ttl="props.ttl"
|
||||||
|
:value="props.value"
|
||||||
|
:view-as="props.viewAs" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.content-value-wrapper {
|
||||||
|
background-color: v-bind('themeVars.bodyColor');
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,109 @@
|
||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
inverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect
|
||||||
|
x="6"
|
||||||
|
y="6"
|
||||||
|
width="36"
|
||||||
|
height="36"
|
||||||
|
rx="3"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<rect
|
||||||
|
x="13"
|
||||||
|
y="13"
|
||||||
|
width="8"
|
||||||
|
height="8"
|
||||||
|
fill="#FFF"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M27 13L35 13"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M27 20L35 20"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M13 28L35 28"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M13 35H35"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect
|
||||||
|
x="6"
|
||||||
|
y="6"
|
||||||
|
width="36"
|
||||||
|
height="36"
|
||||||
|
rx="3"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<rect
|
||||||
|
x="13"
|
||||||
|
y="13"
|
||||||
|
width="8"
|
||||||
|
height="8"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M27 13L35 13"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M27 20L35 20"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M13 28L35 28"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M13 35H35"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
inverse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
strokeWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg v-if="props.inverse" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="8"
|
||||||
|
width="40"
|
||||||
|
height="32"
|
||||||
|
rx="2"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M12 18L19 24L12 30"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M23 32H36"
|
||||||
|
stroke="#FFF"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="8"
|
||||||
|
width="40"
|
||||||
|
height="32"
|
||||||
|
rx="2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M12 18L19 24L12 30"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
<path
|
||||||
|
d="M23 32H36"
|
||||||
|
stroke="currentColor"
|
||||||
|
:stroke-width="props.strokeWidth"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* all types of Browser sub tabs
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
export const BrowserTabType = {
|
||||||
|
Status: 'status',
|
||||||
|
KeyDetail: 'key_detail',
|
||||||
|
Cli: 'cli',
|
||||||
|
SlowLog: 'slow_log',
|
||||||
|
}
|
|
@ -87,13 +87,19 @@
|
||||||
"load_more": "Load More Keys",
|
"load_more": "Load More Keys",
|
||||||
"load_all": "Load All Left Keys",
|
"load_all": "Load All Left Keys",
|
||||||
"more_action": "More Action",
|
"more_action": "More Action",
|
||||||
"nonexist_tab_content": "Selected key does not exist. Please retry",
|
"nonexist_tab_content": "Selected key does not exist or no key is selected. Please retry",
|
||||||
"empty_server_content": "Select and open a connection from the left",
|
"empty_server_content": "Select and open a connection from the left",
|
||||||
"empty_server_list": "No redis server",
|
"empty_server_list": "No redis server",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"score": "Score",
|
"score": "Score",
|
||||||
"total": "Length: {size}"
|
"total": "Length: {size}",
|
||||||
|
"sub_tab": {
|
||||||
|
"status": "Status",
|
||||||
|
"key_detail": "Key Detail",
|
||||||
|
"cli": "Command Line",
|
||||||
|
"slow_log": "Slow Log"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ribbon": {
|
"ribbon": {
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
|
|
|
@ -87,13 +87,19 @@
|
||||||
"load_more": "加载更多键",
|
"load_more": "加载更多键",
|
||||||
"load_all": "加载剩余所有键",
|
"load_all": "加载剩余所有键",
|
||||||
"more_action": "更多操作",
|
"more_action": "更多操作",
|
||||||
"nonexist_tab_content": "所选键不存在,请尝试刷新重试",
|
"nonexist_tab_content": "所选键不存在或未选中任何键,请尝试刷新重试",
|
||||||
"empty_server_content": "可以从左边选择并打开连接",
|
"empty_server_content": "可以从左边选择并打开连接",
|
||||||
"empty_server_list": "还没添加Redis服务器",
|
"empty_server_list": "还没添加Redis服务器",
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
"type": "类型",
|
"type": "类型",
|
||||||
"score": "分值",
|
"score": "分值",
|
||||||
"total": "总数:{size}"
|
"total": "总数:{size}",
|
||||||
|
"sub_tab": {
|
||||||
|
"status": "状态",
|
||||||
|
"key_detail": "键详情",
|
||||||
|
"cli": "命令行",
|
||||||
|
"slow_log": "慢日志"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ribbon": {
|
"ribbon": {
|
||||||
"server": "服务器",
|
"server": "服务器",
|
||||||
|
|
|
@ -52,7 +52,7 @@ import useTabStore from './tab.js'
|
||||||
import { types } from '@/consts/support_redis_type.js'
|
import { types } from '@/consts/support_redis_type.js'
|
||||||
import { decodeRedisKey, nativeRedisKey } from '@/utils/key_convert.js'
|
import { decodeRedisKey, nativeRedisKey } from '@/utils/key_convert.js'
|
||||||
import { KeyViewType } from '@/consts/key_view_type.js'
|
import { KeyViewType } from '@/consts/key_view_type.js'
|
||||||
import { nextTick } from 'vue'
|
import { BrowserTabType } from '@/consts/browser_tab_type.js'
|
||||||
|
|
||||||
const useConnectionStore = defineStore('connections', {
|
const useConnectionStore = defineStore('connections', {
|
||||||
/**
|
/**
|
||||||
|
@ -668,6 +668,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
const k = decodeRedisKey(key)
|
const k = decodeRedisKey(key)
|
||||||
const binaryKey = k !== key
|
const binaryKey = k !== key
|
||||||
tab.upsertTab({
|
tab.upsertTab({
|
||||||
|
subTab: BrowserTabType.KeyDetail,
|
||||||
server,
|
server,
|
||||||
db,
|
db,
|
||||||
type,
|
type,
|
||||||
|
@ -690,6 +691,7 @@ const useConnectionStore = defineStore('connections', {
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.upsertTab({
|
tab.upsertTab({
|
||||||
|
subTab: BrowserTabType.Status,
|
||||||
server,
|
server,
|
||||||
db,
|
db,
|
||||||
type: 'none',
|
type: 'none',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { find, findIndex, get, size } from 'lodash'
|
import { find, findIndex, get, isEmpty, set, size } from 'lodash'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
const useTabStore = defineStore('tab', {
|
const useTabStore = defineStore('tab', {
|
||||||
|
@ -6,6 +6,7 @@ const useTabStore = defineStore('tab', {
|
||||||
* @typedef {Object} TabItem
|
* @typedef {Object} TabItem
|
||||||
* @property {string} name connection name
|
* @property {string} name connection name
|
||||||
* @property {boolean} blank is blank tab
|
* @property {boolean} blank is blank tab
|
||||||
|
* @property {string} subTab secondary tab value
|
||||||
* @property {string} [title] tab title
|
* @property {string} [title] tab title
|
||||||
* @property {string} [icon] tab icon
|
* @property {string} [icon] tab icon
|
||||||
* @property {string[]} selectedKeys
|
* @property {string[]} selectedKeys
|
||||||
|
@ -64,12 +65,16 @@ const useTabStore = defineStore('tab', {
|
||||||
*
|
*
|
||||||
* @param idx
|
* @param idx
|
||||||
* @param {boolean} [switchNav]
|
* @param {boolean} [switchNav]
|
||||||
|
* @param {string} [subTab]
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_setActivatedIndex(idx, switchNav) {
|
_setActivatedIndex(idx, switchNav, subTab) {
|
||||||
this.activatedIndex = idx
|
this.activatedIndex = idx
|
||||||
if (switchNav === true) {
|
if (switchNav === true) {
|
||||||
this.nav = idx >= 0 ? 'browser' : 'server'
|
this.nav = idx >= 0 ? 'browser' : 'server'
|
||||||
|
if (!isEmpty(subTab)) {
|
||||||
|
set(this.tabList, [idx, 'subTab'], subTab)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
this.nav = 'server'
|
this.nav = 'server'
|
||||||
|
@ -79,6 +84,7 @@ const useTabStore = defineStore('tab', {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update or insert a new tab if not exists with the same name
|
* update or insert a new tab if not exists with the same name
|
||||||
|
* @param {string} subTab
|
||||||
* @param {string} server
|
* @param {string} server
|
||||||
* @param {number} [db]
|
* @param {number} [db]
|
||||||
* @param {number} [type]
|
* @param {number} [type]
|
||||||
|
@ -89,11 +95,13 @@ const useTabStore = defineStore('tab', {
|
||||||
* @param {*} [value]
|
* @param {*} [value]
|
||||||
* @param {string} [viewAs]
|
* @param {string} [viewAs]
|
||||||
*/
|
*/
|
||||||
upsertTab({ server, db, type, ttl, key, keyCode, size, value, viewAs }) {
|
upsertTab({ subTab, server, db, type, ttl, key, keyCode, size, value, viewAs }) {
|
||||||
let tabIndex = findIndex(this.tabList, { name: server })
|
let tabIndex = findIndex(this.tabList, { name: server })
|
||||||
if (tabIndex === -1) {
|
if (tabIndex === -1) {
|
||||||
this.tabList.push({
|
this.tabList.push({
|
||||||
name: server,
|
name: server,
|
||||||
|
title: server,
|
||||||
|
subTab,
|
||||||
server,
|
server,
|
||||||
db,
|
db,
|
||||||
type,
|
type,
|
||||||
|
@ -105,21 +113,23 @@ const useTabStore = defineStore('tab', {
|
||||||
viewAs,
|
viewAs,
|
||||||
})
|
})
|
||||||
tabIndex = this.tabList.length - 1
|
tabIndex = this.tabList.length - 1
|
||||||
|
} else {
|
||||||
|
const tab = this.tabList[tabIndex]
|
||||||
|
tab.blank = false
|
||||||
|
tab.subTab = subTab
|
||||||
|
// tab.title = db !== undefined ? `${server}/db${db}` : `${server}`
|
||||||
|
tab.title = server
|
||||||
|
tab.server = server
|
||||||
|
tab.db = db
|
||||||
|
tab.type = type
|
||||||
|
tab.ttl = ttl
|
||||||
|
tab.key = key
|
||||||
|
tab.keyCode = keyCode
|
||||||
|
tab.size = size
|
||||||
|
tab.value = value
|
||||||
|
tab.viewAs = viewAs
|
||||||
}
|
}
|
||||||
const tab = this.tabList[tabIndex]
|
this._setActivatedIndex(tabIndex, true, subTab)
|
||||||
tab.blank = false
|
|
||||||
// tab.title = db !== undefined ? `${server}/db${db}` : `${server}`
|
|
||||||
tab.title = server
|
|
||||||
tab.server = server
|
|
||||||
tab.db = db
|
|
||||||
tab.type = type
|
|
||||||
tab.ttl = ttl
|
|
||||||
tab.key = key
|
|
||||||
tab.keyCode = keyCode
|
|
||||||
tab.size = size
|
|
||||||
tab.value = value
|
|
||||||
tab.viewAs = viewAs
|
|
||||||
this._setActivatedIndex(tabIndex, true)
|
|
||||||
// this.activatedTab = tab.name
|
// this.activatedTab = tab.name
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -162,6 +172,14 @@ const useTabStore = defineStore('tab', {
|
||||||
// this.activatedIndex = tabIndex
|
// this.activatedIndex = tabIndex
|
||||||
},
|
},
|
||||||
|
|
||||||
|
switchSubTab(name) {
|
||||||
|
const tab = this.currentTab
|
||||||
|
if (tab == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tab.subTab = name
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tabIndex
|
* @param {number} tabIndex
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
.content-container {
|
.content-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
//height: 100%;
|
height: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
@ -95,6 +95,7 @@ body {
|
||||||
.value-wrapper {
|
.value-wrapper {
|
||||||
//border-top: v-bind('themeVars.borderColor') 1px solid;
|
//border-top: v-bind('themeVars.borderColor') 1px solid;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue